泛型和 Class 继承混乱

Generics and Class Inheritance confusion

我有以下代码块作为我遇到的问题的简化示例。但我收到一条错误消息,声称我无法将一种类型转换为另一种类型。我用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 的定义使用 LivingThingAppendage 的子 类 时,为什么我不能将 Cat 转换为 LivingThing<Appendage>

通过向您的 类:

之一添加一种方法,可以更容易地理解为什么您尝试做的事情在不进行一些修改的情况下无法工作
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# 不应该让我们这样做。

不过,如果 LivingThingreturn 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 作为参数的方法。

你想做的事情叫做"covariance";将具有更多派生泛型参数的泛型 class 的实例分配给具有较少派生参数的 class 的变量。

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

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 的定义方式;如果它是一个 "marker class"(没有成员;存在只是为了将其子项标识为 LivingThings 的派生点),或者是一个没有非抽象成员的抽象 class,它应该像一个魅力一样工作。如果您在此 class 中有成员代码,则需要从 class.

中提取协变接口