C# 如何创建具有参数和私有类型返回类型的快速调用委托,从而加快DynamicVoke

C# 如何创建具有参数和私有类型返回类型的快速调用委托,从而加快DynamicVoke,c#,reflection,f#,delegates,immutable-collections,C#,Reflection,F#,Delegates,Immutable Collections,我正在努力创建对私有ImmutableDictionary.Add的调用,它允许我利用KeyCollisionBehavior进行更精细的控制(只有当键和值不同时,Add方法才会抛出,我需要它总是抛出) 通过基本反射,我可以达到我想要的效果,但是,在MethodInfo上调用Invoke,或者在委托上调用DynamicInvoke的开销非常大(事实上,每次调用的时间几乎增加了三倍,这在我的场景中太大了) 我需要调用的函数的签名: ///调用属性Origin,返回一个私有变量 专用输入源{get;

我正在努力创建对私有
ImmutableDictionary.Add
的调用,它允许我利用
KeyCollisionBehavior
进行更精细的控制(只有当键和值不同时,
Add
方法才会抛出,我需要它总是抛出)

通过基本反射,我可以达到我想要的效果,但是,在
MethodInfo
上调用
Invoke
,或者在委托上调用
DynamicInvoke
的开销非常大(事实上,每次调用的时间几乎增加了三倍,这在我的场景中太大了)

我需要调用的函数的签名:

///调用属性Origin,返回一个私有变量
专用输入源{get;}
///调用ImmutableDictionary.Add,这将获取MutationInput并返回私有MutationResult
私有静态MutationResult Add(TKey-key、TValue-value、KeyCollisionBehavior、mutationput-origin);
///然后调用MutationResult.Finalize
内部ImmutableDictionary Finalize(ImmutableDictionary priorMap);
这里的挑战是我需要传递一个私有类型,并且私有类型是签名的一部分

通常,在调用
CreateDelegate
之后,您可以简单地将其强制转换为
Func
,这将提供近乎直接的调用速度。但是如果泛型类型是私有的和/或在编译时未知,我不知道如何创建
Func
。使用
object
不起作用,会在强制转换时出现运行时异常

下面是我目前拥有的代码的一个简短版本(删除了很多try/catch和check)。这项工作:

