2012-02-29 13 views
6

के साथ गतिशील रूप से प्रतिनिधियों का निर्माण करना हाय मैं एक ऐसा फ़ंक्शन बनाने की कोशिश कर रहा हूं जो गतिशील रूप से एक ही वापसी मूल्य के साथ एक प्रतिनिधि बनाता है और उसी पैरामीटर को विधिइन्फो के रूप में प्राप्त करता है जो इसे पैरामीटर के रूप में प्राप्त करता है और यह भी पैरामीटर नामों के लिए बहुत महत्वपूर्ण है !पैरामीटर नाम

मैं अब तक क्या किया है एक समारोह है कि एक लैम्ब्डा है कि एक ही पैरामीटर प्रकार प्राप्त करता है और 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); 
     } 
    } 

करता है किसी के पास कोई सुझाव है कि मैं यह कैसे कर सकता हूं? यह स्पष्ट करने के लिए, पैरामीटर नामों के बारे में मेरा कारण है, इसलिए मैं पैरामीटर नामों के साथ प्रतिनिधि को सक्रिय करने में सक्षम हूं, इसलिए मैं इसे इस तरह कह सकता हूं (जयकार = "हाँ! ', ऊंचाई = 3) (मेरा एप्लिकेशन पाइथन के साथ एकीकृत है, इस प्रकार मैं डायनेमिक इनवोक के बिना ऐसा करने में सक्षम हूं और यही कारण है कि पैरामीटर नाम और मैंने क्यों लिखा '=' और नहीं ':')

उत्तर

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

आप मुझे धन्यवाद:

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

यह इस विस्तार विधि का उपयोग करता आज पहले कोशिश की और यह बहुत अच्छा काम किया !! मैं आपके अधिकांश कोड को समझता हूं, लेकिन मैं वास्तव में समझ नहीं पाया कि आप नाम और नाम के साथ क्या कर रहे हैं, यह बेस बेस के नाम के समान क्यों है + प्रकार का नाम, संख्या = 2 क्या है? वैसे भी आपको बहुत धन्यवाद, मैं –

+0

@UchihaMadara के लिए काम करने की कोशिश कर रहा था, यह सुनिश्चित करने का एक तरीका है कि प्रकार का नाम अद्वितीय है, क्योंकि आपके पास एक ही असेंबली में एक ही नाम के साथ दो प्रकार नहीं हो सकते हैं। और नंबर 2 वहां है ताकि नाम 'पर्सनजंप', 'पर्सनजंप 2', 'पर्सनजूम एमपी 3, आदि जैसे हैं – svick

+0

मुझे लगता है कि यह बहुत स्मार्ट है, धन्यवाद! =] –

0

खुला स्रोत ढांचे ImpromptuInterface (nuget के माध्यम से v5.6.7) एक डीएलआर currying/partial लागू कार्यान्वयन मुझे लगता है कि जब तक आप एक शाब्दिक प्रतिनिधि की जरूरत नहीं है के रूप में इस मामले में काम करेगा है।

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

तो jump, एक शाब्दिक प्रतिनिधि नहीं है आप इसे प्रतिबिंबित नहीं कर सकते हैं, लेकिन आप इसे सीधे आह्वान कर सकते हैं जैसे कि यह एक प्रतिनिधि थे और:

यहाँ यह करते हैं और ऐसा लागू की ग # संस्करण है यह एक डीएलआर ऑब्जेक्ट है इसलिए मेरा अनुमान है कि यह सिर्फ अजगर में ही काम करेगा।

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; 
} 
संबंधित मुद्दे