2010-04-30 74 views
5

以下代码无法编译,产生一个“Widget必须是具有公共无参数构造函数的非抽象类型”错误。我会认为编译器拥有它需要的所有信息。这是一个错误?疏忽?还是有一些情况下,这将无效?为什么new()通用约束不满足在构造函数中带有可选参数的类?

public class Factory<T> where T : new() 
{ 
    public T Build() 
    { 
     return new T(); 
    } 
} 

public class Widget 
{ 
    public Widget(string name = "foo") 
    { 
     Name = name; 
    } 

    public string Name { get; set; } 
} 

public class Program 
{ 
    public static void Main() 
    { 
     var widget = new Widget(); // this is valid 
     var factory = new Factory<Widget>(); // compiler error 
    } 
} 
+0

寻呼Eric Lippert ....我认为这将是可选参数是一个编译器功能,但通用约束是CLR。所以可选参数被编译器替代,并且JIT只会看到(需要)参数。 – Richard 2010-04-30 17:29:01

+0

@Richard:基本上这是问题。可选参数对版本控制也有一些奇怪的副作用,正是由于这个原因... – 2010-04-30 17:33:30

回答

7

虽然这在逻辑上应该工作,但不幸的是没有。 CLR仍将您的构造函数看作是基于参数的构造函数。请记住,尽管C#支持可选参数,但它在编译时在编译器级完成。基础类型仍然只包含一个构造函数,只有一个参数。就CLR而言,“默认参数”被转换为属性,如下所示:

public Widget(([Optional, DefaultParameterValue("foo")] string name) { // ... 

CLR是一种多语言运行时。泛型被设计为在所有语言的CLR级别上工作,所以在没有默认参数的语言中,约束必须是真实的。不需要语言来理解OptionalAttribute,也不需要DefaultParameterValueAttribute,所以它不能对所有语言都一致地工作,因此它是不允许的。


编辑:

在回答您的评论:

我不明白的是,为什么C#编译器不能生成必要的代码,以满足CLR

理论上,C#编译器团队可以让语言生成两个独立的构造函数,而不是一个用属性标记的构造函数。这可能会爆炸成许多构造函数,因为命名参数为“构造函数”(或方法调用方法)的许多可能组合创建了功能,特别是在有多个参数可用时。我个人很高兴他们没有,因为它会导致混乱,因为生成的类型中有过多的方法和构造函数,这会导致公共API与生成它的代码看起来非常不同。看看下面的构造函数:

public Widget(
      int id = 0, 
      string name = "foo", 
      float width=1.0f, 
      float height=1.0f, 
      float depth=1.0f 
     ) { // ... 

是你自动生成这里所有可能的组合,编译器会需要产生构造这个单独的构造,因为有N!可能的方式来调用此...

+0

@Joshua:C#编译器生成上面粘贴的代码。这就是它的工作原理 - 它不是无参数的构造函数,而是一个可选的构造函数。我将编辑以添加详细信息... – 2010-04-30 17:34:48

+0

好的,谢谢,这解释了为什么它不起作用。我仍然想知道生成无参数构造函数的含义是什么。 – 2010-04-30 17:40:38

+0

@Joshua:我编辑了我的答案,以解释为什么这可能是一个坏主意......现在更有意义了吗?记住,它不只是可选的,你也可以使用命名参数,所以N个参数表示N!可能需要生成的组合... – 2010-04-30 17:42:01

相关问题