Is it possible to change the Target of an Action at runtime in C#?
I am trying to do something similar to javascript's function.bind() in c#.
I have an action:
var action = new Action(()=>{this.SomeProperty = 123;});
At runtime, I want to execute the action in a different closure so the instance "this" is a dynamic object.
something like 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...
开发者_运维知识库
I am starting to think this is not possible given what I am learning about how the runtime works in regard to lambdas and dynamics.. Perhaps there is some way using reflection?
Thanks Lance
Any advice is greatly appreciated.
You don't really need to do anything complicated, you just need to make an action taking a dynamic parameter.
Action<dynamic> method = new Action<dynamic>((obj) => obj.SomeProperty = 123);
You can then call the action with any number of objects (some will throw exceptions obviously if they cant set that property)
method(new ExpandoObject());
method(new object()); // exception
If you want to call it like your example, it would be as simple as making an extension method:
public static class DynamicActionExtensions
{
public static void DynamicInvoke<T>(this T actual)
{
dynamic obj = actual;
obj.SomeProperty = 123;
}
}
EDIT: How about this:
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();
Is it possible to change the target of an Action
? No, because Action
is immutable. However, you can relatively easily create a new delegate with the same method but a different target.
public static Action Bind(this Delegate d, object target)
{
return (Action) Delegate.CreateDelegate(typeof(Action), target, d.Method);
}
This will work just fine for lambdas that don't capture variables:
Action a = () => this.MyProperty = 12;
a();
var f = new Foo();
var b = a.Bind(f);
b();
Console.WriteLine(this.MyProperty == f.MyProperty); // "True"
Unfortunately, this doesn't seem to help you because you want to change the type of the target at runtime. Since the compiler bakes the type of the target into the delegate, there's no way to change it. And with dynamic
it's even worse because it generates completely different code to access dynamic types.
If you're willing to limit yourself to what the compiler will generate in an Expression<>
, you'd be able to do some complex expression tree manipulation and make it work. Since expression trees can't even have assignment operators, even your trivial example wouldn't be possible with this method.
I'm pretty sure that there's no way to both get Intellisense and be able to use runtime (dynamic) binding for anything that LINQ doesn't allow.
精彩评论