将动作作为参数传递不允许其订阅

时间:2019-01-30 15:42:56

标签: c# events lambda delegates

这是测试班:

public class TestClass
{
    private readonly Action _action;

    public TestClass()
    {
        _action += () => { Console.WriteLine("1"); };
        Subscribe(_action);
        _action?.Invoke();
    }

    private static void Subscribe(Action action)
    {
        action += () => { Console.WriteLine("2"); };
    }
}

在运行时,不会打印“ 2”,因为不会调用订阅该动作的lambda。

在我看来,当将Action作为参数传递时,它是按值传递的(使用C ++术语),因此对+ =运算符的调用发生在Action的另一个实例上。

如何传递Action作为参数,以便以后可以使用+ =运算符订阅它。还是为此目的使用操作无效?

1 个答案:

答案 0 :(得分:2)

原因是,当编译器看到带有委托的+=运算符时,它使用Delegate.Combine方法来连接委托的调用列表,并返回 new 委托。 / p>

可以检查是否在Console.WriteLine(action.GetHashCode());运算符之前和之后调用+=

因此,您实际上是通过为 inner 范围内的变量(方法范围内的变量)分配新值来尝试从 outer 范围内更改变量。

说明:

当我们将Action委托作为参数传递时,它将创建一个方法范围的变量,并将对该委托的引用放入其中。稍后,当我们调用+=运算符时,它将返回一个新引用,并将其分配给相同的方法作用域变量。此时,它指向另一个对象,而原始_action变量仍指向初始对象。

如果要为在方法范围之外创建的变量分配新值并更改其指针,可以使用ref关键字通过引用传递它。这将向编译器指示参数是通过引用而不是通过值传递的:

private static void Subscribe(ref Action action)
{
    action += () => { Console.WriteLine("2"); };
}