C # приведение с обобщениями, которые используют интерфейсы - PullRequest
5 голосов
/ 09 мая 2011

У меня есть несколько универсальных интерфейсов и классов, которые реализуют эти интерфейсы, например так:

    interface A<M, N>
        where M : X<N>
        where N : Y
    {
    }
    class B<M, N> : A<M, N>
        where M : X<N>
        where N : Y
    {

    }

    interface X<M> where M : Y
    {

    }
    interface Y
    {

    }
    class X1<M> : X<M> where M : Y
    {

    }
    class Y1 : Y
    {

    }

Я знаю, что это выглядит очень грязно, но мне это нужно для моего приложения. Мой вопрос: почему я не могу этого сделать:

A<X<Y>, Y> variable = new B<X1<Y1>, Y1>();

Ответы [ 2 ]

8 голосов
/ 09 мая 2011

Марк прав;просто чтобы дать вам дополнительную информацию о том, почему это не может работать.Рассмотрим следующее переименование вашего кода:

    interface IZoo<TCage, TAnimal>
        where TCage : ICage<TAnimal>
        where TAnimal : IAnimal
    {
    }

    class Zoo<TCage, TAnimal> : IZoo<TCage, TAnimal>
        where TCage : ICage<TAnimal>
        where TAnimal : IAnimal
    {
    }

    interface ICage<TAnimal> where TAnimal : IAnimal
    {
    }

    interface IAnimal
    {
    }

    class FishTank<TAnimal> : ICage<TAnimal> where TAnimal : IAnimal
    {
    }

    class Fish : IAnimal
    {
    }

А теперь ваш вопрос, почему это не разрешено:

Zoo<FishTank<Fish>, Fish> aquarium = new Zoo<FishTank<Fish>, Fish>();
IZoo<ICage<IAnimal>, IAnimal> zoo = aquarium;

?

Поскольку предположим, что сейчасна IZoo есть метод:

    interface IZoo<TCage, TAnimal>
        where TCage : ICage<TAnimal>
        where TAnimal : IAnimal
    {
        void PutAnimalInCage(TCage cage, TAnimal animal);
    }

И тогда вы говорите:

zoo.PutAnimalInCage(giraffePaddock, giraffe);

И вы просто кладете загон для жирафов в аквариум!Мы не можем поддерживать безопасность типов в мире, где желаемое преобразование является законным, и у IZoo может быть любой выбранный вами метод.

Теперь это опасно, потому что у IZoo есть такой метод.Если у него нет такого метода, тогда вы правы, это может быть совершенно безопасно.В C # 4.0 мы добавили функцию к языку, чтобы вы могли попросить компилятор «проверить, можно ли сделать этот интерфейс безопасным вариантом», пометив параметры типа, которые вы хотите быть ковариантными с «out», и те, которые вы хотитебыть противоречивым с "в".Если вы сделаете это, то компилятор проверит для вас, может ли отклонение, которое вы хотите, сделать безопасным для типов.Если это невозможно, тогда он не разрешит объявление типа.

То, как этот вопрос обычно возникает в StackOverflow, - это люди, спрашивающие, почему это незаконно:

List<Giraffe> giraffes = new List<Giraffe>();
List<Mammal> mammals = giraffes; // illegal

По той же причине.Потому что тогда ничто не помешает вам позже

mammals.Add(new Tiger());

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

8 голосов
/ 09 мая 2011

Дисперсия должна быть явной (и требует C # 4.0);например, это делает его компиляцией как ковариантным (обратите внимание на модификаторы out в интерфейсе):

interface A<out M, out N>
    where M : X<N>
    where N : Y
{
}
interface X<out M> where M : Y
{

}

Обратите внимание, однако, что это также ограничивает ваш интерфейс ... ковариацией!Например, у вас не может быть метода Add(M) - так как он должен быть инвариантным (он же in).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...