如何在运行时定义委托类型(即动态委托类型)

3

要在运行时创建委托,技术方法包括Delegate.CreateDelegate、Expression Lambda、DynamicMethod等。所有这些技术都需要您知道委托的类型。

我正在尝试通用地将闭合委托转换为开放委托,并且为了实现这一点,似乎我需要在实际创建结果委托之前动态创建开放委托的类型。考虑以下内容:

pubic class WeakEvent<TDelegate> where TDelegate : class
{
     public WeakEvent(Delegate aDelegate)
     {
         var dgt = aDelegate as TDelegate;

         if(dgt == null)
             throw new ArgumentException("aDelegate");

         MethodInfo method = dgt.Method;
         var parameters = Enumerable
                         .Repeat(dgt.Target.GetType(),1)
                         .Concat(method.GetParameters().Select(p => p.ParameterType));

         Type openDelegateType = // ???  original delegate, with new 1st arg for @this

         var dm = new DynamicMethod("InnerCode", method.ReturnType, parameters);

         ... your favourite IL code emmisions go here

         var openDelegate = dm.CreateDelegate(openDelegateType);
     }
}

上述代码的目的是创建一个新的委托,该委托与原始委托相同,但为this参数创建了一个新的第一个参数...即之前封闭委托的开放版本。
是否有一种简单的方法可以克隆和修改现有的委托类型,或者最接近的解决方案是构建通用的Func<>和Action<>类型?
3个回答

1

经过一些实验,我发现以下代码是实现我所希望的功能的最佳方式:

    private Type CreateOpenDelegate()
    {
        var parms = _method.GetParameters();
        bool hasReturn = _method.ReturnType != typeof (void);
        Type generic = GetGenericTypeForOpenDelegate(parms, hasReturn);

        var argTypes = new List<Type>(parms.Length + 2) {_method.DeclaringType};

        foreach (var arg in parms)
        {
            if(arg.IsOut || arg.IsRetval)
                throw new NotImplementedException();

            argTypes.Add(arg.ParameterType);
        }

        if(hasReturn)
            argTypes.Add(_method.ReturnType);

        var result = generic.MakeGenericType(argTypes.ToArray());

        return result;
    }

    private static Type GetGenericTypeForOpenDelegate(ParameterInfo[] parms, bool hasReturn)
    {
        if (hasReturn)
        {
            switch (parms.Length)
            {
                case 0:
                    return typeof (Func<,>);
                    break;
                case 1:
                    return typeof (Func<,,>);
                    break;
                case 2:
                    return typeof (Func<,,,>);
                    break;
                case 3:
                    return typeof (Func<,,,,>);
                    break;
            }
        }
        else
        {
            switch (parms.Length)
            {
                case 0:
                    return typeof (Action<>);
                    break;
                case 1:
                    return typeof (Action<,>);
                    break;
                case 2:
                    return typeof (Action<,,>);
                    break;
                case 3:
                    return typeof (Action<,,,>);
                    break;
            }
        }
        throw new NotImplementedException();
    }

这真是遗憾,因为据我所知,这意味着您无法使用泛型 Func 和 Action 动态地 [重新] 创建带有 ref 或 out 参数的委托。

1

具有不同签名的新委托是一种新类型。由于C#是类型安全的,除了编写一些代码并在运行时编译之外(除了内存泄漏之外),不可能做到这一点,这并不是一种优雅的方法。

但是,您可以从已经创建的Action<>或Func<>列表中选择适当的委托,基于委托的类型。或者,根据您预期的类型创建自己的列表,并在运行时选择该列表。


0

@Mark - 在你的第二个代码示例之后,你说:

这很遗憾,因为这意味着(据我所知),你不能使用 ref 或 out 参数动态地重新创建委托,因为泛型 Func 和 Action 不允许它。

这正是我的问题,可以查看 在运行时创建带有 ref 参数的 C# 委托类型,由用户 Ani 提供的解决方案:Expression.GetDelegateType 允许使用 ref 参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接