2012-02-29 2 views
6

안녕하세요 저는 동일한 반환 값과 매개 변수로받는 MethodInfo와 동일한 매개 변수를 사용하여 대리인을 동적으로 만드는 함수를 만들려고합니다.이 매개 변수 이름은 매우 중요합니다 ! 내가 지금까지 무슨 짓을매개 변수 이름을 사용하여 대리자를 동적으로 만들기

같은 매개 변수 유형을 수신하고 MethodInfo과 같은 반환 값이 람다를 반환하는 함수를 만드는 것입니다하지만 매개 변수 이름을 가지고 있지 않습니다

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); 
     } 
    } 

합니까 누구든지 내가 그걸 어떻게 할 수 있는지 어떤 제안이 있니? 매개 변수 이름에 관심이있는 이유는 매개 변수 이름을 가진 대리인을 활성화 할 수 있도록하기 위해서입니다. (cheer = "YAY!", height = 3) 내 응용 프로그램은 내가 DynamicInvoke없이 그것을 할 수 있습니다 방법은 파이썬과 통합이 매개 변수 이름은 중요한 이유도 이유도 나는 '='작성하지 왜 ':')

답변

6

대리자를 동적으로 만들려면 Reflection.Emit을 사용할 수 있습니다. 대리자는 .Net의 특수한 형식이므로이 코드를 만드는 코드는 분명하지 않습니다. 다음은 Expression.Lambda()에서 사용되는 반사 된 코드를 기반으로합니다. 상황에 따라 사용자 지정 대리자 형식을 만들 수 있습니다. Action 또는 Func 대리자를 사용할 수 없습니다 (mo 17 개 이상의 매개 변수 또는 ref 또는 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; 
    } 
} 

, 당신은 같은 대리인이 반복해서 입력 만들지 않도록, 어떤 종류의 캐시를 만들 수 있습니다.

코드에서 유일한 수정 lambdaExpression을 만들어 줄 것입니다 :

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

하지만 실제로는 전혀 Expression의 처리 할 필요는 없습니다. Delegate.CreateDelegate()는 충분하다 :

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

는 I 감사 :이 확장 방법을 사용

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); } 

오늘 일찍 시험해 보니 좋았어요 !! 대부분의 코드를 이해하지만 실제로 이름과 이름을 사용하여 수행중인 작업을 파악할 수 없습니다.베이스, 형식베이스 이름 + 유형 이름과 같은 이유는 무엇입니까? 숫자는 무엇입니까? 어쨌든 고맙습니다. 일간의 노력에 감사드립니다. –

+0

@UchihaMadara, 동일한 어셈블리에서 같은 이름을 가진 두 가지 유형을 가질 수 없기 때문에 형식 이름이 고유한지 확인하는 것입니다. 그리고 숫자 2는'PersonJump','PersonJump2','PersonJump3' 등과 같은 이름입니다. – svick

+0

정말 똑똑합니다. 고마워요! =] –

0

오픈 소스 프레임 워크 ImpromptuInterface (nuget를 통해 v5.6.7)는 currying/partial 내가만큼 당신이 문자 그대로의 위임이 필요하지 않습니다이 경우에 작동 것이라고 생각 구현을 적용됩니다 DLR 있습니다.

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

그래서 jump 당신은 그것을 반영 할 수 있지만,이 대리인 인 것처럼 당신이 직접 호출 할 수 있고, 문자 그대로의 위임되지 않습니다 : 여기에

은 그것을 만들고 호출의 C# 버전입니다 그것은 DLR 객체이기 때문에 제 생각에 그것은 파이썬에서 똑같이 작동 할 것입니다.

난 그냥이 문제를 해결하기 위해 좋은 방법을 우연히 발견 한
0

, 그것은 정적 메서드 명을 수용 할 수있는 다음과 같습니다 :

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; 
} 
관련 문제