Nav*_*K N 18 c# compiler-construction constructor overloading
考虑以下课程,
class Foo
{
    public Foo(int count)
    {
        /* .. */
    }
    public Foo(int count)
    {
        /* .. */
    }
}
Run Code Online (Sandbox Code Playgroud)
上面的代码无效,不会编译.现在考虑以下代码,
class Foo<T>
{
    public Foo(int count)
    {
        /* .. */
    }
    public Foo(T t)
    {
        /* .. */
    }
}
static void Main(string[] args)
{
    Foo<int> foo = new Foo<int>(1);
}
Run Code Online (Sandbox Code Playgroud)
以上代码有效且编译良好.它调用Foo(int count).
我的问题是,如果第一个无效,第二个如何有效?我知道类Foo <T>是有效的,因为T和int是不同的类型.但是当它像Foo <int> foo = new Foo <int>(1)一样使用时,T会得到整数类型,并且两个构造函数都具有相同的签名吗?为什么编译器不显示错误而不是选择执行重载?
Joh*_*lla 23
没有歧义,因为编译器将选择Foo(...)匹配的最具体的重载.由于具有通用型参数的方法被认为是比对应的非通用方法不太具体,Foo(T)因此小于特定Foo(int)时T == int.因此,您正在调用Foo(int)重载.
您的第一个案例(有两个Foo(int)定义)是一个错误,因为编译器只允许一个具有完全相同签名的方法定义,并且您有两个.
Eri*_*ert 19
在设计C#2.0和CLR中的泛型类型系统时,您的问题引起了激烈的争论.事实上,由AW发布的"绑定"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
}
Run Code Online (Sandbox Code Playgroud)
你想要那是非法的只是因为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