Почему в этом примере не работает ковариация .NET 4.0? - PullRequest
2 голосов
/ 11 апреля 2011

Я получаю ошибки компилятора в этих строках:

            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 ]

7 голосов
/ 11 апреля 2011

Вы пытаетесь преобразовать класс Dictionary<int, T>, полученный из IEnumerable<T>. Вы должны привести его к IEnumerable<KeyValuePair<int, T>> или передать коллекцию Values из своих словарей.

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

Это не проблема ковариации, а просто совместимость типов.

5 голосов
/ 11 апреля 2011

Прежде всего, поддержка со-/ контравариантности является обязательной.Вам нужно добавить квалификаторы out или in к параметру универсального типа, чтобы он работал (на интерфейсе, а не на классе).Таким образом, вы должны иметь:

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

Во-вторых, вы не можете сделать это в своем примере, потому что ваш параметр T является одновременно-1007 * и контравариантным.Для упрощения параметр типа, который возвращается только классом, может быть ковариантным (он же out), а параметр типа, который принимается только классом, является контравариантным (он же in).Ваш класс принимает и возвращает T.

Наконец, ваша реальная проблема в том, что Dictionary<K,V> не IEnumerable<V>, а IEnumerable<KeyValuePair<K,V>>.Вам нужно переопределить метод GetEnumerator в MyList следующим образом:

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

Ваш пример должен сработать.

1 голос
/ 11 апреля 2011

MyList<Car> косвенно реализует IEnumerable<KeyValuePair<int, Car>> (через Dictionary<int,Car>) и поэтому не совместимо с IEnumerable<KeyedBase>.

...