泛型和类继承混淆

时间:2015-03-11 16:36:58

标签: c# generics inheritance covariance

我有以下代码块作为我遇到的问题的简化示例。但我得到一个错误声称我无法将一种类型转换为另一种类型。我用LINQPad来测试它。

void Main()
{
    LivingThing<Appendage> mysteryAnimal = new Cat();
}

public class Appendage { }
public class Paw : Appendage { }

public class LivingThing<TExtremity> where TExtremity : Appendage { }
public class Animal<TExtremity> : LivingThing<TExtremity> where TExtremity : Appendage { }
public class Cat : Animal<Paw> { }

当我知道Cat的定义使用的是CatLivingThing<Appendage>的子类时,为什么我无法将LivingThing投射到Appendage

2 个答案:

答案 0 :(得分:4)

通过向您的某个类添加方法,可以更容易地了解为什么您尝试执行的操作无法通过某些修改而无法运行:

public class LivingThing<TExtremity> where TExtremity : Appendage {
    private TExtremity extremity;
    public void SetExtremity(TExtremity e) {
        extremity = e;
    }
}

现在让我们想象C#允许你完成任务。然后它应该让你这样做:

public class Hand : Appendage { }
...
// Let's pretend this works
LivingThing<Appendage> cat = new Cat();
// Now that C# let us do the assignment above, it must allow this too,
// because Cat is a LivingThing and Hand is an Appendage:
cat.SetExtremity(new Hand());
哎呀,我们有一只手拿着猫! C#不应该让我们这样做。

如果LivingThing具有返回 TExtremity的方法,则可以执行您希望执行的操作。 C#提供了定义继承层次结构的方法,使您可以根据自己的尝试灵活地进行分配。以下是您修改后的代码:

void Main()
{
    ILivingThing<Appendage> mysteryAnimal = new Cat();
}

public class Appendage { }
public class Paw : Appendage { }

public interface ILivingThing<out TExtremity> where TExtremity : Appendage { }
// You have a choice of keeping Animal a class. If you do, the assignment
// Animal<Appendage> mysteryAnimal = new Cat()
// would be prohibited.
public interface IAnimal<TExtremity> : ILivingThing<out TExtremity> where TExtremity : Appendage { }
public class Cat : Animal<Paw> { }

有一个问题:ILivingThing<TExtremity>IAnimal<TExtremity>都不允许具有TExtremity类型的可设置属性或以TExtremity为参数的方法。

答案 1 :(得分:1)

你要做的事情被称为&#34;协方差&#34 ;;将具有更多派生泛型参数的泛型类的实例分配给具有较少派生的类的变量。

在C#中,类不支持此功能。对于接口,必须明确指定它。以下将编译:

void Main()
    {
        ILivingThing<Appendage> mysteryAnimal = new Cat();
    }

    public class Appendage { }
    public class Paw : Appendage { }

    public interface ILivingThing<out TExtremity> where TExtremity : Appendage { }
    public class Animal<TExtremity> : ILivingThing<TExtremity> where TExtremity : Appendage { }
    public class Cat : Animal<Paw> { }

这对于您的实际代码是否可接受取决于LivingThing的定义方式;如果它是&#34;标记类&#34; (没有成员;仅仅是为了将其子项定义为LivingThings的派生点),或者没有非抽象成员的抽象类,它应该像魅力一样工作。如果您在此类中有成员代码,则需要从该类中提取协变接口。