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"字符串的引用。这是编译器耍的小“花招”,但效果不错,而且只要你不去细想内部发生的事情,整个过程还是感觉非常自然的。

posted @ 2021-12-31 16:35  Destiny130  阅读(35)  评论(0)    收藏  举报