繁体   English   中英

将通用类型引用存储在C#中

[英]Store generic type reference in C#

我将某些Java代码移植到C#,并且在复制此行为时遇到了麻烦:

***** JAVA代码*****

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;
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具有通用参数,您无法删除该参数。

我认为您已接近解决方案,但我认为您有一点倒挂的想法。 而不是让非泛型的FruitGenericFruit<Fruit>继承,您应该翻转它并使泛型版本从非泛型的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>

就是行不通,因为您通过说TFruit<T>来创建无限循环。 (开始更换TFruit<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();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM