无法隐式转换类型 - 泛型

时间:2017-09-07 09:17:42

标签: c# generics

我有一个通用类Zone<T> where T: Media.Medium。在这个类中,我有一个方法public void AddNode(Node node),其中我有一个语句node.ParentZone = this,它引发了这个编译错误:

  

无法隐式转换类型&#39;区域&lt; T&gt;&#39;至   &#39;&区LT; Media.Medium&gt;&#39;

但我无法理解为什么public abstract class Node public Zone<Media.Medium> ParentZone字段和Zone<T> where T: Media.Medium字段受where T: Media.Medium限制,因此T为Media.Medium在任何情况下。

以下是孤立的代码:( Zone<T>的完整内容以及Node的相关部分)

public class Zone<T> where T: Media.Medium
{
    public readonly Type MediaType = typeof(T);
    public readonly Guid ID = new Guid();

    private readonly List<Node> _nodes = new List<Node>();

    public void AddNode(Node node)
    {
        foreach (var port in node.GetPorts())
        {
            port.Medium = Activator.CreateInstance<T>();
        }
  

//在下面的行中编译错误

        node.ParentZone = this; 
  

//无法隐式转换类型&#39;区域&lt; T&gt;&#39;至   &#39;&区LT; Media.Medium&gt;&#39;

        _nodes.Add(node);
    }



    public List<Node> GetNodes() => new List<Node>(_nodes);

    public void RemoveNode(Node node) => _nodes.Remove(node);


}





public abstract class Node
{       
    public Zone<Media.Medium> ParentZone;

    ...
}

更新#1:

此代码的目标是:我想将Node个对象添加到Zone个对象,Zone个对象具有Node个对象的列表。每当我向Node添加Zone时,我都希望将Zone对象设置为Node对象的父对象。

我愿意为实现这一目标而进行任何重构。如果有更好的那个,就不应该这样。

4 个答案:

答案 0 :(得分:3)

您应该清楚,通用约束不提供泛型类型之间的可分配性。或者表达不同:如果泛型类继承自另一个类并且共享相同的类型参数,则泛型类可分配给另一个类。例如:

class Zone<T> where T : Medium { }

class ChildZone<T>: Zone<T>  where T : Medium { }

class Medium { }

class ChildMedium : Medium { }

什么有效:

Zone<Medium> mediumZone = new ChildZone<Medium>();
Zone<ChildMedium> childMediumZone = new ChildZone<ChildMedium>();

什么行不通:

Zone<Medium> mediumZone = new Zone<ChildMedium>();
ChildZone<Medium> childMediumZone = new ChildZone<ChildMedium>();

为什么呢?因为继承在泛型类中起作用,而不是泛型参数。考虑一下:

class Zone<T> {
   T Value { get; set; }
}

Zone<Medium>的实例可以在其Medium属性中读取类型Value的项目。这意味着它可以同时读写MediumChildMedium。相反,Zone<ChildMedium>只能读写ChildMedium。这里的问题是setter,因为无法将Medium分配给Zone<ChildMedium>.Value,而不是分配给Zone<Medium>.Value。这使得这些类型不兼容。

如果您的Zone类是一个接口,并且您确保它只返回T类型的值,但您无法设置该类型的值,则可以使用covariance:< / p>

interface IZone<out T> {
    T Value { get; }
}

如果您需要读取和写入值,您唯一的选择是使Node也是通用的并传递通用约束:

namespace Core.Nodes
{
    public abstract class Node<T> where T : Media.Medium
    {       
        public Zone<T> ParentZone;

        //...
    }
}

答案 1 :(得分:2)

看起来你的Node类声明无效

public abstract class Node<T> where T: Media.Medium
{       
    public Zone<T> ParentZone;
    ...
}

答案 2 :(得分:1)

我认为这是一个经典的协方差/逆变问题。如果你不想让Node变得通用(也许它根本不是通用的?)那么试着摆弄这个:

https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/index

如果您具体声明要尝试实现的目标,则可以更改示例并使其正常工作。它需要添加接口IZone

public interface IZone<out T> where T : Media.Medium
{
    //important stuff here
}

public class Zone<T> : IZone<T> where T : Media.Medium
{
    ...
}

public abstract class Node
{
    public IZone<Media.Medium> ParentZone;

}

答案 3 :(得分:1)

您已说过类型T继承自Media.Medium,而非Zone<T>继承自Media.Medium。这是一个简单的,有希望的说明性示例:

class Program
{
    class A<T> where T: B
    {
        public void Foo(B b)
        {
            T t = Activator.CreateInstance<T>();
            //this is OK
            b = t;
            //this is not
            b = this;
        }
    }
    class B
    {            
    }
    class C : B
    {
        public void Foo(B b)
        {
            //this is ok
            b = this;
        }
    }
}
相关问题