///来自ImmutableDictionary的Github源的枚举类型副本
类型KeyCollisionBehavior=
///设置给定键的值,即使该值覆盖了现有值。
|SetValue=0
///如果检测到密钥冲突,则跳过变异操作。
|跳过=1
///如果键已存在且具有其他值,则引发异常。
|ThrowIfValueDifferent=2
///如果键已存在,则抛出异常,而不管其值如何。
|ThrowAlways=3
///添加类型安全性的简单包装DU
类型转换PutWrapper=
///将类型ImmutableDictionary.MutationInput包装为内部Add#4方法中的第四个参数
|obj突变
///添加类型安全性的简单包装器
类型mutationResultRapper=
///包装类型ImmutableDictionary.MutationResult,它是内部Add#4操作的结果
|obj突变结果
///类型缩写
类型BclImmDict=System.Collections.Immutable.ImmutableDictionary
///ImmutableDictionary的私有扩展
类型ImmutableDictionary()=
静态let dicType=typeof
static let addMethod=dicType.GetMethod(“Add”,BindingFlags.NonPublic | | | BindingFlags.static)
静态let addMethodDelegate=
让parameters=addMethod.GetParameters()|>Array.map(fun p->p.ParameterType)
设funType=
typedefof.MakeGenericType[|
参数[0]
参数[1]
参数[2]
参数[3]
addMethod.ReturnType
|]
CreateDelegate(funType,addMethod)//在这里,人们通常会强制转换为Func
静态let mutationResultFinalizeMethod=
如果不是(isNull addMethod)&¬(isNull(addMethod.ReturnParameter)),那么
///嵌套私有类型MutationResult,为简单起见,取自ImmutableDictionary.Add#4的返回参数类型
让mutationResultType=addMethod.ReturnParameter.ParameterType
如果不是(isNull mutationResultType),则
mutationResultType.GetMethod(“Finalize”,BindingFlags.NonPublic | | | | | | BindingFlags.Instance | | | BindingFlags.DeclaredOnly)
其他的
无效的
其他的
无效的
///System.Collections.Immutable.ImmutableDictionary.get\u Origin//的值类型ImmutableDictionary.MutationInput
静态let getOrigin=dicType.GetProperty(“Origin”,BindingFlags.NonPublic | | | BindingFlags.Instance)
///通过反射调用私有成员ImmutableDictionary.Add(key、value、behavior、origin)
静态成员私有refl\u Add(key:'key,value:'value,行为:KeyCollisionBehavior,mutationput-origin)=
//使用Invoke或DynamicInvoke没有什么区别。
//Invoke(null,[|键;值;行为;原点|])
addMethodDelegate.DynamicInvoke([|框键;框值;框突变结果
///通过调用私有属性get\u origin获取不可变字典的“origin”
静态成员私有refl_GetOrigin(this:BclImmDict)=
getOrigin.GetValue此
|>突变输入
///通过获取(内部)MutationResult并返回一个新的非mutational字典来完成结果
静态成员私有refl_Finalize(MutationResult MutationResult,map:BclImmDict)=
mutationResultFinalizeMethod.Invoke(mutationResult,[| map |])
:?>BclImmDict
///实际添加,通过CollisionBehavior添加控制
静态成员InternalAddAndFinalize(键:'键,值:'值,行为,此映射)=
让origin=ImmutableDictionary.refl\u GetOrigin(thisMap)
让mutationResult=ImmutableDictionary.refl_添加(键、值、行为、原点)
让finalizedMap=ImmutableDictionary.refl\u Finalize(mutationResult,thisMap)
定稿地图

我知道上面的代码是用F#编写的,但是如果你知道如何用C来解决这个问题,我可以将你的答案翻译成我喜欢的目标语言。

我认为你把它复杂化了。来源:

如果字典中已经存在给定的键/值对,则返回字典的现有实例

因此,我们可以判断给定的键/值是否已存在于
public static ImmutableDictionary<TKey, TValue> AddAndThrowIfAlreadyPresent<TKey, TValue>(
    ImmutableDictionary<TKey, TValue> dict,
    TKey key,
    TValue value)
{
    // null checks, etc
    var newDict = dict.Add(key, value);
    if (newDict == dict)
        throw new ArgumentException($"An element with the same key and value already exists. Key: {key}");
    return newDict;
}
enum KeyCollisionBehavior
{
    SetValue = 0,
    Skip = 1,
    ThrowIfValueDifferent = 2,
    ThrowAlways = 3,
}

internal static class ImmutableDictionaryHelper<TKey, TValue>
{
    private static readonly MethodInfo OriginPropertyGetter = typeof(ImmutableDictionary<TKey, TValue>)
        .GetProperty("Origin", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true);

    private static readonly MethodInfo AddMethod = typeof(ImmutableDictionary<TKey, TValue>)
        .GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.Name == "Add" && m.GetParameters().Length == 4).FirstOrDefault();

    private static readonly Type MutationResultType = AddMethod.ReturnType;

    private static readonly MethodInfo FinalizeMethod = MutationResultType
        .GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);

    private static readonly Func<TKey, TValue, KeyCollisionBehavior, ImmutableDictionary<TKey, TValue>, ImmutableDictionary<TKey, TValue>> AddAndFinalize = CreateAddAndFinalize();

    private static Func<TKey, TValue, KeyCollisionBehavior, ImmutableDictionary<TKey, TValue>, ImmutableDictionary<TKey, TValue>> CreateAddAndFinalize()
    {
        var method = new DynamicMethod(
            nameof(AddAndFinalize),
            typeof(ImmutableDictionary<TKey, TValue>),
            new[] { typeof(TKey), typeof(TValue), typeof(KeyCollisionBehavior), typeof(ImmutableDictionary<TKey, TValue>) },
            typeof(ImmutableDictionary<TKey, TValue>));

        var ilGen = method.GetILGenerator();
        ilGen.DeclareLocal(OriginPropertyGetter.ReturnType);
        ilGen.DeclareLocal(AddMethod.ReturnType);

        // var origin = dictionary.Origin;
        ilGen.Emit(OpCodes.Ldarg_3);
        ilGen.Emit(OpCodes.Callvirt, OriginPropertyGetter);
        ilGen.Emit(OpCodes.Stloc_0);

        // var result = Add(key, value, behavior, origin)
        ilGen.Emit(OpCodes.Ldarg_0);
        ilGen.Emit(OpCodes.Ldarg_1);
        ilGen.Emit(OpCodes.Ldarg_2);
        ilGen.Emit(OpCodes.Ldloc_0);
        ilGen.Emit(OpCodes.Call, AddMethod);
        ilGen.Emit(OpCodes.Stloc_1);

        // var newDictionary = result.Finalize(dictionary);
        ilGen.Emit(OpCodes.Ldloca_S, 1);
        ilGen.Emit(OpCodes.Ldarg_3);
        ilGen.Emit(OpCodes.Call, FinalizeMethod);

        // return newDictionary;
        ilGen.Emit(OpCodes.Ret);

        var del = method.CreateDelegate(typeof(Func<TKey, TValue, KeyCollisionBehavior, ImmutableDictionary<TKey, TValue>, ImmutableDictionary<TKey, TValue>>));
        var func = (Func<TKey, TValue, KeyCollisionBehavior, ImmutableDictionary<TKey, TValue>, ImmutableDictionary<TKey, TValue>>)del;
        return func;
    }

    public static ImmutableDictionary<TKey, TValue> Add(ImmutableDictionary<TKey, TValue> source, TKey key, TValue value, KeyCollisionBehavior behavior)
    {
        if (source == null)
            throw new ArgumentNullException(nameof(source));

        return AddAndFinalize(key, value, behavior, source);
    }
}
enum KeyCollisionBehavior
{
    SetValue = 0,
    Skip = 1,
    ThrowIfValueDifferent = 2,
    ThrowAlways = 3,
}

