简体   繁体   English

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

[英]Store generic type reference in C#

i'm porting some of my Java code to C# and i'm having trouble replicating this behavior: 我将某些Java代码移植到C#,并且在复制此行为时遇到了麻烦:

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

public abstract class Fruit<T extends Fruit>{
   //Fruit implementation
}

This is great because i only want generic types that extend Fruit. 这很棒,因为我只想要扩展Fruit的通用类型。 Then i can store a reference for all of the concret fruit objects like this: 然后,我可以为所有具体水果对象存储一个引用,如下所示:

Banana banana = new Banana(); //This class extends Fruit
Strawberry strawberry = new Strawberry (); //This class extends Fruit

Fruit fruit;
fruit = banana;
//or
fruit = strawberry;

This works just fine. 这样很好。 Now i'm trying the same in C# and the Fruit class is declared like this: 现在我正在C#中尝试相同的操作,并且Fruit类的声明如下:

***** C# CODE ***** ***** C#代码*****

abstract public class Fruit<T> where T : Fruit<T> {
  //Fruit implementation
}

But in C#, i can't store a reference like this: 但是在C#中,我不能存储这样的引用:

Fruit fruit; //This gives a compilation error!

i'm not able to store the banana and the strawberry in the same reference, i can only do this: 我无法将香蕉和草莓存储在同一参考中,我只能这样做:

Fruit<Banana> fruit;
fruit = banana;
//or
Fruit<Strawberry> fruit;
fruit = strawberry;

I think i can get around it by adding an inheritance level like this: 我认为我可以通过添加这样的继承级别来解决它:

abstract public class GenericFruit<T> where T : GenericFruit<T> {}

and then create the Fruit class equivalent 然后创建等效的Fruit类

abstract public class Fruit : GenericFruit<Fruit>{}

and now extending Banana and Strawberry from Fruit like this: 现在像这样从水果中扩展香蕉和草莓:

public class Banana : Fruit {}
public class Strawberry : Fruit {}

and then store a Fruit reference: 然后存储一个Fruit引用:

Fruit fruit;
fruit = new Banana();
fruit = new Strawberry();

But this somehow feels like cheating :( Any ideas? Am i doing something wrong? 但这听起来有点像作弊:(有什么想法吗?我做错了吗?

The problem you're running into is that you're trying to "forget" (or erase) some of the type information you've created. 您遇到的问题是您试图“忘记”(或擦除)您创建的某些类型信息。 Let's make the example a little more concrete by adding a method to your base class: 让我们通过在基类中添加一个方法来使示例更加具体:

public abstract class Fruit<T> where T : Fruit<T>
{
    public abstract T GetSeedless();
}

Okay, now let's look more closely at what you're trying to do. 好的,现在让我们更仔细地看看您要做什么。 Let's assume that you could do exactly what you wanted and you had a fruit basket: 假设您可以完全按照自己的意愿去做,并且拥有一个水果篮:

Fruit fruit = new Apple();
var seedlessFruit = fruit.GetSeedless();

Okay, what is the type of seedlessFruit ? 好吧,是什么类型seedlessFruit You might be inclined to say that it's Fruit and that would be reasonable, but C# doesn't allow this. 您可能倾向于说它是Fruit ,那是合理的,但是C#不允许这样做。 C# doesn't let you erase the generic parameter of a class. C#不允许您擦除类的通用参数。 When you declared Fruit<T> you condemned all Fruit to have a generic parameter, you can't erase that. 当您声明Fruit<T>您谴责所有Fruit具有通用参数,您无法删除该参数。

I think you're close to a solution, but I think you have it a bit upside down. 我认为您已接近解决方案,但我认为您有一点倒挂的想法。 Instead of having the non-generic Fruit inherit from GenericFruit<Fruit> , you should flip it and have the generic version inherit from the non-generic one. 而不是让非泛型的FruitGenericFruit<Fruit>继承,您应该翻转它并使泛型版本从非泛型的Fruit继承。

I also have one other recommendation and that's to make the non-generic Fruit into an interface rather than an abstract class. 我还有一个建议,那就是将非泛型的Fruit变成一个接口而不是一个抽象类。 I'll demonstrate why (ultimately it's because C# doesn't allow return type covariance when overriding methods; a bummer to be sure). 我将说明原因(最终是因为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();
    }
}

What I've done here is sort of created a fake return type covariance by explicitly implementing the IFruit interface in the Fruit class. 我在这里所做的是通过在Fruit类中显式实现IFruit接口来创建假返回类型协方差。 This now allows you to store different kinds of fruits in the same reference and still use the GetSeedless method: 现在,您可以将不同种类的水果存储在同一引用中,并且仍然使用GetSeedless方法:

IFruit fruit = new Apple();
var seedlessFruit = fruit.GetSeedless();

This also allows you to pick and choose which methods and properties should be available when you want to erase the generic information. 这也使您可以选择要擦除通用信息时应使用的方法和属性。 Each of those methods can be implemented explicitly in the base class and "replaced" with generic versions. 这些方法中的每一个都可以在基类中显式实现,并用通用版本“替换”。 That way if you do have the generic type information, you can work with more specific types. 这样,如果您确实具有通用类型信息,则可以使用更特定的类型。

First of all, this: 首先,这是:

abstract public class Fruit<T> where T : Fruit<T>

Just can't work, because you are creating an infinite loop by saying T is Fruit<T> . 就是行不通,因为您通过说TFruit<T>来创建无限循环。 (Start to replace T in Fruit<T> by Fruit<T> , you will see it's impossible to end). (开始更换TFruit<T>Fruit<T>你会看到这是不可能结束)。

EDIT: As Kyle said, this works. 编辑:正如凯尔所说,这可行。

The solution could be: 解决方案可能是:

abstract public class Fruit
{
    // Generic implementation
}

abstract public class Fruit<T> : Fruit
    where T : Fruit // edit: or Fruit<T>
{
    // Overriding generic implementation
}

And you could have: 您可能会:

public class Banana : Fruit<YourType> // edit: or Fruit<Banana>
{
    // Specific implementation
}

Finally, this should work nicely: 最后,这应该很好地工作:

Fruit fruit;
fruit = new Banana();
fruit = new Strawberry();

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

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