2009-07-26 21 views
12

何らかの方法で(またはその両方)DelegateまたはMethodInfoがこのタイトルの対象となるとします。しかし、どちらも私が探している統語的な素朴さを提供していません。だから、要するに、私は次のように書くことができますいくつかの方法があります: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デリゲートの他の方法を「ラップ」この試してみたい場合:私は、静的メソッドFooを使用してい

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

f2("test", 100); 

注:

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

をして、私はnullをインスタンスに渡しますが、それがインスタンスメソッドであれば、バインドするオブジェクトを渡しています。 Programは私の静的メソッドが定義されているクラスになります。

間違った引数型を渡すと、実行時にエラーが発生します。おそらく、できるだけ多くの型情報がコンパイル時にキャプチャされるように、プログラムを設計する方法を探します。

+4

私はあなたにこれを考えるための天才と考えていますが、return(DynamicFunction)Delegateを使用してデリゲートを作成しようとするとバインドエラーが発生します。CreateDelegate(typeof(DynamicFunction)、 \t \t \t \t this、functions [function]); –

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

これはかなり速くなります。各動的呼び出しには、params-styleの受け渡しのために、1つのparamごとの型チェック、2つのdelegate呼び出し、および1つの配列構築が含まれます。

+1

私はInvokeが遅くてCreateDelegateを代わりに使うことを思い出しているようです。そのため、これらの関数は1秒あたり何回も多く呼び出される可能性があるので、私はその解決策に乗り出しました。しかし、私が半分眠っていないときに、この詳細を見てください。(読んでいる:8時間ほど) –

+1

私はこの技術を(2つのパラメータを使って)私のものに対してタイムアウトしました。 **通話あたり2分の1秒**。だから私はオーバーヘッドが実際のアプリケーションで問題になる可能性は低いと思う。あなたのユーザーがどのような機能を果たしているかによって異なります。 –