【问题标题】:Generic type whose type parameter is an abstract base class类型参数是抽象基类的泛型类型
【发布时间】:2014-01-31 14:47:40
【问题描述】:

假设我有一个名为Animal基类和两个名为LionTiger派生类。我还想要一个名为AnimalList<Animal>通用类型 来包含Animal 的成员。例如:

var list = new AnimalList<Animal>();

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

到目前为止非常简单,但有一个问题...我希望基类 Animalabstract。也就是说,我不想允许创建基类的实例。但是,将基类抽象化会导致错误CS0310(请参阅下面的完整示例),这并不奇怪。

我确实想出了一个解决方案:根据需要使基类非抽象,并简单地从默认(无参数)构造函数中抛出异常:

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

但这让我感到有些不安。 有更好的方法吗?


这里有一个完整的例子来说明我上面描述的场景:

// 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());
    }
}

【问题讨论】:

  • new() 约束要求类具有可调用的默认构造函数,但抽象类没有。抽象类的全部意义在于您毕竟无法构造它们。如果您删除该约束,您的示例代码可以正常工作。一般来说,我认为new() 约束有点代码味道。
  • 正如@CodesInChaos 建议的那样,它不是abstract 关键字,而是new() 约束:rextester.com/LXDH46444
  • 无论如何都限制在抽象类的泛型定义有什么好处?为什么不只是class AnimalList : Animal, IList&lt;Animal&gt;
  • @CodesInChaos - 你的评论让我走上了正轨。请参阅my answer
  • @LorentzVedeler - 你说得很好。我真实代码中的模式实际上包括层次结构中的另一个级别:abstract class AnimalList&lt;Animal&gt; : Animal, IList&lt;Animal&gt;class ExtendedLionList : AnimalList&lt;Lion&gt;

标签: c# generics abstract-class


【解决方案1】:

只需删除 new() 约束即可。它要求你提供一个可以通过new() 实例化的类(这就是要求的全部内容),但抽象类显然不能做到这一点。

来自MSDN

新约束指定泛型类声明中的任何类型参数都必须具有公共无参数构造函数。 要使用新的约束,类型不能是抽象的

【讨论】:

    【解决方案2】:

    是否绝对有必要对泛型参数使用new() 约束?如果删除它,它将编译,但不允许在 AnimalList 内创建 T 的新实例。

    这样做的原因是,如果您要求该类具有公共的无参数构造函数,则它不能是抽象的,因为抽象类专门用于不可创建,但您要求该类可使用new()。由于这些要求相互矛盾,编译器会抱怨。

    该解决方案似乎是一个 hack,并且也不起作用,因为派生类构造函数也会调用基类构造函数(而不是完全覆盖它)并且仍然抛出异常。

    【讨论】:

      【解决方案3】:

      在我的问题结束时,我问:

      有没有更好的方法?

      我从这个页面上的有用答案得出的结论:是的!

      特别是,@CodesInChaos 在他对我的问题的评论中说:

      一般来说,我认为new() 约束有点代码味道。

      这让我寻找new() 约束的替代方案。 (我确实需要在我的类层次结构中的基类中创建泛型类型参数的实例。)

      我在Create a new instance of T without the new constraint 的回复中找到了我想要的东西:

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

      这让我可以放弃我在源代码中的几个new() 约束。最后,删除new() 约束允许我根据需要将中间类设置为abstract

      【讨论】:

      • 我认为Activator.CreateInstance 有点代码味道。
      【解决方案4】:

      接口可以与new约束结合使用:

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

      但是,您的第一行仍然会失败:

      var list = new AnimalList<Animal>();
      

      AnimalList&lt;Animal&gt;(或AnimalList&lt;IAnimal&gt;)不是有效类型,因为无法构造给定的T。你确定new是你想要的吗?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-10
        • 2012-01-16
        • 1970-01-01
        • 2013-09-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多