- C#只支持扩展方法,不支持扩展属性、扩展事件、扩展操作符
- 扩展方法必须在非泛型的静态类中声明,而且扩展方法至少要有一个参数,而且只有第一个参数能用this关键字标记
- C#在静态类中查找方法时,要求静态类本身必须具有文件作用域,即扩展方法必须在顶级静态类中定义,而不能在嵌套的静态的类中的定义
- 由于静态类可以取任何名字,所以C#编译器要花一定的时间来寻找扩展方法,它必须检查文件作用域中的所有静态类,并扫描他们的所有静态方法来查找一个匹配;
我可不可以这样理解,性能只是在编译时有损失,编译之后就和普通的静态方法调用一样了,没有任何区别,之所以能得出这个结论是因为通过比较如下的两种调用方式和对应的IL代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Extention { class Program { static void Main(string[] args) { var dt = DateTime.Now; var dtString = dt.DT(); var dtString1 = Extention.Extention1.DT(dt); var dtString2 = Extention.Extention1.DT1(dt); Console.ReadLine(); } } public static class Extention1 { public static string DT(this DateTime dt) { return dt.ToString(); } public static string DT1(DateTime dt) { return dt.ToString(); } } }
Main函数中对DT的三种调用生成的IL代码如下:
C#编译器是如何快速的定位扩展方法的匹配的呢?
在C#中,一旦用this关键词标记了某个静态方法的第一个参数,编译器就会在内部向该方法应用一个定制特性ExtensionAttribute;
另外,任何静态类只要包含至少一个扩展方法,它的元数据也会应用这个特性,类似的,程序集中只要包含了至少一个符合上述特点的静态类,它的元数据中也会应用这个特性,
这样一来,如果代码调用了一个不存在的实例方法,编译器就能快速的扫描引用的所有程序集,判断他们哪些包含了扩展方法,然后在这些程序集中,可以只扫描包含了扩展方法的静态类,
在每个这样的静态类中,可以只扫描扩展方法来查找匹配,利用这些技术,代码能以最快的速度编译完毕;
.class public auto ansi abstract sealed beforefieldinit Extention.Extention1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Methods .method public hidebysig static string DT ( valuetype [mscorlib]System.DateTime dt ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2088 // Code size 19 (0x13) .maxstack 1 .locals init ( [0] string CS$1$0000 ) IL_0000: nop IL_0001: ldarga.s dt IL_0003: constrained. [mscorlib]System.DateTime IL_0009: callvirt instance string [mscorlib]System.Object::ToString() IL_000e: stloc.0 IL_000f: br.s IL_0011 IL_0011: ldloc.0 IL_0012: ret } // end of method Extention1::DT .method public hidebysig static string DT1 ( valuetype [mscorlib]System.DateTime dt ) cil managed { // Method begins at RVA 0x20a8 // Code size 19 (0x13) .maxstack 1 .locals init ( [0] string CS$1$0000 ) IL_0000: nop IL_0001: ldarga.s dt IL_0003: constrained. [mscorlib]System.DateTime IL_0009: callvirt instance string [mscorlib]System.Object::ToString() IL_000e: stloc.0 IL_000f: br.s IL_0011 IL_0011: ldloc.0 IL_0012: ret } // end of method Extention1::DT1 } // end of class Extention.Extention1
- 多个静态类可以定义相同的扩展方法,如果编译器检测到存在两个或多个扩展方法,就会提示调用不明确,此时需要调用静态方法语法的方式来代替实例方法语法
- 用一个扩展方法扩展一个类型时,同时也扩展了派生类,所以不要将Object作为扩展方法的第一个参数,否则这个方法在所有表达式上都能调用
- 扩展方法存在版本控制问题(为同一个类型定义两个相同的扩展方法,后来者会把前面的覆盖掉,导致行为不一致)