如何在 C# 中向动作添加另一个动作?

问题描述

为什么这段代码只会打印“A”和“B”,而不会打印“C”?

Action act = null;
act += () => MessageLog.Message("A");
act += () => MessageLog.Message("B");
Action<Action> add = a => a += () => MessageLog.Message("C");
add(act);
act.Invoke();

解决方法

委托是不可变的。 += 运算符创建一个 new 委托,其中包含一个调用列表,该列表由左侧的调用列表和运算符右侧的调用列表组成。然后它将对该委托的引用存储在左侧的变量中。所以

act += () => MessageLog.Message("A");

相当于

act = act + (Action) (() => MessageLog.Message("A"));

这又相当于:

act = (Action) Delegate.Combine(act,(Action) (() => MessageLog.Message("A"));

那么,现在我们可以去找您的 add 代表。这个 lambda 表达式:

a => a += () => MessageLog.Message("C");

... 修改参数 a 以引用新组合的委托...然后忽略它。

相反,您需要返回组合委托(因此将 add 更改为 Func<Action,Action>),然后在调用 add 时使用返回值:

Action act = null;
act += () => MessageLog.Message("A");
act += () => MessageLog.Message("B");
Func<Action,Action> add = a => a + () => MessageLog.Message("C");
act = add(act);
act.Invoke();
,

您可能知道,A += B 只是 A = A + B 的简写,所以您的 add 委托实际上只是:

a => a = a + () => MessageLog.Message("C")

+ 创建组合委托 a + () => MessageLog.Message("C")= 将其分配回 a。请注意您是如何重新分配参数 a,现在应该很清楚这个 doesn't change the variable that is passed in (act) at all

除了返回组合委托,还可以通过引用传递act

delegate void ByRefAction<T>(ref T parameter);

...

    Action act = null;
    act += () => Console.WriteLine("A");
    act += () => Console.WriteLine("B");
    ByRefAction<Action> add = (ref Action a) => a += () => Console.WriteLine("C");
    add(ref act);
    act.Invoke();