2016-11-04 8 views
1

Action<>のインスタンスを作成するのに苦労しています。MethodInfoの私はリフレクションを使用して取得できます。ジェネリックメソッドを使用したインターフェイスのインスタンスをアクションに変換する

以下の例を使用して、オブジェクトに対してカスタムメソッドを簡単に呼び出すことができますが、実際にはそれらをアクションに変換したいと考えています。

私はDelegate.CreateDelegateを使ってやってみましたが、ジェネリック型のアクションを返すようには思えません。私は、次のインターフェイス作成した

例:次に

interface IMyTest { } // Marker interface 
interface<TInput> IMyTest : IMyTest { 
    void MyMethod(TInput input); 
} 

は、私はクラスのインターフェイス継承:

class MyClass : IMyTest<DateTime>, IMyTest<int> { 
    void MyMethod(DateTime input) { 
     // Do something 
    } 
    void MyMethod(int input) { 
     // Do something else 
    } 
} 

をそして私は、リフレクションを使用するメソッドを作成しますインタフェースインスタンスから「MyMethod」を見つけて呼び出す:

void DoCallMyMethod(object target, object input) { 
    IEnumerable<Type> interfaces = target.GetType().GetInterfaces() 
     .Where(x => typeof(IMyTest).IsAssignableFrom(x) && x.IsGenericType); 

    foreach (Type @interface in interfaces) { 
     Type type = @interface.GetGenericArguments()[0]; 
     MethodInfo method = @interface.GetMethod("MyMethod", new Type[] { type }); 

     if (method != null) { 
      method.Invoke(target, new[] { input }); 
     } 
    } 
} 

そして最後に、私はそれをすべて一緒に置く:

MyClass foo = new MyClass(); 
DoCallMyMethod(foo, DateTime.Now); 
DoCallMyMethod(foo, 47); 

を結果は何かになるように、私はDoCallMyMethodインサイド

をしたいと思う私はMethodInfo method汎用Actionに変換したいと思いますこのように:

Action<type> myAction = method; 

しかし、th明らかに動作しません。

は、私はいくつかの類似したSOの記事を見つけました次のような答えになってしまった(しかし、正確に私の場合をカバーなし):

Action<object> action = 
    (Action<object>)Delegate.CreateDelegate(typeof(Action<object>), target, method); 

をしかし、「ターゲットメソッドにバインドすることはできませんので、これは動作しません。その署名またはセキュリティの透過性がデリゲート型のものと互換性がないためです。

typeの入力を、オブジェクト参照を保持したまま(新しいインスタンスが表示されないまま)、入力としてどのように取得できますか?

答えて

1

方法は、特定のオブジェクトの種類(intDateTimeを)期待しているので、あなたは1 MyMethodのsからAction<object>を作成することはできません、あなたがAction<object>としてそれを扱う場合は、任意の型のオブジェクトとそれを呼び出すことがあります。

戻り値はAction<T>ですので、DoCallMyMethodメソッドに入力値を渡す必要はありません。あなたは、一般的なパラメータとして入力タイプを渡すことができます。

public Action<T> DoCallMyMethod<T>(object target) 
{ 
    var @interface = typeof(IMyTest<T>); 
    if (@interface.IsAssignableFrom(target.GetType())) 
    { 
     var method = @interface.GetMethod("MyMethod"); 
     if (method != null) 
     { 
      var action = Delegate.CreateDelegate(typeof(Action<T>), target, method) as Action<T>;      
      return action; 
     } 
    } 

    return null; 
} 

次に、このようにそれを使用する:あなたはコンパイル時に入力の種類がわからない場合、あなたはこれを試すことができ

MyClass foo = new MyClass(); 
var action1 = DoCallMyMethod<DateTime>(foo); 
var action2 = DoCallMyMethod<int>(foo); 

action1(DateTime.Now); 
action2(47); 

を:

public Delegate DoCallMyMethod(object target, Type inputType) 
{ 
    var @interface = typeof(IMyTest<>).MakeGenericType(inputType); 
    if (@interface.IsAssignableFrom(target.GetType())) 
    { 
     var method = @interface.GetMethod("MyMethod"); 
     if (method != null) 
     { 
      var @delegate = Delegate.CreateDelegate(typeof(Action<>).MakeGenericType(inputType), target, method); 
      return @delegate; 
     } 
    } 

    return null; 
} 

そしてそうのようにそれを使用します。

MyClass foo = new MyClass(); 
var action1 = DoCallMyMethod(foo, typeof(DateTime)) as Action<DateTime>; 
var action2 = DoCallMyMethod(foo, typeof(int)) as Action<int>; 

action1(DateTime.Now); 
action2(47); 

//or 

int input = 47; 
var @delegate = DoCallMyMethod(foo, input.GetType()); 
@delegate.DynamicInvoke(input); 

しかし、あなたがする必要がある場合は、オブジェクトのタイプが有効である場合は、アクションがオブジェクトを受信し、メソッドを呼び出すことができますAction<object>を返します。

public Action<object> DoCallMyMethod(object target, Type inputType) 
{ 
    var @interface = typeof(IMyTest<>).MakeGenericType(inputType); 
    if (@interface.IsAssignableFrom(target.GetType())) 
    { 
     var method = @interface.GetMethod("MyMethod"); 
     if (method != null) 
     { 
      Action<object> action = obj => 
      { 
       if (obj.GetType().IsAssignableFrom(inputType)) 
        method.Invoke(target, new object[] { obj }); 
      }; 

      return action; 
     } 
    } 

    return null; 
} 

そして...

MyClass foo = new MyClass(); 
Action<object> action = DoCallMyMethod(foo, typeof(int)); 
action(47);   // MyMethod(int) is called. 
action("...");  // Nothing happens. 

今あなたが持っています整数が渡されたときにのみMyMethod(int)を呼び出すAction<object>です。

+0

何とか動作しますが、あなたの答えから推測できない質問がもう1つあります(質問からは不明な点があります)。入力のタイプを知らなくても 'delegate'を取得したら、どうすれば汎用アクション<>を取得できますか?例えば、私が 'AddExternalCaller (アクションコールバック)'メソッドを持っていれば、 'callback'は' MyMethod'の変種の1つになります。 'List >'のグローバルコレクションにアクションを追加するために、 'AddExternalCaller()'と 'DoCallMyMethod()'の両方をどうやって取得できますか? – GTHvidsten

+0

@GTHvidsten:しかし、 'Action 'や 'Action 'のような '' Action 'は '' Action ''ではないので 'List >'に追加することはできません。 'int'が必要なときにオブジェクトを渡すメソッドを呼び出すことはできません。実際には、 'Action 'は反動的なので、 'Action 'を 'Action '変数に割り当てることができるので、これは反対方向で有効です。整数を持つオブジェクトを期待しているメソッドを呼び出すことができます。私はあなたがコールバックを保存する方法を変更する必要があると思います。 –

+0

'Action 'から 'Action 'に変換したのは、次の構文で変換する方法です: 'void convert (アクションコールバック){アクション foo = input =>コールバック((T)入力) ; } '。私はダイナミックデリゲートをアクションに変換することができました: 'Action handler = request => @ delegate.DynamicInvoke(request);'。現時点では私のシナリオでもうまくいきます。これに特有の欠点がありますか? – GTHvidsten

関連する問題