調(diào)用私有方法的N種方法
非公開(kāi)的類型或者方法被“隱藏”在程序集內(nèi)部,本就不希望從外部訪問(wèn),但是有時(shí)候調(diào)用一個(gè)內(nèi)部或者私有方法可能是唯一的“救命稻草”,這篇文章列出了幾種具體的實(shí)現(xiàn)方式。以如下這個(gè)Foobar類型為例,它具有一個(gè)內(nèi)部屬性InternalValue,我們來(lái)看看有多少種方式可以從外部獲取一個(gè)Foobar對(duì)象的InternalValue屬性值。
public class Foobar { ? ?internal int InternalValue => 123; }
一、反射
對(duì)于大部分人來(lái)說(shuō),最先想到的自然是“反射”,具體實(shí)現(xiàn)體現(xiàn)再如下所示的InternalValueAccessor類型的GetInternalValue方法中。但是我們都知道反射是一種并不高效的方式,對(duì)于需要頻繁調(diào)用,我們一般不推薦使用。
var foobar = new Foobar(); Debug.Assert(InternalValueAccessor.GetInternalValue(foobar) == 123);public static class InternalValueAccessor { ? ?public static int GetInternalValue(Foobar foobar) ? ?{ ? ? ? ?var propertyInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!; ? ? ? ?return (int)propertyInfo.GetValue(foobar)!; ? ?} }
二、MethodInfo.CreateDelegate方法
要獲得Foobar對(duì)象的InternalValue屬性值(int類型),實(shí)際上需要一個(gè)Func<Foobar,int>類型的委托。由于返回值實(shí)際上是通過(guò)InternalValue屬性的Get方法獲得的,而表示方法的MethodInfo類型具有一個(gè)CreateDelegate<TDelegate>方法,我們可以采用如下的方式利用InternalValue屬性的Get方法來(lái)創(chuàng)建所需的Func<Foobar,int>委托。
var foobar = new Foobar(); Debug.Assert(InternalValueAccessor.GetInternalValue(foobar) == 123);public static class InternalValueAccessor { ? ?private static Func<Foobar, int>? _getInternalValue; ? ?public static int GetInternalValue(Foobar foobar)=> (_getInternalValue??= CreateDelegate())(foobar); ? ?private static Func<Foobar, int> CreateDelegate() ? ?{ ? ? ? ?var methodInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!.GetMethod!; ? ? ? ?return methodInfo.CreateDelegate<Func<Foobar, int>>(); ? ?} }
三、表達(dá)式(樹(shù))
一般來(lái)說(shuō),所有的反射解決方案都可以轉(zhuǎn)換成基于表達(dá)式(樹(shù))的解決方案。我們需要的Func<Foobar,int>委托可以按照如下的方式,利用構(gòu)建的表達(dá)式編譯生成。
public static class InternalValueAccessor { ? ?private static Func<Foobar, int>? _getInternalValue; ? ?public static int GetInternalValue(Foobar foobar)=> (_getInternalValue??= CreateDelegate())(foobar); ? ?private static Func<Foobar, int> CreateDelegate() ? ?{ ? ? ? ?var methodInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!.GetMethod!; ? ? ? ?var foobar = Expression.Parameter(typeof(Foobar), "foobar"); ? ? ? ?var getValue = Expression.Call(foobar, methodInfo); ? ? ? ?return Expression.Lambda<Func<Foobar, int>>(getValue, foobar).Compile(); ? ?} }
四、動(dòng)態(tài)方法(call)
實(shí)際上表達(dá)式(樹(shù))是對(duì)IL代碼的抽象表達(dá),所以既然這樣的問(wèn)題自然可以利用IL Emit來(lái)解決。在如下的代碼中,我們創(chuàng)建了一個(gè)DynamicMethod類型表示的動(dòng)態(tài)方法,以IL Emit的方式利用IL指令Call完成了針對(duì)InternalValue屬性的Get方法的調(diào)用。我們所需的Func<Foobar,int>委托最終由這個(gè)DynamicMethod對(duì)象創(chuàng)建而成。
public static class InternalValueAccessor { ? ?private static Func<Foobar, int>? _getInternalValue; ? ?public static int GetInternalValue(Foobar foobar) => (_getInternalValue ??= CreateDelegate())(foobar); ? ?private static Func<Foobar, int> CreateDelegate() ? ?{ ? ? ? ?var methodInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!.GetMethod!; ? ? ? ?var method = new DynamicMethod("GetInternalValue", typeof(int), new Type[] { typeof(Foobar) }); ? ? ? ?var il = method.GetILGenerator(); ? ? ? ?il.Emit(OpCodes.Ldarg_0); ? ? ? ?il.EmitCall(OpCodes.Call, methodInfo, null); ? ? ? ?il.Emit(OpCodes.Ret); ? ? ? ?return method.CreateDelegate<Func<Foobar, int>>(); ? ?} }
五、動(dòng)態(tài)方法(calli)
了解IL的朋友應(yīng)該知道,方法調(diào)用涉及的IL治理有三個(gè)(Call、Callvir和Calli)。如果使用Calli指令,在完成針對(duì)參數(shù)的壓棧之后,我們還需要執(zhí)行Ldftn指令將方法指針壓入棧中,最終執(zhí)行Calli指令完成方法的執(zhí)行。
public static class InternalValueAccessor { ? ?private static Func<Foobar, int>? _getInternalValue; ? ?public static int GetInternalValue(Foobar foobar) => (_getInternalValue ??= CreateDelegate())(foobar); ? ?private static Func<Foobar, int> CreateDelegate() ? ?{ ? ? ? ?var methodInfo = typeof(Foobar).GetProperty("InternalValue", BindingFlags.Instance | BindingFlags.NonPublic)!.GetMethod!; ? ? ? ?var method = new DynamicMethod("GetInternalValue", typeof(int), new Type[] { typeof(Foobar) }); ? ? ? ?var il = method.GetILGenerator(); ? ? ? ?il.Emit(OpCodes.Ldarg_0); ? ? ? ?il.Emit(OpCodes.Ldftn, methodInfo); ? ? ? ?il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, typeof(int), new Type[] { typeof(Foobar) }, null); ? ? ? ?il.Emit(OpCodes.Ret); ? ? ? ?return method.CreateDelegate<Func<Foobar, int>>(); ? ?} }
分類:?[02] 編程技巧