Dynamically creating delegates with parameter names

Hey. I am trying to create a function that dynamically creates a delegate with the same return value and the same parameters as MethodInfo, which it receives as a parameter as well, and this is very important for the same parameter names!

What I have done so far is to create a function that returns a lambda that receives the same parameter types and has the same return value as MethodInfo, but does not have parameter names:

static void Example() { Person adam = new Person(); MethodInfo method = typeof(Person).GetMethod("Jump"); Delegate result = CreateDelegate(adam, method); result.DynamicInvoke((uint)4, "Yeahaa"); } private static Delegate CreateDelegate(object instance, MethodInfo method) { var parametersInfo = method.GetParameters(); Expression[] expArgs = new Expression[parametersInfo.Length]; List<ParameterExpression> lstParamExpressions = new List<ParameterExpression>(); for (int i = 0; i < expArgs.Length; i++) { expArgs[i] = Expression.Parameter(parametersInfo[i].ParameterType, parametersInfo[i].Name); lstParamExpressions.Add((ParameterExpression)expArgs[i]); } MethodCallExpression callExpression = Expression.Call(Expression.Constant(instance), method, expArgs); LambdaExpression lambdaExpression = Expression.Lambda(callExpression, lstParamExpressions); return lambdaExpression.Compile(); } private class Person { public void Jump(uint height, string cheer) { Console.WriteLine("Person jumped " + height + " "+ cheer); } } 

Does anyone have any suggestions on how I can do this? To be clear, the reason I care about parameter names is that I can activate a delegate with parameter names, so I could call it that (cheer = "YAY! ', Height = 3) (My application is integrated with Python, since I can do this without DynamicInvoke, and this is also the reason why parameter names are so important and why I wrote '=' and not ':')

+6
source share
3 answers

To dynamically create a delegate, you can use Reflection.Emit. Because delegates are special types in .Net, the code to create it is not entirely obvious. The following is the reflected code for the methods used by Expression.Lambda() . There, it was used to create custom delegate types in situations where there are no Action or Func delegates available (more than 17 parameters or parameters using 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 file of a certain type so as not to create the same delegate type again and again.

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 don’t have to deal with Expression . Delegate.CreateDelegate() enough:

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

The open source framework ImpromptuInterface (v5.6.7 via nuget) has a DLR currying / partial that uses an implementation that I think will work in this case if you don't need a literal delegate.

Here is the C # version for creating and invoking it:

 dynamic jump =Impromptu.Curry(adam).Jump(); jump(cheer:"yay", height:(uint)3); 

So, jump not a literal delegate, you cannot reflect it, but you can call it directly as if it were a delegate, and this is a DLR object, so I assume that it will work exactly the same in python.

0
source

I just came across a good way to solve this problem, for delegates it looks like a static method:

 private static Delegate CreateDelegate(MethodInfo method) { var paramTypes = method.GetParameters().Select(p => p.ParameterType); Type delegateType = Expression.GetDelegateType(paramTypes.Append(method.ReturnType).ToArray()); return Delegate.CreateDelegate(delegateType, method, true); } 

It uses this extension method:

 public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> collection, TSource element) { if (collection == null) throw new ArgumentNullException("collection"); foreach (TSource element1 in collection) yield return element1; yield return element; } 
0
source

Source: https://habr.com/ru/post/909669/


All Articles