2013-02-18 2 views
2

Я пытаюсь выяснить, как динамически создавать действие во время выполнения, но при этом сокращаться.Динамически сгенерируйте действие

Предположим, я хочу вызвать метод и передать динамически созданное действие, чтобы я мог отслеживать, было ли действие вызвано и т. Д. (По какой-либо причине).

void DoSomething(Action<string> action); 

Это метод, который я буду ссылаться, и я хочу, чтобы как-то динамически создать действие, которое будет удовлетворять параметр.

Я знаю, что я мог бы просто построить один используя new Action<string>((s) => { });

Но в этом случае я не знаю, во время компиляции подписи действий и все, что я хочу, это супер-родовые действия, которые позволят мне знаете, если он был вызван.

Это часть системы связи для проекта, и я хочу иметь возможность поддерживать действия, которые можно использовать (подумайте о обратном вызове OnCompleted).

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

Я хочу, чтобы иметь возможность генерировать представление, стрелять, что по проводам, создать Action динамически на сервере, вызовите метод объекта и передать в моем динамическом действии, отправить результат обратно в клиент и вызвать действительный действие там.

Немного уточнение:

стороне клиента:

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

Underneath:

  1. Discover подпись действий
  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!"); 
} 

Затем обратно на стороне клиента параметры, передаваемые в динамически генерируемые действия на сервере, реальное действие просто вызывается, с теми.

ответ

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

В основном, это создает список, чтобы держать все действие параметров множества методов в:

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

параметров выражения строятся с использованием аргументов типа действия и ряд сеттер выражения создаются для добавления этого конкретного параметра в список значений.

При выполнении выражения присваивает список значений как константу переменной, которую он имеет, затем вырабатывает значения в списке и возвращает выражение.

В результате каждый действие может иметь динамически генерируемое действие, и результаты просто попадают в список, список затем может быть отправлен по проводнику от сервера к клиенту, и значения будут использоваться на клиенте как аргументы фактического действия.

0

Convert DoSomething ссылки на следующее:

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

И назовите это;

Proxy.DoSomething<string>((q) => Console.WriteLine("The server said: " + q, "some string"); 
+0

Не уверен, что вы полностью следовали за вопросом, но не для меня, чтобы передать «некоторую строку» на сервер, что кажется довольно крутым, не так ли? «Q» поступает с сервера. – Clint

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) 
+0

Не удалось ли это просто использовать деревья выражений ...? –

+0

Конечно, вы могли бы использовать деревья выражений. Я добавил свой ответ, чтобы включить пример этого. –

+0

Итак, не находите ли вы избыток или даже плохую идею с использованием Emit вместо простого и понятного дерева выражений? : D –

Смежные вопросы