Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当两个重载具有相同的签名时调用构造函数重载_C#_Compiler Construction_Constructor_Overloading - Fatal编程技术网

C# 当两个重载具有相同的签名时调用构造函数重载

C# 当两个重载具有相同的签名时调用构造函数重载,c#,compiler-construction,constructor,overloading,C#,Compiler Construction,Constructor,Overloading,考虑下一节课 class Foo { public Foo(int count) { /* .. */ } public Foo(int count) { /* .. */ } } 以上代码无效,无法编译。现在考虑下面的代码, class Foo<T> { public Foo(int count) { /* .. */ } public Foo(T

考虑下一节课

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);
}
class-Foo
{
公共食物(整数)
{
/* .. */
}
公共食物(T)
{
/* .. */
}
}
静态void Main(字符串[]参数)
{
Foo-Foo=新Foo(1);
}
上面的代码是有效的,编译得很好。它调用Foo(int count)


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

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


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

Eric Lippert最近对此进行了讨论

事实上,它们并不都具有相同的特征——一个是使用泛型,而另一个不是

有了这些方法,您还可以使用非int对象调用它:

Foo<string> foo = new Foo<string>("Hello World");
Foo-Foo=new-Foo(“你好世界”);

在设计C#2.0和CLR中的泛型类型系统时,您的问题引起了激烈的争论。事实上,A-W发布的“绑定”C#2.0规范中包含了错误的规则,这是如此之热!有四种可能性:

1) 将声明在某些构造下可能不明确的泛型类设置为非法。(这是绑定规范错误地说的规则。)因此您的
Foo
声明将是非法的

2) 以产生歧义的方式构造泛型类是非法的。声明
Foo
是合法的,构造
Foo
是合法的,但是构造
Foo
是非法的

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类
{
公共C(T){…}//构造一个包装T的C
公共C(流状态){…}//基于磁盘中的某些序列化状态构造C
}
你想因为
C
模棱两可而将其视为非法吗?讨厌。规则1是个坏主意,所以我们放弃了它

不幸的是,事情并非如此简单。IIRC CLI规则规定,允许实现拒绝实际导致签名模糊的非法构造。也就是说,CLI规则类似于规则2,而C实际上实现了规则3。这意味着理论上可能会有合法的C#程序翻译成非法代码,这是非常不幸的

关于这些模棱两可的东西是如何让我们的生活变得悲惨的,我写了几篇关于这个话题的文章:


我检查了语言规范,但它没有说泛型参数不如具体类型具体。然而,非泛型方法被认为比泛型方法“更好”。你是对的,它的措辞不是很好。我的意思是“具有泛型类型参数的方法…”。顺便说一句,出于好奇,我认为Thorarin所讨论的部分是§14.4.2.2,它定义了“更好的函数成员”。我理解这一点。但在我看来,编译器应该发出警告,因为这种类型的行为会造成混乱。不确定编译器如何通过警告的方式在这里做任何有用的事情-一旦定义了Foo(T T)-所有其他具有该名称的单参数方法都会与您建议的检查类型相冲突。我猜第三个Foo在2处必须是int“翻译成非法代码的合法C#程序”这怎么可能?我认为翻译会产生
newobj
指令和方法标记,其中
Foo.ctor(int)
Foo.ctor(T)
有不同的方法标记。因此IL中没有歧义。我遗漏了什么吗?@BenVoigt:首先,如果我说“无法验证的代码”或“具有实现定义行为的代码”,它会比“非法代码”更准确“。产生签名冲突的泛型构造的后果是微妙的。例如,假设我们有两个具有相同签名的方法,并且希望在元数据中指出其中一个是特定接口方法的实现。方法实现表中没有元数据结构可以消除具有相同签名的两个方法之间的歧义。还有其他类似的奇怪情况。@BenVoigt:你可能是对的,在具体的构造函数案例中,不会有问题。显然,他在这里也回答了这个问题。