协方差问题

时间:2012-11-09 22:17:23

标签: c# generics

我在下面的演员中遇到了一些问题:

    class A 
    { 
    }

    class B : A 
    {
    }

    class C<T> where T : A
    {
        protected T property { get; set; }
    }

    class D : C<B> 
    {
    }

    class MainClass
    {
        public static void Main (string[] args)
        {
            C<A> x = new D();
            // Error CS0029: Cannot implicitly convert type `SampleApp.D' to `SampleApp.C<SampleApp.A>' (CS0029) (SampleApp)
        }
    }

我不明白为什么这会失败,因为DC<A>宽,因为它实现了C<B>B : A。任何解决方法?

3 个答案:

答案 0 :(得分:2)

如果您可以使用C#4.0,则可以编写以下代码。

class A { }
class B : A {}

interface IC<out T> {}
class C<T> :IC<T> where T : A { protected T property { get; set; }  }

class D : C<B> {}

class MainClass {
    public static void Main()
    {
        IC<A> x = new D();
    }
}

答案 1 :(得分:1)

我们为Animal命名您的班级A,为Barker命名B,为Dog命名D

实际上C<Animal>Dog : C<Barker>宽。假设您拥有Me类型的公共属性T并且可以分配:

C<Animal> a = new Dog();
a.Me = Elephant; // where Elephant inherited from Animal

糟糕! Dog使用Barker进行参数化。你见过吠叫大象吗?

您需要声明一些协变接口,以允许将使用更多派生类型参数C<Barker>实例化的类分配给使用较少派生类型参数C<Animal>实例化的对象。您可以使用空接口,如@NickW建议的那样,但您将无法对该接口的实例执行某些操作(它是空的!)。所以,让我们这样做:

interface IC<out T>
    where T : Animal
{
    IEnumerable<T> Parents(); // IEnumerable is covariant
    T Me { get; } // no setter
}

class C<T> : IC<T>
    where T: Animal
{
    // implementation
}

class D : C<Barker>
{
    // implementation
}

以上情况仍然不可能,但现在你可以

IC<Animal> a = new Dog();
foreach(var parent in a.Parents)
     Console.WriteLine(parent);

Console.WriteLine(a.Me);

答案 2 :(得分:-1)

你不能这样做,因为Generics是实际模板而且它们不像你想用它们做的那样。让我告诉你:

当您说"C<A>"时,它表示"parameter" "A"的通用类。 但 当您说"D"时,它恰好意味着"D"

所以D不等于A的参数的泛型类。你可以在两种类型的ToString函数的结果中看到它(通过使用typeof)。

希望有所帮助

干杯