为什么.NET 4.0协方差在这个例子中不起作用?

Cad*_*oux 2 c# .net-4.0 covariance

我在这些行上遇到编译器错误:

            RenderLookup(Cars);
            RenderLookup(Employees);


Error   2   Argument 1: cannot convert from 'Demo.MyList<Demo.Car>' to 'System.Collections.Generic.IEnumerable<Demo.KeyedBase>' Program.cs  85  26  Demo

Error   4   Argument 1: cannot convert from 'Demo.MyList<Demo.Employee>' to 'System.Collections.Generic.IEnumerable<Demo.KeyedBase>'    Program.cs  86  26  Demo
Run Code Online (Sandbox Code Playgroud)

这是怎么回事?我以为.NET 4.0会处理这个?我错过了什么?

using System;
using System.Collections.Generic;

namespace Demo
{
    public interface KeyedBase
    {
        int Key { get; }
        string Description { get; }
    }

    public class MyList<T> : Dictionary<int, T> where T : KeyedBase
    {
        public void Add(T itm)
        {
            Add(itm.Key, itm);
        }
    }

    public class Car : KeyedBase
    {
        private readonly int _ID;
        private readonly string _Description;
        public Car(int ID, string Description)
        {
            _ID = ID;
            _Description = Description;
        }

        public int Key
        {
            get { return _ID; }
        }

        public string Description
        {
            get { return _Description; }
        }
    }

    public class Employee : KeyedBase
    {
        private readonly int _ID;
        private readonly string _FirstName;
        private readonly string _LastName;
        public Employee(int ID, string FirstName, string LastName)
        {
            _ID = ID;
            _FirstName = FirstName;
            _LastName = LastName;
        }

        public int Key
        {
            get { return _ID; }
        }

        public string Description
        {
            get { return _LastName + ", " + _FirstName; }
        }
    }

    class Program
    {
        private static void RenderLookup(IEnumerable<KeyedBase> Lookup)
        {
            Console.WriteLine("Choose:");
            foreach (var itm in Lookup)
            {
                Console.WriteLine("{0} : {1}", itm.Key, itm.Description);
            }
        }

        static void Main(string[] args)
        {
            var Cars = new MyList<Car> { new Car(1, "Subaru"), new Car(2, "Volswagen") };

            var Employees = new MyList<Employee>
                                {
                                    new Employee(1, "Mickey", "Mouse"),
                                    new Employee(2, "Minnie", "Mouse")
                                };

            RenderLookup(Cars);
            RenderLookup(Employees);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

NOt*_*Dev 7

你正试图将Dictionary<int, T>衍生的课程投入IEnumerable<T>.你必须将它投射到IEnumerable<KeyValuePair<int, T>>或通过Values你的词典集合.

RenderLookup(Cars.Values);
RenderLookup(Employees.Values);
Run Code Online (Sandbox Code Playgroud)

这不是协方差的问题,而是与类型兼容性有关.


por*_*ges 5

首先,共同/逆转支持是选择加入.您需要添加限定符outin泛型类型参数才能使其工作(在接口上,而不是类).所以你必须要:

interface IMyList<(in/out) T> : ...
Run Code Online (Sandbox Code Playgroud)

其次,你不能在你的例子中这样做,因为你的参数T既有共变量又有逆变量.为简化起见,仅由类返回的类型参数可以是covariant(aka out),并且仅由类接受的类型参数是逆变(aka in).你的班级都接受并返回T.

最后,你的真正问题Dictionary<K,V>不是IEnumerable<V>,但是IEnumerable<KeyValuePair<K,V>>.您需要在MyList上覆盖GetEnumerator方法,如下所示:

public class MyList<T> : Dictionary<int, T>, IEnumerable<T> where T : KeyedBase
{
    public void Add(T itm)
    {
        Add(itm.Key, itm);
    }

    public virtual IEnumerator<T> GetEnumerator()
    {
        return Values.GetEnumerator();
    }
}
Run Code Online (Sandbox Code Playgroud)

那么你的例子就可以了.