开发者

Why isn't .NET 4.0 covariance working in this example?

I'm getting compiler errors on these lines:

            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

What's the deal? I thought .NET 4.0 would handle this? What am I missing?

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 K开发者_JS百科ey
        {
            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);
        }
    }
}


You're trying to cast Dictionary<int, T>-derived class into IEnumerable<T>. You have to cast it to IEnumerable<KeyValuePair<int, T>> or pass Values collection from your dictionaries.

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

This is not a problem with covariance but just with type compatibility.


First of all, co-/contravariance support is opt-in. You need to add the qualifiers out or in to a generic type parameter in order for it to work (on an interface, not a class). So you'd have to have:

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

Secondly, you can't do this in your example because your parameter T is both co- and contravariant. To simplify, a type parameter that is only returned by a class can be covariant (aka out), and a type parameter that is only accepted by a class is contravariant (aka in). Your class both accepts and returns T.

Lastly, your real problem is that Dictionary<K,V> isn't IEnumerable<V>, but IEnumerable<KeyValuePair<K,V>>. You'll need to override the GetEnumerator method on MyList like so:

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();
    }
}

Your example should then work.


MyList<Car> indirectly implements IEnumerable<KeyValuePair<int, Car>> (via Dictionary<int,Car>) and so it isn't compatible with IEnumerable<KeyedBase>.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