为什么ref和out不足以消除C#中的重载歧义?

例如,为什么这个方法Max(ref int x, ref int y)不被认为是Max(int x, int y)重载? 为什么同样out

这个问题预示着一个错误的前提。

  Max(int x, int y) Max(ref int x, ref int y) Max(out int x, out int y) 

都是名为Max的方法的重载。 但是,请注意,在任何给定的类定义中,最后两个中只有一个可能存在。 从规范§3.6:

方法的签名包括方法的名称,类型参数的数量以及每个forms参数的类型和种类(值,引用或输出),按从左到右的顺序考虑。 出于这些目的,在forms参数类型中出现的方法的任何类型参数不是通过其名称来标识,而是通过其在方法的类型参数列表中的序号位置来标识。 方法的签名特别不包括返回类型,可以为最右边的参数指定的params修饰符,也不包括可选的类型参数约束。

[…]

虽然outref参数修饰符被认为是签名的一部分,但在单一类型中声明的成员只能通过refout在签名上有所不同。 如果在具有out修饰符的两个方法中的所有参数都更改为ref修饰符的情况下,如果两个成员在同一类型中声明具有相同签名的成员,则会发生编译时错误。 对于签名匹配的其他目的(例如,隐藏或覆盖), refout被认为是签名的一部分并且彼此不匹配。 (此限制是为了允许C#程序轻松转换为在公共语言基础结构(CLI)上运行,这不提供定义不同于refout方法的方法。)

refout是同一个东西,在语义上。 CLR没有区分这两者。 C#语言正在进行区分。 对于CLR,只有ref

语言设计者决定以这种方式创建它。

怀疑 (这只是我的意见),这是因为必须指定refout以减少混淆。 该语言可以很容易地设计为不需要显式指定ref / out ,并让编译器隐式地执行它,但这会引起混淆。 同样,使这些重载会阻止其他语言以这种方式编写(因为CLR不仅仅是C#),并且只会增加许多用户的整体混淆,并且不太清楚调用哪种方法。

我们来写一些代码:

 static void M1(int y) { Console.WriteLine("val"); } static void M1(ref int y) { Console.WriteLine("ref"); } //static void M1(out int y) // compile error //{ // Console.WriteLine("out"); //} static void Main2() { int a = 3; M1(a); M1(ref a); // M1(out a); } 

Ther只是refout版本之间的冲突。 注释掉out参数方法,它按预期编译和运行:输出为valref