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

时间:2011-04-10 20:25:46

标签: 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

这笔交易是什么?我以为.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);
        }
    }
}

3 个答案:

答案 0 :(得分:7)

您正在尝试将Dictionary<int, T>派生类转换为IEnumerable<T>。您必须将其转换为IEnumerable<KeyValuePair<int, T>>或从您的词典中传递Values集合。

RenderLookup(Cars.Values);
RenderLookup(Employees.Values);

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

答案 1 :(得分:5)

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

interface IMyList<(in/out) T> : ...

其次,您无法在示例中执行此操作,因为您的参数T同时是co- 逆变。为简化起见,仅由类返回的类型参数可以是协变的(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();
    }
}

然后你的例子应该有效。

答案 2 :(得分:1)

MyList<Car>间接实施IEnumerable<KeyValuePair<int, Car>>(通过Dictionary<int,Car>),因此与IEnumerable<KeyedBase>不兼容。