C#中Foo.Do(int x)实例方法与其同名扩展方法是否签名相同?
定义在Foo类的实例方法Foo.Do(int x)和静态类FooExtensions中的扩展方法FooExtensions.Do(this Foo foo, int x)不具有相同的方法签名。
两类方法的定义如下:
class Foo { public void Do(int x) { Console.WriteLine("Foo.Do()"); } } static class FooExtensions { public static void Do(this Foo foo, int x) { Console.WriteLine("FooExtensions.Do()"); } }
1. 签名判定的基础边界
微软官方文档《Methods (C# Programming Guide)》对方法签名的规则说明如下:
方法在类、结构或接口中声明时,需指定访问级别(如public、private)、可选修饰符(如abstract、sealed)、返回值、方法名称以及方法参数,上述部分共同构成方法的签名。
在方法重载判定场景下,方法的返回类型不属于方法签名的组成部分;但在判定委托与其指向方法的兼容性时,返回类型属于方法签名的组成部分。
上述重载判定规则的适用范围是同一个类、结构、接口范围内声明的方法,跨类型的方法不属于同一个重载判定集合,不存在签名冲突的前提。方法签名的判定天然以方法的声明所属类型为边界,跨不同类型定义的方法,就算名称和参数看起来相似,也不会被判定为相同签名。
2. 两个方法的实际签名构成差异
从C#语言规范和元数据层面看,两者的签名组成有明确区别:
Foo.Do(int x)是实例方法,归属Foo类型:C#语言层面的签名中,隐式传入的实例this指针不算在显式声明的参数列表内,该方法的显式参数只有int x,方法名Do,返回值void。FooExtensions.Do(this Foo foo, int x)是静态扩展方法,归属FooExtensions类型:它本质是带特殊元数据标记的静态方法,第一个参数前的this是扩展方法的专属标识,该方法的显式参数列表为Foo foo, int x,和前者的参数长度、首个参数类型都不匹配。
容易产生混淆的点在于,两者的调用语法看起来一致——都是fooObj.Do(xxx)的形式,但这是扩展方法的语法糖效果,不代表两者签名相同。
C#编译器的方法调用解析规则明确:当调用方的实例类型本身定义了匹配调用参数的同名实例方法时,会优先绑定实例方法,永远不会匹配到同名扩展方法。直接运行上述示例代码,调用new Foo().Do(1)的输出永远是Foo.Do(),不会触发扩展方法的逻辑。
内容的提问来源于stack exchange,提问作者Display Name