internal static class ImmutableDictionaryHelper<TKey, TValue>
{
    private static readonly Func<TKey, TValue, KeyCollisionBehavior, ImmutableDictionary<TKey, TValue>, ImmutableDictionary<TKey, TValue>> AddAndFinalize = CreateAddAndFinalize();

    private static Func<TKey, TValue, KeyCollisionBehavior, ImmutableDictionary<TKey, TValue>, ImmutableDictionary<TKey, TValue>> CreateAddAndFinalize()
    {
        var originPropertyGetter = typeof(ImmutableDictionary<TKey, TValue>)
            .GetProperty("Origin", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true);
        var addMethod = typeof(ImmutableDictionary<TKey, TValue>)
            .GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.Name == "Add" && m.GetParameters().Length == 4).FirstOrDefault();
        var finalizeMethod = addMethod.ReturnType
            .GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);

        var key = Expression.Parameter(typeof(TKey), "key");
        var value = Expression.Parameter(typeof(TValue), "value");
        var behavior = Expression.Parameter(typeof(KeyCollisionBehavior), "behavior");
        var dictionary = Expression.Parameter(typeof(ImmutableDictionary<TKey, TValue>), "dictionary");

        // var convertedBehavior = (ImmutableDictionary<TKey, TValue>.KeyCollisionBehavior)behavior
        var convertedBehavior = Expression.Convert(behavior, addMethod.GetParameters()[2].ParameterType);
        // var origin = dictionary.Origin;
        var origin = Expression.Property(dictionary, originPropertyGetter);
        // var result = Add(key, value, behavior, origin)
        var result = Expression.Call(addMethod, key, value, convertedBehavior, origin);
        // var newDictionary = result.Finalize(dictionary);
        var newDictionary = Expression.Call(result, finalizeMethod, dictionary);

        var func = Expression.Lambda<Func<TKey, TValue, KeyCollisionBehavior, ImmutableDictionary<TKey, TValue>, ImmutableDictionary<TKey, TValue>>>(
            newDictionary, key, value, behavior, dictionary).Compile();
        return func;
    }

    public static ImmutableDictionary<TKey, TValue> Add(ImmutableDictionary<TKey, TValue> source, TKey key, TValue value, KeyCollisionBehavior behavior)
    {
        if (source == null)
            throw new ArgumentNullException(nameof(source));

        return AddAndFinalize(key, value, behavior, source);
    }
}