我有以下代码块作为我遇到的问题的简化示例。 但是我收到一个错误,声称我无法将一种类型转换为另一种类型。我使用 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 的定义是使用 LivingThing
和 Appendage
的子类时,为什么我不能将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# 不应该让我们这样做。
但是,如果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
作为参数的方法。
您尝试执行的操作称为"协方差";将具有派生参数较多的泛型类的实例分配给派生参数较少的类的变量。
在 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 的定义方式;如果它是一个"标记类"(没有成员;存在只是将其子级标识为 LivingThings 的派生点),或者是一个没有非抽象成员的抽象类,它应该像一个魅力一样工作。如果此类中有成员代码,则需要从类中提取协变接口。