【问题标题】:Why does Generic class signature require specifying new() if type T needs instantiation ?如果类型 T 需要实例化,为什么通用类签名需要指定 new() ?
【发布时间】:2009-12-03 13:03:55
【问题描述】:

我正在编写一个通用类,如下所示。

public class Foo<T> : 
    where T : Bar, new()
{
    public void MethodInFoo()
    {
        T _t = new T();
    }
}

如您所见,类型 T 的对象 _t 在运行时被实例化。为了支持泛型类型 T 的实例化,该语言强制我将 new() 放入类签名中。如果 Bar 是一个抽象类,我会同意这一点,但如果 Bar 标准非抽象类具有公共无参数构造函数,为什么需要如此。

如果未找到 new(),编译器会提示以下消息。

无法创建变量类型“T”的实例,因为它没有 new() 约束

【问题讨论】:

  • 感谢大家的回答。答案的内容非常适合在问题主题上进行头脑风暴。

标签: c# generics


【解决方案1】:

因为通常不假设模板参数需要[非抽象和]可构造[通过公共无参数构造函数]才能使类型匹配模板参数定义。

直到您在模板上添加:new() 约束:

  • 编译器不允许你构造T
  • 编译器将允许您将 T 与抽象类型或没有公共无参数构造函数的类型匹配

:Bar 位是正交的,意味着:

  • 不要让人们匹配不是从 [或者是] Bar 派生的类型
  • 让我将 Ts 转换为 Bar 或在正文中从 Bar 派生的类型
  • 让我在T 上调用Bar 的公共和范围内的内部方法

【讨论】:

  • 我不喜欢“默认构造函数”的措辞,因为在某些情况下可能不清楚。我的表述更像:添加, new() 强制用户代码使用具有 no-argument 构造函数的泛型类型参数。
  • @Romain:MSDN 将其称为“公共无参数构造函数”。 msdn.microsoft.com/en-us/library/d5x73970.aspx
  • 更正了问题!我从来没有注意到这一点。谢谢指正。
  • @Kobi:默认构造函数的定义稍微宽泛一些,可以考虑包括所有参数都具有默认值的构造函数等。此外,转换运算符之类的东西也开始被考虑(在读者的脑海中)。我最初的回答中缺少的一个关键点是它必须是public
  • +0 答案的内容在技术上是正确的,但除了有效地说“你不能”之外,实际上并没有回答问题的“为什么”部分,因为这不是它的工作方式。是的,但为什么会这样呢?
【解决方案2】:

仅仅因为 Bar 类定义了一个无参数构造函数,并不意味着所有 Bar 都会这样做 - 可能有一个类继承自 Bar 但隐藏了无参数构造函数。这样的类将满足Bar 约束,但理所当然地不符合new() 约束。

(请注意,如果您使用 Bar sealed 来避免这种可能性,您可以(可以理解地)不再将其用作通用约束) - edit 尝试这样做会产生编译器错误 CS0701。

【讨论】:

  • 虽然这是真的,但编译器并不关心 Bar 是否有一个公共的无参数构造函数 [或者让你在正文中使用它],直到你说::new()
  • +1。简单、直接、正确并回答问题中的“为什么”。
  • 您确定密封会排除它与模板规范匹配并实际生成编译器错误吗? (我可以想象编译器会弄清楚这一点,如果它被密封并没有多大意义,但它真的会抱怨吗)?
  • @AnthonyWJones:有趣的是,如果你回顾一下我的回答的历史,你会看到我的回答是以这种精神开始的,直到我添加了一些东西来捕捉所有其他观点。我对这个答案的评论反映了这样一个事实,即当编译器假设没有这样的事情直到你告诉它时,谈论 Bar 有一个默认构造函数有点误导,并且不会让你构造一个 Bar()
  • 可以使用密封类作为通用约束,除非约束意味着单个类。例如,可能有一个具有泛型类型 T 和 U 的类,这样 U 继承了 T,并将密封类传递给 T 和 U。
【解决方案3】:

可能是因为如果你不包含new() 约束,那么T 可以合法地成为Bar 的子类,没有默认(即公共和无参数)构造函数,在这种情况下,new T() 语句里面该方法将无效。

  1. 只有Bar 作为约束条件,T 可以是BarBar 的任何派生类,有或没有默认构造函数。
  2. 只有new() 作为约束,T 可以是任何具有默认构造函数的类型。
  3. Barnew() 作为约束条件,T 必须是BarBar 的子类,并且还必须具有默认构造函数。

【讨论】:

  • @Ruben:没错!从Bar 派生的类型不一定有默认构造函数,即使Bar 本身有,因此也需要new() 约束。
  • +1:很好地列举了各种可能性;没有松散的、潜在的误导性信息(以及我的回答中的其他重要信息)
【解决方案4】:

因为 Bar 的子类可能没有无参数构造函数。

where T : Bar

表示 Bar 或 Bar 的子类。如果你只想要一个 Bar 的实例,你就不会使用泛型。有很多实例(例如 Object 和 String),超类有一个无参数的构造函数,而子类没有。

【讨论】:

  • 和其他答案一样,在你说 :new() 之前,T 不必有 [public parameterless] 构造函数(并且主体不能尝试使用它]
【解决方案5】:

虽然Bar 可能是具体的,但派生类T 本身可能是抽象的或缺少默认构造函数。

【讨论】:

  • 我会说 may be 而不是 is。模板规范在你说new()之前都不在乎,但不会让你在正文中new()
  • 在我的回答中 s/template/generic method/ :D 还有 s/thederived/a derived/。 s/默认构造函数/公共无参数构造函数/
【解决方案6】:

您可能已经使用了 Bar 构造函数:

T _t = new Bar();

没有new() 约束。但是,您使用了 T 构造函数,编译器不能也不会假设在添加 new() 约束之前可以构造绑定到 T 的类型。

【讨论】:

  • +1 好点 - 我会添加“并且编译器不能也不会假设构造绑定到 T 的类型是可能的,直到您添加 new() 约束。如果我是提问者,然后我会在这个和我的作为接受的答案之间徘徊:P
【解决方案7】:

对于那些不确定的,记住你可以使用

where T : IDeviceCommand

要求 T 实现某个接口作为最低合同要求。您可以将上述内容表述为“如果提供的 Type 'T' 至少实现了 IDeviceCommand 接口,您可以给我打电话”。当然,这允许您对“T”为您提供操作方法的设施做出一系列(正确的)假设。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多