Преобразование C # с помощью универсальных программ, использующих интерфейсы

#c# #generics #interface #instantiation

#c# #обобщения #интерфейс #создание экземпляра

Вопрос:

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

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

Ответ №1:

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

     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», а те, которые вы хотите сделать контравариантными, — с помощью «in». Если вы это сделаете, компилятор проверит для вас, можно ли сделать требуемое изменение безопасным для типов. Если это невозможно, то это не позволит объявить тип.

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

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

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

 mammals.Add(new Tiger());
  

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

Ответ №2:

Отклонение должно быть явным (и требует 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 ).

Комментарии:

1. эй, когда я пытаюсь это сделать, он сообщает о своей языковой функции C # 4.0, и я получаю сообщение об ошибке, что мне делать?

2. @Twinhelix — какую версию Visual Studio вы используете и на какую версию платформы вы ориентируетесь? У меня это работает на VS2010, ориентированном на .NET 2.0. До C # 4.0 … различий вроде как не было, за исключением очень ограниченных случаев.

3. Привет, я использую VS2008 и .NET 3.5. Я действительно не могу изменить настройки своей среды, есть ли какой-либо способ обойти эту проблему?

4. Я не могу обновиться, поскольку это компьютер компании, множество привилегий и другие другие. Надеялся, что есть обходной путь: P