是否可以在C#中在运行时更改Action的目标?

时间:2011-03-17 02:11:49

标签: c# lambda runtime action bind

我正在尝试在c#中执行类似于javascript的function.bind()的操作。

我有一个动作:

var action = new Action(()=>{this.SomeProperty = 123;});

在运行时,我想在不同的闭包中执行操作,因此实例“this”是一个动态对象。

类似的东西:

var action = new Action(()=>{this.SomeProperty = 123;});

dynamic myDynamic = new ExpandoObject();
myDynamic.SomeProperty=321;

action.Bind(myDynamic); <--Of course this does not work...

action.DynamicInvoke();

Console.WriteLine(myDynamic.SomeProperty); //Should write 123...

我开始认为这是不可能的,因为我正在学习关于lambdas和动力学的运行时如何工作..也许有一些方法使用反射?

由于 兰斯

非常感谢任何建议。

2 个答案:

答案 0 :(得分:1)

你真的不需要做任何复杂的事情,你只需要采取一个动态参数。

Action<dynamic> method = new Action<dynamic>((obj) => obj.SomeProperty = 123);

然后,您可以使用任意数量的对象调用该操作(如果他们无法设置该属性,某些对象会明显抛出异常)

method(new ExpandoObject());
method(new object()); // exception

如果你想像你的例子一样调用它,它就像制作扩展方法一样简单:

public static class DynamicActionExtensions
{
     public static void DynamicInvoke<T>(this T actual)
     {
          dynamic obj = actual;
          obj.SomeProperty = 123;
     }
}

编辑:这个怎么样:

public class Invoker
{
     protected List<dynamic> _objects = null;
     protected Action<dynamic> _method = null;

     public Invoker()
     {
         _objects = new List<dynamic>();
     }

     public void Bind(dynamic actual)
     {
          _objects.Add(actual);
     }

     public void SetDelegate<T>(Action<T> action)
     {
         _method = action; // should work due to covariant type assignment
     }

     public void DynamicInvoke()
     {
         _objects.ForEach(x => _method(x));
     }
}

Invoker x = new Invoker();
x.SetDelegate<SomeTypeForIntellisense>((obj) => obj.SomeProperty = 123);
x.Bind(new ExpandoObject());
x.DynamicInvoke();

答案 1 :(得分:0)

是否可以更改Action的目标?不,因为Action是不可变的。但是,可以相对轻松地使用相同的方法创建新的委托,但目标不同。

public static Action Bind(this Delegate d, object target)
{
    return (Action) Delegate.CreateDelegate(typeof(Action), target, d.Method);
}

这对于不捕获变量的lambda来说效果很好:

        Action a = () => this.MyProperty = 12;
        a();
        var f = new Foo();
        var b = a.Bind(f);
        b();
        Console.WriteLine(this.MyProperty == f.MyProperty); // "True"

不幸的是,这似乎对您没有帮助,因为您希望在运行时更改目标的类型。由于编译器将目标类型烘焙到委托中,因此无法更改它。使用dynamic会更糟糕,因为它会生成完全不同的代码来访问动态类型。

如果您愿意将自己限制在编译器将在Expression<>中生成的内容,那么您将能够执行一些复杂的表达式树操作并使其工作。由于表达式树甚至不能具有赋值运算符,因此使用此方法也无法实现简单的示例。

我很确定无法让Intellisense 能够对LINQ不允许的任何内容使用运行时(动态)绑定。