在 C# 中存储泛型类型引用
本文关键字:泛型类型 引用 存储 | 更新日期: 2023-09-27 18:00:05
我正在将我的一些 Java 代码移植到 C#,但在复制此行为时遇到问题:
JAVA CODE *****
public abstract class Fruit<T extends Fruit>{
//Fruit implementation
}
这很棒,因为我只想要扩展 Fruit 的泛型类型。然后我可以像这样存储所有混凝土水果对象的引用:
Banana banana = new Banana(); //This class extends Fruit
Strawberry strawberry = new Strawberry (); //This class extends Fruit
Fruit fruit;
fruit = banana;
//or
fruit = strawberry;
这工作得很好。现在我正在 C# 中尝试相同的操作,并且 Fruit 类声明如下:
C# 代码 *****
abstract public class Fruit<T> where T : Fruit<T> {
//Fruit implementation
}
但是在 C# 中,我无法存储这样的引用:
Fruit fruit; //This gives a compilation error!
我无法将香蕉和草莓存储在同一个参考中,我只能这样做:
Fruit<Banana> fruit;
fruit = banana;
//or
Fruit<Strawberry> fruit;
fruit = strawberry;
我想我可以通过添加这样的继承级别来解决它:
abstract public class GenericFruit<T> where T : GenericFruit<T> {}
,然后创建等效的 Fruit 类
abstract public class Fruit : GenericFruit<Fruit>{}
现在从水果中扩展香蕉和草莓,如下所示:
public class Banana : Fruit {}
public class Strawberry : Fruit {}
,然后存储水果引用:
Fruit fruit;
fruit = new Banana();
fruit = new Strawberry();
但这在某种程度上感觉像作弊:(有什么想法吗?我做错了什么吗?
您遇到的问题是您试图"忘记"(或删除(您创建的某些类型信息。 让我们通过向基类添加一个方法来使示例更具体一些:
public abstract class Fruit<T> where T : Fruit<T>
{
public abstract T GetSeedless();
}
好的,现在让我们更仔细地看看你要做什么。 让我们假设你可以做你想做的事,并且你有一个水果篮:
Fruit fruit = new Apple();
var seedlessFruit = fruit.GetSeedless();
好的,seedlessFruit
的类型是什么? 您可能倾向于说它Fruit
这是合理的,但 C# 不允许这样做。 C# 不允许擦除类的泛型参数。 当您声明Fruit<T>
谴责所有Fruit
都有一个泛型参数时,您无法擦除它。
已经接近一个解决方案,但我认为你有点颠倒了。 与其让非泛型Fruit
从GenericFruit<Fruit>
继承,不如翻转它,让泛型版本从非泛型版本继承。
我还有一个建议,那就是将非泛型Fruit
变成一个接口而不是一个抽象类。 我将演示原因(最终是因为 C# 在重写方法时不允许返回类型协方差;可以肯定的是,这很糟糕(。
public interface IFruit
{
IFruit GetSeedless();
}
public abstract class Fruit<T> : IFruit where T : Fruit<T>
{
public abstract T GetSeedless();
IFruit IFruit.GetSeedless()
{
return GetSeedless();
}
}
我在这里所做的是通过在 Fruit
类中显式实现 IFruit
接口来创建假返回类型协方差。 现在,这允许您在同一引用中存储不同种类的水果,并且仍然使用 GetSeedless
方法:
IFruit fruit = new Apple();
var seedlessFruit = fruit.GetSeedless();
这还允许您在要擦除通用信息时选择哪些方法和属性应该可用。 这些方法中的每一个都可以在基类中显式实现,并"替换"为泛型版本。 这样,如果您确实具有泛型类型信息,则可以使用更具体的类型。
首先,这个:
abstract public class Fruit<T> where T : Fruit<T>
只是行不通,因为你通过说T
是Fruit<T>
来创建一个无限循环。(开始用Fruit<T>
替换Fruit<T>
中的T
,你会发现它不可能结束(。
编辑:正如凯尔所说,这是有效的。
解决方案可能是:
abstract public class Fruit
{
// Generic implementation
}
abstract public class Fruit<T> : Fruit
where T : Fruit // edit: or Fruit<T>
{
// Overriding generic implementation
}
你可以有:
public class Banana : Fruit<YourType> // edit: or Fruit<Banana>
{
// Specific implementation
}
最后,这应该可以很好地工作:
Fruit fruit;
fruit = new Banana();
fruit = new Strawberry();