2009-07-26 4 views
12

में फ़ंक्शन पॉइंटर्स मुझे लगता है कि कुछ तरीकों से (या दोनों) Delegate या MethodInfo इस शीर्षक के लिए अर्हता प्राप्त करते हैं। हालांकि, न तो मैं जिस वाक्य की तलाश कर रहा हूं वह वाक्य रचनात्मक नग्नता प्रदान करता है। तो, संक्षेप में, वहाँ कुछ रास्ता है कि मैं निम्नलिखित लिख सकते है:सी #

FunctionPointer foo = // whatever, create the function pointer using mechanisms 
foo(); 

मैं एक ठोस प्रतिनिधि उपयोग नहीं कर सकते (यानी, delegate कीवर्ड का उपयोग एक प्रतिनिधि प्रकार की घोषणा करने) का कोई रास्ता नहीं है, क्योंकि रनटाइम तक सटीक पैरामीटर सूची तक जानना। संदर्भ के लिए, यहां मैं LINQPad में वर्तमान में क्या कर रहा हूं, जहां B (अधिकतर) उपयोगकर्ता उत्पन्न कोड होगा, और इसलिए Main होगा, और इसलिए मेरे उपयोगकर्ताओं के लिए nicety के लिए, मैं .Call:

को हटाने का प्रयास कर रहा हूं
void Main() 
{ 
    A foo = new B(); 
    foo["SomeFuntion"].Call(); 
} 

// Define other methods and classes here 
interface IFunction { 
    void Call(); 
    void Call(params object[] parameters); 
} 

class A { 
    private class Function : IFunction { 
     private MethodInfo _mi; 
     private A _this; 
     public Function(A @this, MethodInfo mi) { 
      _mi = mi; 
      _this = @this; 
     } 

     public void Call() { Call(null); } 
     public void Call(params object[] parameters) { 
      _mi.Invoke(_this, parameters); 
     } 
    } 

    Dictionary<string, MethodInfo> functions = new Dictionary<string, MethodInfo>(); 

    public A() { 
     List<MethodInfo> ml = new List<MethodInfo>(this.GetType().GetMethods()); 
     foreach (MethodInfo mi in typeof(Object).GetMethods()) 
     { 
      for (int i = 0; i < ml.Count; i++) 
      { 
       if (ml[i].Name == mi.Name) 
        ml.RemoveAt(i); 
      } 
     } 

     foreach (MethodInfo mi in ml) 
     { 
      functions[mi.Name] = mi; 
     } 
    } 

    public IFunction this[string function] { 
     get { 
      if (!functions.ContainsKey(function)) 
       throw new ArgumentException(); 

      return new Function(this, functions[function]); 
     } 
    } 
} 

sealed class B : A { 
    public void SomeFuntion() { 
     Console.WriteLine("SomeFunction called."); 
    } 
} 

उत्तर

28

आप कहते हैं कि आप संख्या और मानकों के प्रकार खुला रखने के लिए चाहते हैं, लेकिन आपको लगता है कि एक delgate के साथ क्या कर सकते हैं:

public delegate object DynamicFunc(params object[] parameters); 

यह बिल्कुल वही बात आप वर्तमान में है। इस प्रयास करें:

class Program 
{ 
    static void Main(string[] args) 
    { 
     DynamicFunc f = par => 
         { 
          foreach (var p in par) 
           Console.WriteLine(p); 

          return null; 
         }; 

     f(1, 4, "Hi"); 
    } 
} 

आप अपने Function वर्ग के लिए बहुत समान के रूप में एक उदाहरण-विधि प्रतिनिधि के बारे में सोच सकते हैं: एक वस्तु एक एक MethodInfo। तो इसे फिर से लिखने की कोई जरूरत नहीं है।

भी ढंग से काम C और C++ में संकेत दिए गए किसी भी आपको क्या चाहिए के करीब नहीं हैं: वे एक वस्तु उदाहरण और समारोह के लिए बाध्य नहीं किया जा सकता, और यह भी कि वे स्थिर लिखे जाते हैं, गतिशील रूप से टाइप नहीं।

public static DynamicFunc MakeDynamicFunc(object target, MethodInfo method) 
{ 
    return par => method.Invoke(target, par); 
} 

public static void Foo(string s, int n)  
{ 
    Console.WriteLine(s); 
    Console.WriteLine(n); 
} 

और उसके बाद:

आप एक DynamicFunc प्रतिनिधि में किसी भी अन्य विधि "रैप" चाहते हैं, यह कोशिश

DynamicFunc f2 = MakeDynamicFunc(null, typeof(Program).GetMethod("Foo")); 

f2("test", 100); 

ध्यान दें कि मैं एक स्थिर विधि Foo तो उपयोग कर रहा हूँ उदाहरण के लिए मैं null पास करता हूं, लेकिन अगर यह एक उदाहरण विधि थी, तो मैं ऑब्जेक्ट को बाध्य करने के लिए गुजर रहा था। Program कक्षा होने के लिए मेरी स्थिर विधियों को परिभाषित किया जाता है।

बेशक, यदि आप गलत तर्क प्रकार पास करते हैं तो आपको रनटाइम पर त्रुटियां मिलती हैं। मैं शायद आपके प्रोग्राम को डिज़ाइन करने का एक तरीका ढूंढूंगा ताकि जितना संभव हो सके संकलन समय पर अधिक प्रकार की जानकारी कैप्चर की जा सके।

+4

जबकि मैं आपको इस बारे में सोचने के लिए एक प्रतिभा मानता हूं, जब मैं प्रतिनिधियों को रिटर्न (डायनेमिक फ़ंक्शन) प्रतिनिधि के साथ बनाने का प्रयास करता हूं तो मुझे बाध्य त्रुटियां मिल रही हैं।CreateDelegate (टाइपफ़ोफ़ (डायनामिक फ़ंक्शन), \t \t \t \t यह, फ़ंक्शन [फ़ंक्शन]); –

+1

यह बहुत अधिक जटिल है ... लटकाओ। –

+2

ठीक है, शायद वह जटिल नहीं ... अद्यतन देखें। –

3

यहां एक और कोड है जिसका आप उपयोग कर सकते हैं; परावर्तन के बजाय धीमी गति से इसलिए यदि आप उम्मीद करते हैं अपने डायनामिक समारोह अक्सर कहा जा करने के लिए कॉल है, आप method.Invoke नहीं करना चाहती प्रतिनिधि के अंदर:

public delegate void DynamicAction(params object[] parameters); 
static class DynamicActionBuilder 
{ 
    public static void PerformAction0(Action a, object[] pars) { a(); } 
    public static void PerformAction1<T1>(Action<T1> a, object[] p) { 
     a((T1)p[0]); 
    } 
    public static void PerformAction2<T1, T2>(Action<T1, T2> a, object[] p) { 
     a((T1)p[0], (T2)p[1]); 
    } 
    //etc... 

    public static DynamicAction MakeAction(object target, MethodInfo mi) { 
     Type[] typeArgs = 
      mi.GetParameters().Select(pi => pi.ParameterType).ToArray(); 
     string perfActName = "PerformAction" + typeArgs.Length; 
     MethodInfo performAction = 
      typeof(DynamicActionBuilder).GetMethod(perfActName); 
     if (typeArgs.Length != 0) 
      performAction = performAction.MakeGenericMethod(typeArgs); 
     Type actionType = performAction.GetParameters()[0].ParameterType; 
     Delegate action = Delegate.CreateDelegate(actionType, target, mi); 
     return (DynamicAction)Delegate.CreateDelegate(
      typeof(DynamicAction), action, performAction); 
    } 
} 

और तुम इस तरह इसका इस्तेमाल कर सकते हैं:

static class TestDab 
{ 
    public static void PrintTwo(int a, int b) { 
     Console.WriteLine("{0} {1}", a, b); 
     Trace.WriteLine(string.Format("{0} {1}", a, b));//for immediate window. 
    } 
    public static void PrintHelloWorld() { 
     Console.WriteLine("Hello World!"); 
     Trace.WriteLine("Hello World!");//for immediate window. 
    } 

    public static void TestIt() { 
     var dynFunc = DynamicActionBuilder.MakeAction(null, 
      typeof(TestDab).GetMethod("PrintTwo")); 
     dynFunc(3, 4); 
     var dynFunc2 = DynamicActionBuilder.MakeAction(null, 
      typeof(TestDab).GetMethod("PrintHelloWorld")); 
     dynFunc2("extraneous","params","allowed"); //you may want to check this. 
    } 
} 

यह काफी तेज़ होगा; प्रत्येक डायनामिक कॉल में पैराम-स्टाइल पासिंग के कारण 1 टाइप चेक प्रति पैरा, 2 प्रतिनिधि कॉल और एक सर निर्माण शामिल होगा।

+1

मुझे लगता है कि धीमा होने के बारे में कुछ याद रखना और इसके बजाय CreateDelegate का उपयोग करना है, यही कारण है कि मैं उस समाधान पर शुरू करने के लिए कूद गया, क्योंकि यह पूरी तरह से संभव है कि इन कार्यों को प्रति सेकंड कई बार बुलाया जाएगा ... मैं इस बारे में ब्योरा देखें जब मैं आधा सो नहीं रहा हूं (पढ़ता है: 8 घंटे या तो) –

+1

जिज्ञासा से मैंने इस तकनीक को (दो पैरामीटर के साथ) का सामना किया, और पाया कि मेरी विधि में तीन और आधा प्रति कॉल ** ** दूसरे कॉल के लाखों। तो मुझे संदेह है कि ओवरहेड वास्तविक अनुप्रयोग में कोई समस्या नहीं होने की संभावना है; निर्भर करता है कि आपके उपयोगकर्ता अपने कार्यों में किस तरह की चीजें करेंगे। –