1. 程式人生 > 其它 >CLR via C# 8.6.2 建立委託來引用一個物件上的擴充套件方法疑問

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"字串的引用。這是編譯器耍的小“花招”,但效果不錯,而且只要你不去細想內部發生的事情,整個過程還是感覺非常自然的。