【问题标题】:Generics, Inheritance, and Casting泛型、继承和强制转换
【发布时间】:2010-11-30 22:07:29
【问题描述】:

我的问题与泛型类型中的转换类有关。虽然我意识到将List<string> 之类的对象转换为List<object> 需要支持协方差以防止将object 对象添加到包含字符串的列表中,但我想知道为什么编译器不接受像下面这样的转换以及它是否是使用接口和协变或逆变可解:

public class TypeA<T> {}

public class TypeB<T> {}

public class TypeC : TypeB<int> {}

class Program
{
    public static void MyMethod<OutputType>(TypeA<TypeB<OutputType>> Parameter) {}

    static void Main(string[] args)
    {
        TypeA<TypeC> Test = new TypeA<TypeC>();
        MyMethod<int>(Test);
    }
}

编译这个会报错:

Argument 1: cannot convert from 'ConsoleApplication1.TypeA&lt;ConsoleApplication1.TypeC&gt;' to 'ConsoleApplication1.TypeA&lt;ConsoleApplication1.TypeB&lt;int&gt;&gt;'.

尽管TypeCTypeB&lt;int&gt; 的直接后代

【问题讨论】:

    标签: c# generics inheritance casting


    【解决方案1】:

    正如其他评论者所指出的,尽管TypeC 派生自TypeB&lt;int&gt;,但TypeA&lt;TypeC&gt; 派生自TypeA&lt;TypeB&lt;int&gt;&gt; 是不正确的。但是,您可能可以通过向MyMethod 添加一个额外的类型参数来使您的代码工作:

    public static void MyMethod<DerivedB,OutputType>(TypeA<DerivedB> Parameter)
        where DerivedB : TypeB<OutputType> {}
    

    【讨论】:

      【解决方案2】:

      我对你的问题感到很困惑,因为问题的前言表明你知道答案是什么。您不能将 List&lt;string&gt; 转换为 List&lt;object&gt;,因为这样您就可以将 Giraffe 添加到实际上是字符串列表的对象列表中,这不是类型安全的。

      如果您将“TypeA”替换为“List”,将TypeB&lt;int&gt; 替换为“object”,将“TypeC”替换为“string”,那么您已经将您的情况转变为您已经知道出于充分理由而行不通的情况。并不是编译器神奇地知道一些关于 List 的事情,只是不允许 List 场景——而是编译器不允许任何这样的变化。

      在 C# 4 中,我们在一些接口和委托类型上添加了变体,这些类型已知在变体下是类型安全的。这是一个“选择加入”模型——你必须向编译器证明你是安全的,然后我们才会让你使用方差。

      【讨论】:

        【解决方案3】:

        好吧,既然 C# 4.0 支持接口与泛型的协变,下面的例子或许可以解决这个问题:

        public interface ITypeA<out T> {}
        
        public class TypeA<T> : ITypeA<T> {}
        
        public class TypeB<T> {}
        
        public class TypeC : TypeB<int> {}
        
        class Program
        {
            public static void MyMethod<OutputType>(ITypeA<TypeB<OutputType>> Parameter) {}
        
            static void Main(string[] args)
            {
                ITypeA<TypeC> Test = new TypeA<TypeC>();
                MyMethod<int>(Test);
            }
        }
        

        请注意,类型 T 只能用于 ITypeA 中方法的返回值。

        【讨论】:

        • 原来是我对协方差如何与接口一起工作缺乏了解。正如 Eric 在他的回答中指出的那样,TypeB 和 TypeC 类似于 List 铸造案例。类似的解决方案实际上解决了我的具体问题,非常感谢。
        【解决方案4】:

        又一个泛型协方差问题 - 请参阅我发布的答案 here。 TypeA 不会以任何方式继承 TypeA>

        【讨论】:

          【解决方案5】:

          但是ConsoleApplication1.TypeA&lt;ConsoleApplication1.TypeC&gt; 不继承自ConsoleApplication1.TypeA&lt;ConsoleApplication1.TypeB&lt;int&gt;&gt;

          【讨论】:

            【解决方案6】:

            假设我有一个 List 对象。然后我将它投射到列表中。 List 有一个名为“add”的方法,它接受一个 T 参数。我向其中添加了一个 Apple(因为我有一个列表)。 但是,在某些地方,我的代码仍然需要 List,所以 List 的一些成员实际上是苹果!!!

            因此,即使 X 继承自 Y,也无法将 TypeA 强制转换为 TypeA。

            【讨论】:

            • 你怎么知道苹果是 T 型的?
            • X 和 Y 是什么?我其实什么都不懂。
            • 这应该是为什么继承是一个坏主意的一个例子。您不能将 List 转换为 List,就像您不能将 List(无论 X 是什么)转换为 List 一样,即使 X 扩展了 Y。在这种情况下,T 是一个泛型类型,我使用的是 List.
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多