【问题标题】:C# casting with generics that use interfaces使用使用接口的泛型进行 C# 强制转换
【发布时间】:2011-05-09 09:53:01
【问题描述】:

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

    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&lt;X&lt;Y&gt;, Y&gt; variable = new B&lt;X1&lt;Y1&gt;, Y1&gt;();

【问题讨论】:

    标签: c# generics interface instantiation


    【解决方案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)。

      【讨论】:

      • 嘿,当我尝试这样做时,它说它的 C#4.0 语言功能并且我收到一个错误,我该怎么办?
      • @Twinhelix - 你使用的是什么版本的 Visual Studio,你的目标平台是什么?它适用于针对 .NET 2.0 的 VS2010。在 C# 4.0 之前...在非常有限的情况下,差异并不存在。
      • 嘿,我正在运行 VS2008 和 .NET 3.5。我真的无法更改我的环境设置,有什么办法可以解决这个问题吗?
      • 我无法升级,因为这是公司的电脑,还有很多特权和其他。希望有一个解决方法:P
      猜你喜欢
      • 2014-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多