在C#中存储泛型类型引用

时间:2016-08-03 12:04:35

标签: java c# generics

我将部分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();

但这有点像欺骗:( 任何想法?我做错了吗?

2 个答案:

答案 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>

无法正常工作,因为您通过说TFruit<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();