2012-02-29 23 views
6

parametre adlarıyla oluşturma Dinamik olarak aynı döndürme değerine ve parametre olarak aldığı bir Yöntem Bilgisi ile aynı parametrelere sahip bir temsilci dinamik olarak yaratan bir işlev oluşturmaya çalışıyorum ve bu aynı parametre adları için çok önemlidir. ! Şu ana kadar ne yaptıkDelegeleri dinamik olarak

aynı parametre türlerini alır ve MethodInfo aynı değer döndüren bir lambda döndüren bir işlev oluşturmak ama parametre adları yok:

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

mu Bunu nasıl yapabileceğimi öneren var mı? Açıklığa kavuşturmak için, parametre isimlerini önemsememin sebebi, parametre isimleriyle delegeyi aktif hale getirebilmem, bu şekilde onu çağırabilirim (cheer = "YAY!", Yükseklik = 3) (Uygulamam Python ile entegre edilmiştir ve DynamicInvoke olmadan bunu nasıl yapabilirim ve bu aynı zamanda parametre adlarının bu kadar önemli olmasının nedeni de ve ayrıca neden '=' yazmamış ve neden ':' yazdım?

cevap

6

Dinamik olarak bir temsilci oluşturmak için Reflection.Emit'i kullanabilirsiniz. Delegeler .Net'te özel türler olduklarından, oluşturulacak kod oldukça açık değildir.Aşağıdakiler, Expression.Lambda() tarafından kullanılan yöntemlerin yansıtılan koduna dayanmaktadır. Action veya Func delegesinin bulunmadığı durumlarda özel delege türleri oluşturun. 17 parametreden daha yüksek veya ref veya out parametreleri). Performansta önem veriyorsanız

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

, size aynı temsilci defalarca yazın oluşturmaz böylece bir çeşit önbellek oluşturmak isteyebilirsiniz.

Kodunuzdaki tek değişiklik lambdaExpression yaratan çizgi olacaktır:

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

Ama aslında hiç Expression s ile uğraşmak gerekmez. Delegate.CreateDelegate() yeterlidir:

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

teşekkür I ederiz: Bu uzantı yöntemini kullanır

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

Daha önce bugün çalıştı ve harika çalıştı! Kodunuzun çoğunu anlıyorum, ancak ad ve nameBase ile yaptığınız şeyi neden kavrayamadım, neden bu tip tabanının adı + türün adıyla eşittir, sayı = 2 ne demektir? Her neyse, ben o güne kadar çalışmak için çalışıyordum –

+0

@UchihaMadara, bu tür adın benzersiz olduğundan emin olmak için bir yol çünkü aynı montajda aynı ada sahip iki türü olamaz çünkü. Ve 2 numara da öyle ki isimler “PersonJump”, “PersonJump2”, “PersonJump3” vb. Gibi. – svick

+0

Bunun çok akıllı olduğunu görüyorum, teşekkürler! =] –

0

açık kaynak çerçevesinde ImpromptuInterface (Nuget aracılığıyla v5.6.7) currying/partial Ben sürece bir hazır delegenin gerekmez olarak bu durumda işe yarar mı uygulanmasını uygulamak bir DLR vardır.

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

Yani jump bunu yansıtmıyor olabilir, ama bir temsilci sanki doğrudan çağırmak ve bir edebi temsilci değildir: Burada

onu bunu oluşturma ve çağırma C# versiyonudur Bu bir DLR nesnesidir, bu yüzden benim tahminim python'da aynı şekilde çalışacaktır.

Sadece bu sorunu çözmek için güzel bir şekilde tökezledi
0

, statik bir yönteme delegeler için şuna benzer :

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