2009-08-18 47 views
18

考虑下面的类,调用构造函数重载时都超载具有相同签名

class Foo 
{ 
    public Foo(int count) 
    { 
     /* .. */ 
    } 

    public Foo(int count) 
    { 
     /* .. */ 
    } 
} 

上面的代码是无效的,将无法编译。现在考虑下面的代码,

class Foo<T> 
{ 
    public Foo(int count) 
    { 
     /* .. */ 
    } 

    public Foo(T t) 
    { 
     /* .. */ 
    } 
} 

static void Main(string[] args) 
{ 
    Foo<int> foo = new Foo<int>(1); 
} 

上面的代码是有效的,编译好。它调用Foo(int count)

我的问题是,如果第一个无效,第二个如何有效?我知道类Foo <T>是有效的,因为T和int是不同的类型。但是当它使用像Foo <int> foo = new Foo <int>(1),T得到整数类型和两个构造函数将具有相同的签名权?为什么编译器不显示错误而不是选择一个重载执行?

回答

19

时正在设计C#2.0,并在CLR通用类型系统您的问题已引起激烈辩论。实际上,事实上,A-W发布的“绑定”C#2.0规范实际上有一个错误的规则!有四种可能性:

1)是非法的声明泛型类,可能被一些建筑之下暧昧。 (这是绑定规范错误地说是规则。)所以你的Foo<T>声明是非法的。

2)是非法的构建体,其中将导致不确定性的方式的通用类。宣称Foo<T>将是合法的,构建Foo<double>将是合法的,但构建Foo<int>将是非法的。

3)使这一切合法,并使用重载的技巧,制定出通用或者非泛型的版本是否是更好的。 (这是C#实际上没有。)

4)做别的东西,我都没有想到的。

规则1是一个坏主意,因为它使一些非常普通和无害的情况变得不可能。考虑例如:

class C<T> 
{ 
    public C(T t) { ... } // construct a C that wraps a T 
    public C(Stream state) { ... } // construct a C based on some serialized state from disk 
} 

你想这是非法的只是因为C<Stream>是不明确的?呸。规则#1是一个坏主意,所以我们放弃了它。

不幸的是,它并不那么简单。 IIRC CLI规则指出,允许实现拒绝作为实际上会导致签名歧义的非法构造。也就是说,CLI规则类似于规则2,而C#实际上实现了规则3。这意味着理论上可能有合法的C#程序转化为非法代码,这是非常不幸的。

有关这些种种模糊之处,如何使我们的生活猥琐的一些想法,这里有几篇文章我关于这个问题写道:

http://blogs.msdn.com/ericlippert/archive/2006/04/05/569085.aspx

http://blogs.msdn.com/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx

+0

猜测第二个Foo <>在2必须是int – Dykam 2009-08-18 16:42:36

+0

“合法的C#程序可以翻译成非法代码”这怎么可能?我认为翻译结果是一个'newobj'指令和一个方法标记,其中'Foo .ctor(int)'和'Foo .ctor(T)'具有不同的方法标记。所以在IL中没有含糊之处。我错过了什么? – 2015-02-23 00:48:26

+1

@BenVoigt:首先,如果我说“不可验证的代码”或“具有实现定义的行为的代码”,那么代替“非法代码”就会更准确。产生签名碰撞的通用结构的后果是微妙的。假设我们有两个具有相同签名的方法,并希望在元数据中指出其中一个方法是实现特定的接口方法。没有可以在方法实现表中使用相同签名的两种方法消除歧义的元数据结构。还有其他类似的奇怪情况。 – 2015-02-23 19:18:45

23

没有歧义,因为编译器会选择Foo(...)相匹配的最具体的过载。由于具有泛型类型参数的方法被认为比相应的非泛型方法更具体,因此当T == int时,Foo(T)因此不如Foo(int)特定。因此,您正在调用Foo(int)过载。

你的第一种情况(有两个Foo(int)定义)是错误的,因为编译器只允许一个具有完全相同签名的方法的定义,而你有两个。

+0

我查了语言规范,但并没有说通用参数不如具体类型具体。然而,非泛型方法被认为比通用方法“更好”。 – Thorarin 2009-08-18 11:27:28

+0

你说得对,那句话不是很好。我的意思是说“具有泛型类型参数的方法...”。 – 2009-08-18 11:48:43

+0

顺便说一句,对于好奇,我认为Thorarin所讨论的部分是§14.4.2.2,它定义了“更好的函数成员”。 – 2009-08-18 11:49:38

9

Eric Lippert blogged关于这个最近。

+1

而且,显然他也在这里回答了这个问题。 – 2009-08-18 20:24:02

0

的事实是,他们不都具有相同的签名 - 一个使用泛型,而这另一个则不是。

有了这些方法的地方,你也可以使用非int对象调用它:

Foo<string> foo = new Foo<string>("Hello World");