CLR via C# 8.6.2 建立委託來引用一個物件上的擴充套件方法疑問
阿新 • • 發佈:2021-12-31
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"
字串的引用。這是編譯器耍的小“花招”,但效果不錯,而且只要你不去細想內部發生的事情,整個過程還是感覺非常自然的。