3

実行時にアクションを動的に作成する方法を見つけようとしていますが、不足しています。

メソッドを呼び出して、動的に作成されたアクションを渡したいとしましょう。これにより、アクションが呼び出されたかどうかなどを (何らかの理由で) 追跡できます。

void DoSomething(Action<string> action);

これが私が呼び出すメソッドであり、パラメーターを満たす Action を何らかの方法で動的に構築したいと考えています。

を使用して構築できることを知っていますnew Action<string>((s) => { });

しかし、この場合、コンパイル時に Action の署名がわからず、必要なのは、それが呼び出されたかどうかを知らせる超汎用の Action だけです。

これはプロジェクトの通信システムの一部であり、使用可能なアクションをサポートできるようにしたいと考えています (OnCompleted コールバックを考えてください)。

Proxy.DoSomething((s) => Console.WriteLine("The server said: " + s);

表現を生成し、それをネットワーク経由で撮影し、サーバー上で動的にアクションを作成し、オブジェクトでメソッドを呼び出して動的アクションを渡し、結果をクライアントに送り返し、そこで実際のアクションを呼び出したい.

少し説明:

クライアント側:

var proxy = GetProxyObject(); // Comms proxy
proxy.DoSomething((reply) => Console.WriteLine("Server said: " + reply));

以下:

  1. アクションの署名を発見する
  2. 内部表現オブジェクトを作成する (簡単です)
  3. それをネットワーク経由でサーバーに送信します

サーバ側:

void ReceivedMessage(msg)
{
   var actParam = msg.Parameters[0]; // This is obviously just for demonstration
   var action = BuildActionWrapper(actParam);
   var result = target.InvokeMethod("DoSomething", action.UnderlyingAction);

   // Send result and Action result back to client
   ReplyToClient(...);
}

void DoSomething(Action<string> act)
{
   act("HELLO!");
}

次に、サーバー上で動的に生成されたアクションに渡されたパラメーターをクライアント側に戻すと、実際のアクションはそれらで呼び出されます。

4

3 に答える 3

1

このような式ツリーを構築する方法の例を次に示します。

var mi = typeof(Console).GetMethod("WriteLine", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null);
var parameter = Expression.Parameter(typeof(string));
var body = Expression.Call(null, mi, new[] { parameter });
Expression<Action<string>> expression = Expression.Lambda<Action<string>>(body, new[] { parameter });

expression.Compile()("test");

Reflection.Emit別の方法として、実行時にデリゲートを生成するために使用できます。

例えば:

var dynMethod = new DynamicMethod("", null, new[] { typeof(string) });
var il = dynMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
var mi = typeof(Console).GetMethod("WriteLine", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null);
il.Emit(OpCodes.Call, mi);
il.Emit(OpCodes.Ret);

var dynDelegate = (Action<string>)dynMethod.CreateDelegate(typeof(Action<string>));
dynDelegate("test");

これにより、次のデリゲートが生成されます。

(string s) => Console.WriteLine(s)
于 2013-02-18T09:01:46.793 に答える
0

署名を次のように変換DoSomethingします。

void DoSomething<T>(Action<T> action, T parameter)
{
    action(parameter);
}

そして、それを次のように呼び出します。

Proxy.DoSomething<string>((q) => Console.WriteLine("The server said: " + q, "some string");
于 2013-02-18T08:59:09.253 に答える
0

Ok、

だから私は問題をある程度解決しているように見えた.

    var mi = this.GetType().GetMethod("DoSomething");
    var actArg = mi.GetParameters()[0];
    var args = actArg.ParameterType.GenericTypeArguments;

    var lt = Expression.Label();

    List<object> values = new List<object>();


    var valVar = Expression.Variable(typeof(List<object>), "vals");
    var para = args.Select(a => Expression.Parameter(a))
        .ToArray();

    var setters = new List<Expression>();

    foreach (var p in para)
    {
        setters.Add(Expression.Call(valVar,
            typeof(List<object>).GetMethod("Add", new[] { typeof(object) }), p));
    }

    var block = Expression.Block(
        variables: new ParameterExpression[]
        {
            valVar,
        },

        expressions: Enumerable.Concat(para,
        new Expression[]
        {
            Expression.Assign(valVar, Expression.Constant(values)),
        }.Concat(setters)
        .Concat(new Expression[]
        {
            Expression.Return(lt),
            Expression.Label(lt),
        })));
    var l = Expression.Lambda(block, para).Compile();
    mi.Invoke(this, new object[] { l });

基本的に、これはメソッドが設定するすべての Action パラメーターを保持するリストを作成します。

public void DoSomething(Action<string> act)
{
    act("Hello");
}

式のパラメーターはアクションの型引数を使用して構築され、その特定のパラメーターを値リストに追加するために多数のセッター式が構築されます。

式を実行すると、値リストが変数に定数として割り当てられ、セッター式がその値をリストに追加し、式が戻ります。

最終的な結果として、アクションは動的に生成されたアクションを持つことができ、結果はリストに入れられます。その後、リストはサーバーからクライアントにネットワーク経由で送信され、値はクライアントで実際の引数として使用されます。アクション。

于 2013-02-18T10:54:35.337 に答える