Creating delegates dynamically with parameter names

后端 未结 3 1419
Happy的楠姐
Happy的楠姐 2020-12-06 02:55

Hi I\'m trying to create a function that dynamically creates a delegate with the same return value and the same parameters as a MethodInfo it receives as parameter and also

3条回答
  •  日久生厌
    2020-12-06 03:47

    To dynamically create a delegate, you can use Reflection.Emit. Since delegates are special types in .Net, the code to create them is not quite obvious. The following is based on reflected code of methods used by Expression.Lambda(). There, it's used to create custom delegate types in situations, where there is no Action or Func delegate available (more than 17 parameters, or parameters with ref or out).

    class DelegateTypeFactory
    {
        private readonly ModuleBuilder m_module;
    
        public DelegateTypeFactory()
        {
            var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
                new AssemblyName("DelegateTypeFactory"), AssemblyBuilderAccess.RunAndCollect);
            m_module = assembly.DefineDynamicModule("DelegateTypeFactory");
        }
    
        public Type CreateDelegateType(MethodInfo method)
        {
            string nameBase = string.Format("{0}{1}", method.DeclaringType.Name, method.Name);
            string name = GetUniqueName(nameBase);
    
            var typeBuilder = m_module.DefineType(
                name, TypeAttributes.Sealed | TypeAttributes.Public, typeof(MulticastDelegate));
    
            var constructor = typeBuilder.DefineConstructor(
                MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
                CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) });
            constructor.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
    
            var parameters = method.GetParameters();
    
            var invokeMethod = typeBuilder.DefineMethod(
                "Invoke", MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Public,
                method.ReturnType, parameters.Select(p => p.ParameterType).ToArray());
            invokeMethod.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
    
            for (int i = 0; i < parameters.Length; i++)
            {
                var parameter = parameters[i];
                invokeMethod.DefineParameter(i + 1, ParameterAttributes.None, parameter.Name);
            }
    
            return typeBuilder.CreateType();
        }
    
        private string GetUniqueName(string nameBase)
        {
            int number = 2;
            string name = nameBase;
            while (m_module.GetType(name) != null)
                name = nameBase + number++;
            return name;
        }
    }
    

    If you care about performance, you might want to create a cache of some sort, so that you don't create the same delegate type over and over.

    The only modification in your code will be the line that creates lambdaExpression:

    LambdaExpression lambdaExpression = Expression.Lambda(
        s_delegateTypeFactory.CreateDelegateType(method),
        callExpression, lstParamExpressions);
    

    But you actually don't need to deal with Expressions at all. Delegate.CreateDelegate() is enough:

    private static Delegate CreateDelegate(object instance, MethodInfo method)
    {
        return Delegate.CreateDelegate(
            s_delegateTypeFactory.CreateDelegateType(method), instance, method);
    }
    

提交回复
热议问题