从MethodInfo获取具有私有(不可访问)类型的Func <>

时间:2018-10-04 01:23:53

标签: c# .net reflection delegates func

考虑以下代码:

private class ThirdPartyClass {
    private class InternalPrivateClass { }
    private static InternalPrivateClass Init() { 
        return new InternalPrivateClass(); 
    }
    private static int DoSomething(InternalPrivateClass t1) { 
        return 0; 
    }
}

假设我无法控制ThirdPartyClass,并且以任何方式对其进行反向工程都是成本过高的。我希望能够快速调用DoSomething,而不会产生反射的性能开销。所以我到目前为止:

Type t = typeof(ThirdPartyClass);
object context = t.GetMethod("Init", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
MethodInfo mi = t.GetMethod("DoSomething", BindingFlags.NonPublic | BindingFlags.Static);
// ...now what?
  • 调用mi.Invoke(null, new object[]{context})当然很慢,因为它正在使用反射。
  • Delegate.CreateDelegate(typeof(Func<object, int>), mi); 失败,因为Func签名必须完全匹配且(object, int)与MethodInfo的签名(ThirdPartyClass.InternalPrivateClass, int)不匹配
  • 通过反射构造正确类型的委托(请参阅https://stackoverflow.com/a/40579063/2692950)仅使我致电.DynamicInvoke(context),这仍然很慢。我不能将此委托转换为Func以便能够直接调用它,因为同样,签名不匹配。
  • 我无法编写Func<ThirdPartyClass.InternalPrivateClass, int>-由于InternalPrivateClass是私有的,因此无法编译。

已解决! (https://stackoverflow.com/a/52652398/2692950

为何需要此示例:

看看此MD4哈希实现:https://stackoverflow.com/a/46821287/2692950 (简化版:https://stackoverflow.com/a/52640221/2692950

这很好用,只是每个哈希操作都通过反射调用方法!

在此示例中,我们通过反射调用了一个不可访问的私有函数System.Security.Cryptography.Utils.HashEnd(SafeProvHandle h),并传递了一个SafeHandle作为参数。之所以有效,是因为SafeProvHandle继承自SafeHandleSafeProvHandle不能直接引用,因为它是私有的,因此似乎没有办法直接调用此函数。

(我最关心的是问题的开头是否存在针对一般情况的解决方案,但是是否有人知道有更好的方法来直接通过ALG_ID获取加密服务提供商,我都很耳朵:)

2 个答案:

答案 0 :(得分:4)

这有点棘手,但是可以使用System.Reflection.Emit命名空间中的DynamicMethod来完成。它允许我们在运行时发出调用这些方法的IL,而不必在我们的代码中引用有效的可见标识符。此类可以使用的技巧之一就是跳过我们通过构造函数中的参数设置的各种安全性和可见性检查。从示例中,我们需要替换Utils类。这是使用DynamicMethod创建委托的重写:

internal static class DelegateUtils
{
    private static readonly Type UtilsType = Type.GetType("System.Security.Cryptography.Utils");
    private static readonly Func<int, SafeHandle> CreateHashDel;
    private static readonly Action<SafeHandle, byte[], int, int> HashDataDel;
    private static readonly Func<SafeHandle, byte[]> EndHashDel;

    static DelegateUtils()
    {
        CreateHashDel = CreateCreateHashDelegate();
        HashDataDel = CreateHashDataDelegate();
        EndHashDel = CreateEndHashDelegate();
    }

    internal static SafeHandle CreateHash(int algid)
    {
        return CreateHashDel(algid);
    }

    internal static void HashData(SafeHandle h, byte[] data, int ibStart, int cbSize)
    {
        HashDataDel(h, data, ibStart, cbSize);
    }

    internal static byte[] EndHash(SafeHandle h)
    {
        return EndHashDel(h);
    }

    private static Func<int, SafeHandle> CreateCreateHashDelegate()
    {
        var prop = UtilsType.GetProperty("StaticProvHandle", BindingFlags.NonPublic | BindingFlags.Static);

        var createHashMethod = UtilsType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
            .FirstOrDefault(mi => mi.Name == "CreateHash" && mi.GetParameters().Length == 2);

        var createHashDyn = new DynamicMethod("CreateHashDyn", typeof(SafeHandle), new[] { typeof(int) }, typeof(object), true);
        var ilGen = createHashDyn.GetILGenerator();
        ilGen.Emit(OpCodes.Call, prop.GetGetMethod(true));
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Call, createHashMethod);
        ilGen.Emit(OpCodes.Ret);

        var del = (Func<int, SafeHandle>)createHashDyn.CreateDelegate(typeof(Func<int, SafeHandle>));
        return del;
    }

    private static Action<SafeHandle, byte[], int, int> CreateHashDataDelegate()
    {
        var hashDataMethod = UtilsType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
            .FirstOrDefault(mi => mi.Name == "HashData" && mi.GetParameters().Length == 4);
        var hashDataDyn = new DynamicMethod("HashDataDyn", typeof(void), new[] { typeof(SafeHandle), typeof(byte[]), typeof(int), typeof(int) }, typeof(object), true);
        var ilGen = hashDataDyn.GetILGenerator();
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Ldarg_1);
        ilGen.Emit(OpCodes.Ldarg_2);
        ilGen.Emit(OpCodes.Ldarg_3);
        ilGen.Emit(OpCodes.Call, hashDataMethod);
        ilGen.Emit(OpCodes.Ret);

        var del = (Action<SafeHandle, byte[], int, int>)hashDataDyn.CreateDelegate(typeof(Action<SafeHandle, byte[], int, int>));
        return del;
    }

    private static Func<SafeHandle, byte[]> CreateEndHashDelegate()
    {
        var endHashMethod = UtilsType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
            .FirstOrDefault(mi => mi.Name == "EndHash" && mi.GetParameters().Length == 1);
        var endHashDyn = new DynamicMethod("EndHashDyn", typeof(byte[]), new[] { typeof(SafeHandle) }, typeof(object), true);
        var ilGen = endHashDyn.GetILGenerator();
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Call, endHashMethod);
        ilGen.Emit(OpCodes.Ret);

        var del = (Func<SafeHandle, byte[]>)endHashDyn.CreateDelegate(typeof(Func<SafeHandle, byte[]>));
        return del;
    }
}

