C#使用泛型使用接口进行转换

Twi*_*lix 5 c# generics interface instantiation

我有一些通用的接口和类来实现这些接口,如下所示:

    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
    {

    }
Run Code Online (Sandbox Code Playgroud)

我知道这似乎是一种非常混乱的做事方式,但我对我的应用程序有点需要它.我的问题是我怎么能不这样做:

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

Mar*_*ell 8

方差需要明确(并且需要C#4.0); 例如,这使它编译为协变(注意out接口中的修饰符):

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

}
Run Code Online (Sandbox Code Playgroud)

但请注意,这也限制了您的界面......协方差!例如,你不可能有一个Add(M)方法 - 因为它需要是变量(aka in).


Eri*_*ert 8

马克是对的; 只是为了给你一些关于为什么这不起作用的背景知识.请考虑以下重新命名代码:

    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
    {
    }
Run Code Online (Sandbox Code Playgroud)

现在你的问题是,为什么这不合法:

Zoo<FishTank<Fish>, Fish> aquarium = new Zoo<FishTank<Fish>, Fish>();
IZoo<ICage<IAnimal>, IAnimal> zoo = aquarium;
Run Code Online (Sandbox Code Playgroud)

因为现在假设IZoo上有一个方法:

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

然后你说:

zoo.PutAnimalInCage(giraffePaddock, giraffe);
Run Code Online (Sandbox Code Playgroud)

而你只需将长颈鹿围场放入水族馆!在您想要的转换是合法的世界中我们无法保持类型安全,IZoo可以使用您选择的任何方法.

现在,这只是危险的,因为IZoo有这样的方法.如果它没有这样的方法,那么你是对的,这可能是非常安全的.在C#4.0中,我们为该语言添加了一个功能,以便您可以通过注释要与"out"协变的类型参数以及您想要的类型参数来询问编译器"检查此接口是否可以安全变换".与"在"逆时针.如果您这样做,那么编译器将检查您是否可以使您想要的方差是类型安全的.如果不能,则不允许声明类型.

这个问题通常出现在StackOverflow上的方式是人们问为什么这是非法的:

List<Giraffe> giraffes = new List<Giraffe>();
List<Mammal> mammals = giraffes; // illegal
Run Code Online (Sandbox Code Playgroud)

同样的道理.因为之后没有什么能阻止你

mammals.Add(new Tiger());
Run Code Online (Sandbox Code Playgroud)

而你刚刚将一只老虎添加到长颈鹿名单中.同样的道理,只是一个更简单的案例.