2012-03-24 11 views
3

これはprior threadの後続の一種です。私は、ユーザーが提供する動的に型付けされたメソッドを呼び出すために小さなラッパーを構築しています。このスキームはうまくいきますが、静的メソッドに対してのみ有効です。 CreateDelegateはインスタンスメソッドに対しても動作するはずですが、それらを使用すると、isStaticフラグがfalseの場合は "バインディングエラー"がスローされます(実際にはthrow-on-errorフラグがfalseであるためnullを返します) 。これが起こるのを見ることができるコードサンプルがあります。CreateDelegateインスタンスメソッドの代理人の作成を拒否する

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 

namespace ConsoleApplication4 
{ 
    delegate void myFoo(int i, string s); 
    delegate void myNull(); 

    internal class Callable 
    { 
     internal int nParams; 
     internal Type[] ptypes; 
     internal Delegate cb; 
     internal static Type[] actions = { typeof(Action), typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>), typeof(Action<,,,,>), 
             typeof(Action<,,,,,>), typeof(Action<,,,,,,>), typeof(Action<,,,,,,,>), typeof(Action<,,,,,,,,>), typeof(Action<,,,,,,,,,>), 
             typeof(Action<,,,,,,,,,,>), typeof(Action<,,,,,,,,,,,>), typeof(Action<,,,,,,,,,,,,>), typeof(Action<,,,,,,,,,,,,,>), typeof(Action<,,,,,,,,,,,,,,>) }; 

     internal Callable(Delegate hisCb) 
     { 
      MethodInfo mi = hisCb.Method; 
      ParameterInfo[] pi = mi.GetParameters(); 
      ptypes = pi.Select(p => p.ParameterType).ToArray(); 
      nParams = ptypes.Length; 
      if (nParams > 0 && nParams < 17) 
      { 
       cb = Delegate.CreateDelegate(actions[nParams].MakeGenericType(ptypes), mi, false); 
       if (cb == null) 
        Console.WriteLine("Warning: Unsuccessful attempt to CreateDelegate for " + hisCb + ", with methodinfo " + mi); 
       else 
        Console.WriteLine("Successful attempt to CreateDelegate for " + hisCb + ", with methodinfo " + mi); 
      } 
      else 
       cb = hisCb; 
     } 

     internal void doUpcall(object[] args) 
     { 
      if (args.Length != nParams) 
       throw new ArgumentException("Argument count must match number of parameters"); 
      switch (nParams) 
      { 
       case 1: 
        ((dynamic)cb).Invoke((dynamic)args[0]); 
        break; 
       case 2: 
        ((dynamic)cb).Invoke((dynamic)args[0], (dynamic)args[1]); 
        break; 
        // ... cases 3-15 similar, omitted to save space 
       default: 
        cb.DynamicInvoke((dynamic)args); 
        break; 
      } 
     } 
    } 

    internal class FooBar 
    { 
     internal FooBar() 
     { 
     } 

     internal static void printFields(int i, string s) 
     { 
      Console.WriteLine("In FooBar.printField-s with i="+i+", s="+s); 
     } 

     internal void printFieldi(int i, string s) 
     { 
      Console.WriteLine("In FooBar.printField-i with i=" + i + ", s=" + s); 
     } 
    } 

    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      FooBar myFooBar = new FooBar(); 
      Callable cbfb0 = new Callable((myFoo)FooBar.printFields); 
      cbfb0.doUpcall(new object[] { 77, "myfb" }); 
      Callable cbfb1 = new Callable((myFoo)myFooBar.printFieldi); 
      cbfb1.doUpcall(new object[] { 77, "myfb" }); 
      string pc = "Main"; 
      Callable cb0 = new Callable((myNull)delegate() { Console.WriteLine("Hello from myNull"); }); 
      cb0.doUpcall(new object[0]); 
      Callable cb1 = new Callable((myFoo)delegate(int i, string s) { Console.WriteLine("i=" + i + ", s.Length = " + s.Length); }); 
      Console.WriteLine("About to attempt to call Foo: Good args"); 
      cb1.doUpcall(new object[] { 2, "bar" }); 
      Console.WriteLine("After calling Foo"); 
      Callable cb2 = new Callable((myFoo)delegate(int i, string s) { Console.WriteLine("My parent class is " + pc + ", i=" + i + ", s.Length = " + s.Length); }); 
      Console.WriteLine("About to attempt to call Foo: Good args"); 
      cb2.doUpcall(new object[] { 12, "Bar" }); 
      Console.WriteLine("After calling Foo"); 
      System.Threading.Thread.Sleep(15000); 
     } 

     private static void Foo(int i, string s) 
     { 
      Console.WriteLine("i=" + i + ", s.Length = " + s.Length); 
     } 
    } 
} 

CreateDelegateがこのように動作する理由を理解できたら助けてください。 C#と.NETの参照情報によれば、静的メソッドとインスタンスメソッドの両方で動作するはずです。 「不成功」のケースを打ち破った場合は、成功または失敗を決定するものがmi.isStaticフラグの値であることを確認できます。

PS:実行時に必須の型に引数をキャストするために(動的)を使用することに注意してください。これは、私が思うに、涼しいです。あなたはキャスト(T)をしたいが、タイプTが何であるか分からず、タイプTのオブジェクトを作成することはできますが、そのオブジェクトを使用するメソッドへの動的呼び出しはできません私の15のケース・ステートメントのように。私はその問題を回避するために(動的)にキャストすることで、数十の古いスレッドが未解決のまま残っているように見える問題を解決します。 (これは、以前のスレッドで提案されているコードで改善されています。

+1

ヒント:あなたは 'Expression.GetActionType(のptype)を使用することができます'取得する

は、インスタンスメソッドのデリゲートを作成するには、あなたが同様にインスタンスに渡すことができますoverlaodを使用する必要があります。デリゲート型 –

+0

しかし、switch文と15件の場合は、まだ必要でしょうか?アクションの配列は醜いです。私はこの変更を行います。しかし、大きなスイッチのステートメントは私の見解ではもっと迷惑です!それでも、これは動作し、私の問題は完全に修正されました(デバッガがファーストチャンスモードで実行されていなかった場合、その例外は呼び出しスタックを.Invokeに戻していました...)。だから私は幸運なキャンパーです、私のコードの醜いswitch文でさえも! –

答えて

5

CreateDelegateは、呼び出し可能なデリゲートを作成します。これを実行するには、メソッドを呼び出すインスタンスが必要です。静的メソッドの場合、インスタンスは必要ありません。静的メソッドへのデリゲートの作成を可能にするオーバーロードを呼び出しています。

cb = Delegate.CreateDelegate(actions[nParams].MakeGenericType(ptypes), 
         hisCb.Target, 
         mi, false); 
+0

ありがとう、フィリップ!これを試して、それは動作します。このボードは素晴らしいです。 –

1

Delegate.CreateDelegateに表示されるコールは、Type, MethodInfo, boolのオーバーロードを呼び出すコールだけです。 documentationは、インスタンスメソッドを持っている場合、あなたは(1異なるオーバーロードを呼び出す必要があり

...それ

は、指定された静的メソッドを表すために、指定した型のデリゲートを作成していることのように述べていますobject)を使用してデリゲートを作成します。

+0

ありがとう、ガベ!私はそれをする方法を尋ねただろうが、フィリップは以下で説明した... –

関連する問題