我将部分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#CODE *****
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;
fruit = new Banana();
fruit = new Strawberry();
但这有点像欺骗:( 任何想法?我做错了吗?
答案 0 :(得分:3)
您遇到的问题是您正试图忘记&#34; (或删除)您创建的一些类型信息。让我们通过向您的基类添加一个方法使示例更加具体:
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();
}
}
我在这里所做的是通过在IFruit
类中明确实现Fruit
接口来创建假返回类型协方差。现在,您可以在同一参考中存储不同种类的水果,并仍然使用GetSeedless
方法:
IFruit fruit = new Apple();
var seedlessFruit = fruit.GetSeedless();
这也允许您在想要删除通用信息时选择可用的方法和属性。这些方法中的每一个都可以在基类中明确实现,并且替换为&#34;与通用版本。这样,如果您确实拥有泛型类型信息,则可以使用更具体的类型。
答案 1 :(得分:2)
首先,这个:
abstract public class Fruit<T> where T : Fruit<T>
无法正常工作,因为您通过说T
是Fruit<T>
来创建无限循环。
(开始将T
中的Fruit<T>
替换为Fruit<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();