4

调用内部或私有方法的N种方法

 9 months ago
source link: https://www.cnblogs.com/artech/p/17547246.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

调用内部或私有方法的N种方法

非公开的类型或者方法被“隐藏”在程序集内部,本就不希望从外部访问,但是有时候调用一个内部或者私有方法可能是唯一的“救命稻草”,这篇文章列出了几种具体的实现方式。以如下这个Foobar类型为例,它具有一个内部属性InternalValue,我们来看看有多少种方式可以从外部获取一个Foobar对象的InternalValue属性值。

public class Foobar
{
    internal int InternalValue => 123;
}

对于大部分人来说,最先想到的自然是“反射”,具体实现体现再如下所示的InternalValueAccessor类型的GetInternalValue方法中。但是我们都知道反射是一种并不高效的方式,对于需要频繁调用,我们一般不推荐使用。

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对象的InternalValue属性值(int类型),实际上需要一个Func<Foobar,int>类型的委托。由于返回值实际上是通过InternalValue属性的Get方法获得的,而表示方法的MethodInfo类型具有一个CreateDelegate<TDelegate>方法,我们可以采用如下的方式利用InternalValue属性的Get方法来创建所需的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>>();
    }
}

三、表达式(树)

一般来说,所有的反射解决方案都可以转换成基于表达式(树)的解决方案。我们需要的Func<Foobar,int>委托可以按照如下的方式,利用构建的表达式编译生成。

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();
    }
}

四、动态方法(call)

实际上表达式(树)是对IL代码的抽象表达,所以既然这样的问题自然可以利用IL Emit来解决。在如下的代码中,我们创建了一个DynamicMethod类型表示的动态方法,以IL Emit的方式利用IL指令Call完成了针对InternalValue属性的Get方法的调用。我们所需的Func<Foobar,int>委托最终由这个DynamicMethod对象创建而成。

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>>();
    }
}

五、动态方法(calli)

了解IL的朋友应该知道,方法调用涉及的IL治理有三个(Call、Callvir和Calli)。如果使用Calli指令,在完成针对参数的压栈之后,我们还需要执行Ldftn指令将方法指针压入栈中,最终执行Calli指令完成方法的执行。

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>>();
    }
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK