2009-07-26 34 views
12

我想在某些方面(或兩者)DelegateMethodInfo有資格這個稱號。但是,既沒有提供我正在尋找的語法好處。因此,簡而言之,有沒有一些辦法,我可以寫:函數指針在C#

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

我不能使用固體委託(即使用delegate關鍵字來聲明委託類型),因爲沒辦法直到運行時才知道確切的參數列表。作爲參考,這裏是我一直在LINQPad目前玩弄,其中B將(大部分)用戶生成的代碼,所以將Main,因此爲了我的用戶更好,我試圖刪除.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++中的函數指針也沒有更接近你所需要的:它們不能綁定到對象實例函數,而且它們是靜態類型的,而不是動態類型的。

如果你想在一個DynamicFunc授人以「包裝」任何其他方法,試試這個:

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 f2 = MakeDynamicFunc(null, typeof(Program).GetMethod("Foo")); 

f2("test", 100); 

請注意,我使用的是靜態方法Foo所以我通過null爲實例,但如果它是一個實例方法,我會傳遞該對象綁定到。 Program恰好是我的靜態方法定義的類。

當然,如果你傳遞了錯誤的參數類型,那麼你會在運行時得到錯誤。我可能會尋找一種方法來設計你的程序,以便在編譯時捕獲儘可能多的類型信息。

+4

雖然我認爲你是一個思考這個問題的天才,但我在嘗試使用以下代碼創建委託時遇到了綁定錯誤:return(DynamicFunction)Delegate。CreateDelegate(typeof(DynamicFunction), \t \t \t \t this,functions [function]); – 2009-07-26 11:43:04

+1

這很複雜......掛在上面。 – 2009-07-26 11:45:53

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

這樣會快很多;每個動態調用將涉及每個param 1個類型檢查,2個委託調用以及由於params風格傳遞導致的一個數組構造。