通用方法和方法重载

use*_*291 21 c# generics

方法重载允许我们定义许多具有相同名称但具有不同参数集的方法(因此具有相同的名称但不同的签名).

这两种方法是否超载?

class A
{
    public static void MyMethod<T>(T myVal) { }
    public static void MyMethod(int myVal) { }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

不应该声明A<int>.MyMethod(myInt);抛出错误,因为构造类型A<int>有两个具有相同名称和相同签名的方法?

Eri*_*ert 47

这两种方法是否超载?

是.

不应该声明A<int>.MyMethod(myInt);抛出错误,因为构造类型A<int>有两个具有相同签名的方法?

这个问题没有意义; A您声明它不是通用类型.也许你打算问:

该语句是否A.MyMethod(myInt);会导致编译器报告错误,因为有两种不明确的候选方法?

没有.正如其他人所说,在这种情况下,重载解析更喜欢非通用版本.请参阅下面的更多细节.

或许你想要问:

A类声明首先应该是非法的,因为在某种意义上它有两种具有相同签名的方法,MyMethod并且MyMethod<int>

不,A型是完全合法的.通用arity是签名的一部分.因此,没有两种方法具有相同的签名,因为第一种方法具有通用arity零,第二种方法具有通用arity one.

或许你想要问:

class G<T> 
{
    public static void M(T t) {}
    public static void M(int t) {}
}
Run Code Online (Sandbox Code Playgroud)

G<T>可以构造泛型类型,使得它具有两个具有相同签名的方法.声明这种类型是否合法?

是的,声明这种类型是合法的.这通常是一个坏主意,但它是合法的.

然后你可以反驳:

但是我的Addison-Wesley发布的C#2.0规范副本在第479页声明"使用相同名称声明的两个函数成员...必须具有参数类型,这样任何封闭的构造类型都不能有两个具有相同名称的成员签名. "这有什么用?

当C#2.0最初被设计为计划时.然而,设计师意识到这种理想的模式将被视为非法:

class C<T> 
{
    public C(T t) { ... } // Create a C<T> from a given T
    public C(Stream s) { ... } // Deserialize a C<T> from disk
}
Run Code Online (Sandbox Code Playgroud)

现在我们说抱歉伙伴,因为你可以说C<Stream>,导致两个施工人员统一,整个班级都是非法的.那将是不幸的.显然,任何人都不可能用Stream作为类型参数来构造这个东西!

不幸的是,在文本更新到最终版本之前,规范已经出版.第479页的规则不是我们实施的规则.

继续代表您提出更多问题:

那么如果你打电话G<int>.M(123)或者在原始的例子中,如果你打电话A.MyMethod(123)会怎么样?

当重载决策面临两种由于通用结构而具有相同签名的方法时,那么通用结构的方法被认为是"不太具体"而不是"自然"的方法.一种不太具体的方法会失去更具体的方法.

那么,如果重载解析有效,为什么这是一个坏主意呢?

情况并A.MyMethod不太糟糕; 通常很容易明确地确定出哪种方法.但情况G<int>.M(123)要糟糕得多.CLR规则使这种情况"实现定义的行为",因此任何旧的事情都可能发生.从技术上讲,CLR可以拒绝验证构造类型的程序G<int>.或者它可能崩溃.事实上它既没有; 在糟糕的情况下,它能做到最好.

是否有这种类型结构的例子导致真正的实现定义行为?

是.有关详情,请参阅这些文章

http://blogs.msdn.com/b/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx

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

  • @aspnetonc:正确.方法可以具有相同的名称,但每个方法必须具有唯一的*签名*.C#语言规范将方法的签名定义为其名称,通用arity和参数类型的组合(并且注释只有一个是"out"而另一个是"ref"的签名被认为是相同的签名.)CLR对"签名"的定义略有不同,因为它还将参数类型和返回类型的返回类型和modopt/modreq修改器视为签名的一部分. (2认同)
  • @Joan:现在,lambdas有这个问题; 你如何键入绑定lambda的内部取决于它的上下文,这可能取决于lambda的内部.让它在常见情况下有效地工作花了我一年的大部分时间,并且仍然存在病态情况,你必须做大量的指数绑定.这种情况仅限于深度嵌套的lambda,这种情况很少见.嵌套函数调用很常见.一般来说,我们总是希望从表达式的*inside*到*outside*之间进行推理,而不是尝试双向进行. (2认同)
  • @ User437291:我不明白这个问题.这听起来像是在问"我不应该做一些不可能的事吗?" - 你*应该*或*不应该*做一些不可能的事情是一个有争议的问题; 无论是否道德,你都不会*做一些不可能的事情.假设答案是"确定,继续,编写一个不符合方法签名唯一性的CLR规则的方法重载".你怎么会开始那样做?您是否计划编写自己的元数据发射器,或者究竟是什么? (2认同)
  • @ user437291:因为CLR旨在实现C#以外的语言.许多语言都有"签名冲突"规则,这些规则不如C#规则严格.例如,在C++中,您可以使用两个方法void M(const int&x)和void M(int&x),这些方法是*不同的方法*,即使它们的签名看起来非常相似.对于编译器编写者来说,如果CLR不允许C++-to-IL编译器产生两个仅在const-ness中不同的重载,那么这将是繁重的,你不觉得吗? (2认同)