【问题标题】:Is it possible to change the Target of an Action at runtime in C#?是否可以在 C# 中在运行时更改动作的目标?
【发布时间】:2026-02-22 11:00:01
【问题描述】:

我正在尝试在 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...

鉴于我正在了解运行时在 lambda 和动态方面的工作原理,我开始认为这是不可能的。也许有某种使用反射的方法?

谢谢 兰斯

非常感谢任何建议。

【问题讨论】:

    标签: c# lambda runtime action bind


    【解决方案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();
    

    【讨论】:

    • 感谢马特的快速响应。你所说明的方式就是我现在的做法。问题是我希望在作者创建动作时拥有智能感知,因为它们可能非常复杂。我知道如果绑定类型没有正确的属性,单元测试应该可以缓解任何异常,但是如果智能感知可用,它会从开发的角度真正简化事情。
    • 您希望代码的使用者能够指定要执行的委托的智能感知吗?
    • 嗨 Tejs,我不知道我是否完全理解这个问题。我有一个在类型化类的范围内创建的操作。因此,匿名 lambda 上下文中的“this”将是最初声明它的类。稍后,当其他东西调用该操作时,我想将 this 的范围更改为一个不同的动态对象,该对象公开原始类的相同属性和方法,但以动态方式。我的想法是,然后我可以拥有智能感知,并在运行时控制调用的关闭以注入我想要的任何东西。
    • 添加了另一个例子,也许能给一些灵感
    • 好吧,我的例子不会完全因为委托的工作方式而起作用,但效果基本上是弄清楚如何分配 Action 以便 Action 可以在其上执行。
    【解决方案2】:

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

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

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

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

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

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

    我很确定没有办法同时获得 Intellisense 能够对 LINQ 不允许的任何东西使用运行时(动态)绑定。

    【讨论】:

    • 是的,当我昨晚要睡觉时,我在想如何同时获得动态的任何东西和智能感知几乎是不可能的。
    最近更新 更多