4

反映された型引数を使用するクロージャを作成するにはどうすればよいですか? .net 3.5 をターゲットにする

反省がなければ私は持っていただろう

void Main()
{
    int i = 0;
    Action<Foo> doSomething = (foo) => i += foo.GetNumber();
    var myFoo = new Foo();
    myFoo.UseFoo(doSomething);
    Console.WriteLine(i);
}

class Foo
{
    public int GetNumber() { return 4; }
    public void UseFoo(Action<Foo> doSomething)
    {
        doSomething(this);
    }
}

別のアセンブリからのリフレクションを介して型が取得されるのはいつですか?Fooどのように設定しdoSomethingますか?

void Main()
{
    Type fooType = GetType("Foo");
    int i = 0;
    object doSomething = // ???;

    var myFoo = Activator.CreateInstance(fooType);
    fooType.GetMethod("UseFoo").Invoke(myFoo, new object[] { doSomething });
    Console.WriteLine(i);
}
4

5 に答える 5

2

これにたどり着くために、DocXczとNikola Anusevの両方の回答を使用しました

static void Main()
{
    var fooType = typeof(Foo); // get type via any method
    int i = 0;

    Action<object> doSomething = (foo) => i += (int)foo.GetType().GetMethod("GetNumber").Invoke(foo, null);
    var typedDoSomething = (typeof(Program)).GetMethod("DelegateHelper").MakeGenericMethod(fooType).Invoke(null, new object[] { doSomething });

    var myFoo = Activator.CreateInstance(fooType);
    fooType.GetMethod("UseFoo").Invoke(myFoo, new object[] { typedDoSomething });
    Console.WriteLine(i);
}

public static Action<T> DelegateHelper<T>(Action<object> generic)
{
    return x => generic(x);
}

基本的に、汎用ヘルパー メソッドを使用して、実行時に決定されたAction<object>を変換する必要がありました。Action<T>

于 2012-07-12T20:30:30.750 に答える
1

.NET3.5であなたが望むことを正確に行うことは不可能であると私は確信しています。

近づく唯一の方法はラムダを使用せず、単純な古いメソッドを使用します。基本的に、次のようなクラスでアクションを定義します。

public class FooActionMethods<TFoo>
{
    public static void DoSomething(TFoo foo)
    {
        int i = 0;
        int number = (int)typeof(TFoo).GetMethod("GetNumber").Invoke(foo, null);
        Console.WriteLine(i + number);
    }
}

次に、たとえば、次のDoSomethingメソッドを呼び出すことができます。

Type fooType = // somehow, we get the type from other assembly
object fooInstance = Activator.CreateInstance(fooType);  

Type fooActionType = typeof(Action<>).MakeGenericType(fooType);
Type fooActionMethodsType = typeof(FooActionMethods<>).MakeGenericType(fooType);

Delegate action = Delegate.CreateDelegate(fooActionType, fooActionMethodsType, "DoSomething");

fooType.GetMethod("UseFoo").Invoke(fooInstance, new object[] { action });

クラス全体FooActionMethodがジェネリックであるため、リフレクション( )を使用して、正確なタイプの。でMakeGenericTypeクローズを作成できます。コンパイル時の情報がないため、リフレクションを介してのみインスタンスと対話できます。これらの相互作用を単純化するために、リフレクションの操作を単純化するいくつかのライブラリを扱ったSOの質問を確認してください。FooActionMethodFooTFoo

それ以外に、これ以上のことはできないと思います。良い質問をありがとう-素晴らしいパズルになります!:)

補足として、式ツリーを使用してラムダ式を動的に作成することでこれを解決しようとしていました。式ツリーにクロージャを含める方法を見つけることができませんでした。そうでなければ、その方法もうまくいくでしょう。

于 2012-07-12T20:17:49.863 に答える
1

コンパイル時に Foo クラスを含むアセンブリを静的にリンクしていない場合、コードで Foo を使用できないことは事実です。

クロージャーでもリフレクションを使用する必要があります。

Action<object> doSomething = 
(foo) => i+= (int)foo.GetType().GetMethod("GetNumber").Invoke(foo, null);

これは、Foo のメソッド UseFoo がこのようなものであり、.NET 4 でのみ動作することを前提としています。

public void UseFoo(Action<Foo> action)
{
    action(this);
}

もちろん、リフレクションの内容は保証されていないため、UseFoo の呼び出し全体を try..catch ブロックに囲む必要があります。

更新: これは、.NET 4 でのみ機能します。この場合、Action は、UseFoo メソッドによって想定される Action として渡すことができます。

于 2012-07-12T19:02:19.143 に答える
0

C# のクロージャは、コンパイラによって生成されるものです。反映された型に対して生成された同じコードが必要な場合は、コードを手動で生成する必要があります。

于 2012-07-12T19:07:59.317 に答える
0

foo が適切なインターフェースを実装している場合Action、このインターフェースで作業を行うことができます。

そうでない場合は、デリゲートでリフレクションを使用するか (簡単)、実行時に式 API を使用してデリゲートをコンパイルする必要があります (より多くの作業、最大のパフォーマンス)。

C# コンパイラがクロージャを作成するiので、それを続ける必要があります。

Action<int> incrementI = inc => i += inc;

iコンパイルされたデリゲートでインクリメントを使用してインクリメントできるようになりましiた。

于 2012-07-12T19:09:19.190 に答える