扩展方法如何挂钩

时间:2010-12-18 16:29:34

标签: c# .net

我只是想知道如何将Extension方法连接到Original类。我知道在IL代码中它调用了静态方法,但它是如何做到的,为什么不打破封装。

5 个答案:

答案 0 :(得分:16)

他们没有“勾结”。

Visaul Studio IDE只是通过在intellisense列表中显示它们来实现它。

编译器“知道”如何处理引用,以便使用正确的参数进行正确的方法调用。

这只是syntactic sugar - 这些方法只是一个单独的静态类上的静态方法。使用this修饰符可让编译器“知道”将ExtensionAttribute添加到类中以将其标记为扩展方法。

由于扩展方法实际上更改类,并且只能访问其上的公共成员,因此保留了封装。

来自MSDN

  

扩展方法是一种特殊的静态方法,但它们被称为,就好像它们是扩展类型的实例方法。

(强调我的)

答案 1 :(得分:9)

通过将this关键字放在静态方法的第一个参数前面来指定扩展方法:

public static void SomeExtension(this string s)
{
    ...
}

这只是使用System.Runtime.CompilerServices.ExtensionAttribute装饰方法的语法糖:

[Extension]
public static void SomeExtension(string s)
{
    ...
}

当编译器看到该属性时,它知道将扩展方法调用转换为适当的静态方法调用,并将该实例作为第一个参数传递。

由于调用只是普通的静态方法调用,因此没有机会破坏封装;与所有静态方法一样,这些方法只能访问扩展类型的公共接口。

答案 2 :(得分:4)

扩展方法只是语法糖,它们只是静态方法。您只能访问公共字段或属性,就像普通的静态方法一样。

答案 3 :(得分:2)

关键因素是类的实例方法与静态方法没有根本的区别。有一个小细节,他们有一个隐藏的论点。例如,String.IndexOf(char)方法实际上与CLR类似:

public static int IndexOf(string thisRef, char value) {
   // etc...
}

thisRef 参数是在代码中使用 this 或访问类成员时提供字符串引用的参数。如您所见,从扩展方法到实例方法,它是非常的一小步。 CLR中无需进行任何更改即可支持该功能。

另一个小的区别是编译器发出的代码检查对于实例方法是否为null,但对于扩展方法不这样做。您可以在null对象上调用扩展方法。虽然这可能看起来像一个特征,但它实际上是由扩展方法引起的限制,实际上并不是该类的成员。

在内部,CLR保留了类的方法列表,即MethodTable。扩展方法不在其中,阻止编译器发出 callvirt IL指令,它用于获取廉价空值检查的“技巧”。显式发出代码以进行空检查是可能的,但他们选择不这样做。不太清楚为什么。

另一个自动结果是扩展方法不能是虚拟的。

答案 4 :(得分:0)