接下来的问题是,这带来了多少速度优势。根据您要散列的数据大小,它可以使您增加2-4倍。较小的设备可以获得更好的速度提升,这可能是因为我们在该处花费的时间更少,而在方法调用之间花费的时间更多。以下是快速基准测试的结果:

  

BenchmarkDotNet = v0.11.1,操作系统= Windows 10.0.17134.286(1803 / April2018Update / Redstone4)
  Intel Core i5-4200U CPU 1.60GHz(Haswell),1个CPU,4个逻辑和2个物理内核
  频率= 2240904 Hz,分辨率= 446.2485 ns,计时器= TSC
    [主机]:.NET Framework 4.7.2(CLR 4.0.30319.42000),32位LegacyJIT-v4.7.3163.0
    DefaultJob:.NET Framework 4.7.2(CLR 4.0.30319.42000),32位LegacyJIT-v4.7.3163.0

     

方法| N |均值|错误标准差|
  ----------- | ------ | ----------:| ----------:| ------- ---:|
  反思| 1000 | 16.239我们| 0.1252我们| 0.1046我们|
    代表| 1000 | 4.329我们| 0.0245美元| 0.0230我们|
  反思| 10000 | 31.832我们| 0.1599美元| 0.1335我们|
    代表| 10000 | 19.703我们| 0.1005我们| 0.0940我们|

请注意,N是要散列的字节数。这是使用OP链接中提供的所有代码来创建MD4实现,然后在其上调用ComputeHash。

基准代码:

public class MD4DelegateVsReflection
{
    private MD4 md4 = MD4.Create();
    private byte[] data;

    [Params(1000, 10000)]
    public int N;

    public void SetupData()
    {
        data = new byte[N];
        new Random(42).NextBytes(data);
    }

    [GlobalSetup(Target = nameof(Reflection))]
    public void ReflectionSetup()
    {
        MD4.SetReflectionUtils();
        SetupData();
    }

    [GlobalSetup(Target = nameof(Delegate))]
    public void DelegateSetup()
    {
        MD4.SetDelegateUtils();
        SetupData();
    }

    [Benchmark]
    public byte[] Reflection() => md4.ComputeHash(data);

    [Benchmark]
    public byte[] Delegate() => md4.ComputeHash(data);
}

答案 1 :(得分:1)

这是从具有不可访问类型的MethodInfo创建Func <>或Action <>的通用解决方案:

public static Delegate CreateDelegate(this MethodInfo methodInfo, object target, params Type[] custTypes) {
    Func<Type[], Type> getType;
    bool isAction = methodInfo.ReturnType.Equals((typeof(void))), cust = custTypes.Length > 0;
    Type[] types = cust ? custTypes : methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
    if (isAction) getType = Expression.GetActionType;
    else {
        getType = Expression.GetFuncType;
        if (!cust) types = types.Concat(new[] { methodInfo.ReturnType }).ToArray();
    }
    if (cust) {
        int i, nargs = types.Length - (isAction ? 0 : 1);
        var dm = new DynamicMethod(methodInfo.Name, isAction ? typeof(void) : types.Last(), types.Take(nargs).ToArray(), typeof(object), true);
        var il = dm.GetILGenerator();
        for (i = 0; i < nargs; i++)
            il.Emit(OpCodes.Ldarg_S, i);
        il.Emit(OpCodes.Call, methodInfo);
        il.Emit(OpCodes.Ret);
        if (methodInfo.IsStatic) return dm.CreateDelegate(getType(types));
        return dm.CreateDelegate(getType(types), target);
    }
    if (methodInfo.IsStatic) return Delegate.CreateDelegate(getType(types), methodInfo);
    return Delegate.CreateDelegate(getType(types), target, methodInfo.Name);
}

在OP中,可以按以下方式调用此函数以获取可直接调用的Func <>:

Func<object, int> f = (Func<object, int>)mi.CreateDelegate(null, typeof(object), typeof(int));
f(context);

感谢@Sagi(https://stackoverflow.com/a/40579063/2692950)和@ mike.z(https://stackoverflow.com/a/52641599/2692950),将我引向这个解决方案