简体   繁体   English

泛型类型,其类型参数是抽象基类

[英]Generic type whose type parameter is an abstract base class

Let's say I have a base class named Animal and two derived classes named Lion and Tiger . 假设我有一个名为Animal基类和两个名为LionTiger 派生类 And I'd like to also have a generic type named AnimalList<Animal> to contain members of Animal . 我还想有一个名为AnimalList<Animal>通用类型来包含Animal成员。 For example: 例如:

var list = new AnimalList<Animal>();

list.Add(new Lion());
list.Add(new Tiger());

Very straightforward so far but there's a catch...I'd like the base class Animal to be abstract . 到目前为止非常简单,但是有一个问题……我希望基类Animalabstract That is, I don't want to allow an instance of the base class to be created. 也就是说, 我不想允许创建基类的实例。 However, making the base class abstract not surprisingly results in error CS0310 (see a complete example below). 但是,使基类抽象化不会令人惊讶地导致错误CS0310 (请参见下面的完整示例)。

I did come up with a solution: Make the base class non-abstract as required and simply throw an exception from the default (parameterless) constructor: 我确实提出了一个解决方案:根据需要使基类成为非抽象类,并仅从默认(无参数)构造函数中引发异常:

public Animal()
{
    throw new System.NotImplementedException();
}

But this makes me feel a little uneasy. 但这使我感到有些不安。 Is there a better way? 有没有更好的办法?


Here's a complete example that illustrates the scenario that I describe above: 这是一个完整的示例,说明了我上面描述的方案:

// public class Animal {}        // OK
public abstract class Animal {}  // 'abstract' keyword causes error 'CS0310'

public class Lion : Animal {}
public class Tiger : Animal {}

public class AnimalList<T> :
    Animal
    where T : Animal, new()
{
    public void Add(T animal) {}
}

class AnimalListDemo
{
    static public void Main(string[] args)
    {
        // The following statement causes:
        // error CS0310 - 'Animal' must be a non-abstract type with a
        // public parameterless constructor in order to use it as
        // parameter 'T' in the generic type or method 'AnimalList<T>'

        var list = new AnimalList<Animal>();

        list.Add(new Lion());
        list.Add(new Tiger());
    }
}

Just remove the new() constraint. 只需删除new()约束即可。 It requires you to provide a class that can be instantiated through new() (which is what the requirement is all about), but an abstract class obviously can't do this. 它要求您提供一个可以通过new()实例化的类(这是所有要求的全部内容),但是抽象类显然不能做到这一点。

From MSDN : MSDN

The new constraint specifies that any type argument in a generic class declaration must have a public parameterless constructor. 新约束指定通用类声明中的任何类型参数都必须具有公共的无参数构造函数。 To use the new constraint, the type cannot be abstract . 要使用新的约束,类型不能为abstract

Is it absolutely necessary to have the new() constraint on the generic parameter? 在泛型参数上绝对必须具有new()约束吗? If you remove it, it will compile, at the cost of not allowing creating new instances of T within AnimalList . 如果删除它,它将编译,但代价是不允许在AnimalList创建T新实例。

Reason for that is that if you request the class to have a public, parameterless constructor, it cannot be abstract, because abstract classes are meant specifically to be non-creatable, but you're requesting the class to be creatable with the new() . 这样做的原因是,如果您要求该类具有一个公共的,无参数的构造函数,则它不能是抽象的,因为抽象类是专门为不可创建的,但是您要求使用new()创建该类。 。 As those requirements contradict each other, the compiler complains. 由于这些要求彼此矛盾,因此编译器会抱怨。

The solution seems to be rather a hack, and will not work either, because the derived class constructor will also call the base class constructor (instead of completely override it) and still throw the exception. 该解决方案似乎是一种破解,并且也不起作用,因为派生类构造函数还将调用基类构造函数(而不是完全覆盖它),并且仍会引发异常。

At the end of my question, I asked: 在问题的最后,我问:

Is there a better way? 有没有更好的办法?

My conclusion from the helpful answers on this page: Yes! 从本页的有用答案得出的结论是的!

In particular, in his comment to my question, @CodesInChaos said the following: 特别是,@ CodesInChaos在对我的问题发表评论时说:

In general I'd consider the new() constraint a bit of a code smell. 通常,我会认为new()约束有点代码味道。

This got me searching for an alternative to the new() constraint. 这让我在寻找new()约束的替代方法。 (I do indeed have the need to create instances of the generic type parameter in a base class within my class hierarchy.) (我确实确实需要在我的类层次结构内的基类中创建泛型类型参数的实例。)

I found what I was looking for in this answer to Create a new instance of T without the new constraint : 我在以下答案中找到了要寻找的东西: 创建没有新约束的T的新实例

var instanceOfT = new T(); // Before

var instanceOfT = (T)Activator.CreateInstance(typeof(T)); // After

This allowed me to drop the few new() constraints that I had in my source code. 这使我可以删除源代码中的一些new()约束。 And finally, dropping the new() constraints allowed me to set an intermediate class to abstract as I desired. 最后,删除new()约束使我可以根据需要将中间类设置为abstract

Interfaces are allowed to be combined with the new constraint: 接口可以与new约束结合使用:

public interface IAnimal {}

public abstract class Animal : IAnimal {} 

public class AnimalList<T> where T : IAnimal, new() {}

However, your first line will still fail: 但是,您的第一行仍将失败:

var list = new AnimalList<Animal>();

AnimalList<Animal> (or AnimalList<IAnimal> ) is not a valid type because the given T can't be constructed. AnimalList<Animal> (或AnimalList<IAnimal> )不是有效类型,因为无法构造给定的T Are you sure new is what you want? 你确定新的是你想要什么?

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

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