Почему List <T>недопустим в ковариантном интерфейсе MyInterface <out T> - PullRequest
9 голосов
/ 17 июня 2011

Последующий вопрос до предыдущего вопроса , это было идентифицировано как проблема с дисперсией. Сделав еще один шаг, если я изменю IFactory следующим образом:

class Program
{
    static void Main(string[] args)
    {
        IFactory<IProduct> factory = new Factory();
    }
}

class Factory : IFactory<Product>
{
}

class Product : IProduct
{
}

interface IFactory<out T> where T : IProduct
{
    List<T> MakeStuff();
}

interface IProduct
{
}

Я получаю:

Недопустимая дисперсия: параметр типа T должен быть инвариантно допустимым в Sandbox.IFactory.MakeStuff (). T является ковариантным.

Почему это не инвариантно верно? Как это можно / нужно решить?

Ответы [ 3 ]

11 голосов
/ 18 июня 2011

Другие ответы верны, но полезно поразмышлять, почему компилятор помечает это как небезопасное.Предположим, мы это допустили;что может пойти не так?

class Sprocket: Product {}
class Gadget : Product {}
class GadgetFactory : IFactory<Gadget> 
{ 
    public List<Gadget> MakeStuff() 
    { 
        return new List<Gadget>() { new Gadget(); } 
    }
}
... later ...
IFactory<Gadget> gf = new GadgetFactory();
IFactory<Product> pf = gf; // Covariant!
List<Product> pl = pf.MakeStuff(); // Actually a list of gadgets
pl.Add(new Sprocket());

и, эй, мы только что добавили звездочку в список, который может содержать только гаджеты.

Есть только одно место, где компилятор может обнаружить проблему,и это в объявлении интерфейса.

Извините за несколько чрезмерно жаргонное сообщение об ошибке.Я не мог придумать ничего лучшего.

7 голосов
/ 17 июня 2011

@ Ответ Крейга правильный.Чтобы решить, измените его на:

IEnumerable<T> MakeStuff()

РЕДАКТИРОВАТЬ: Что касается почему, посмотрите на определение IEnumerable Интерфейс :

public interface IEnumerable<out T> : IEnumerable

Примечаниечто IList Interface не имеет ключевого слова out.Дисперсия поддерживается для параметров универсального типа в интерфейсах и делегатах, а не в классах, поэтому она не применяется к списку .

6 голосов
/ 17 июня 2011

Поскольку вы можете добавлять и удалять объекты из List<T>, T всегда должен быть инвариантом, даже если список является результатом функции. Выполнив var l = MakeStuff(), вы можете поместить материал в список или убрать его, поэтому T должен быть инвариантным.

...