问题描述
为什么这段代码只会打印“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();