CLR via C# 8.6.2 创建委托来引用一个对象上的扩展方法疑问
C# 编译器允许创建委托来引用一个对象上的扩展方法:
internal sealed class SomeType
{
public void Run()
{
// 创建一个 Action 委托(实例)来引用静态 ShowItems 扩展方法,
// 并初始化第一个实参来引用字符串 “Jeff”
Action a = "Jeff".ShowItems;
// 调用(Invoke)委托,后者调用(call) ShowItems,
// 并向它传递对字符串"Jeff"的引用
a();
Action<IEnumerable<char>> b = SS.ShowItems2;
b("abcd");
}
}
public static class SS
{
public static void ShowItems<T>(this IEnumerable<T> collection)
{
foreach (var item in collection)
Console.WriteLine(item);
}
public static void ShowItems2<T>(IEnumerable<T> collection)
{
foreach (var item in collection)
Console.WriteLine(item);
}
}
Class SomeType对应的IL代码
.class private auto ansi sealed beforefieldinit TDemo.SomeType
extends [mscorlib]System.Object
{
// Methods
.method public hidebysig
instance void Run () cil managed
{
// Method begins at RVA 0x220c
// Code size 51 (0x33)
.maxstack 2
.locals init (
[0] class [mscorlib]System.Action a,
[1] class [mscorlib]System.Action`1<class [mscorlib]System.Collections.Generic.IEnumerable`1<char>> b
)
IL_0000: nop
IL_0001: ldstr "Jeff"
IL_0006: ldftn void TDemo.SS::ShowItems<char>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_000c: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
IL_0011: stloc.0
IL_0012: ldloc.0
IL_0013: callvirt instance void [mscorlib]System.Action::Invoke()
IL_0018: nop
IL_0019: ldnull
IL_001a: ldftn void TDemo.SS::ShowItems2<char>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_0020: newobj instance void class [mscorlib]System.Action`1<class [mscorlib]System.Collections.Generic.IEnumerable`1<char>>::.ctor(object, native int)
IL_0025: stloc.1
IL_0026: ldloc.1
IL_0027: ldstr "abcd"
IL_002c: callvirt instance void class [mscorlib]System.Action`1<class [mscorlib]System.Collections.Generic.IEnumerable`1<char>>::Invoke(!0)
IL_0031: nop
IL_0032: ret
} // end of method SomeType::Run
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x20a8
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method SomeType::.ctor
} // end of class TDemo.SomeType
在上述代码中,C# 编译器生成 IL 代码来构造一个 Action
委托。创建委托时,会向构造器传递应调用的方法,同时传递一个对象引用,这个引用应传给方法的隐藏 this
参数。正常情况下,创建引用静态方法的委托时,对象引用是 null
,因为静态方法没有 this
参数。但在这个例子中,C# 编译器生成特殊代码创建一个委托来引用静态方法(ShowItems
),而静态方法的目标对象是对"Jeff"
字符串的引用。稍后,当这个委托被调用(invoke
)时,CLR会调用(call
)静态方法,并向其传递对"Jeff"
字符串的引用。这是编译器耍的小“花招”,但效果不错,而且只要你不去细想内部发生的事情,整个过程还是感觉非常自然的。