【问题标题】:Passing action as an argument does not let to subscribe to it将动作作为参数传递不允许订阅它
【发布时间】:2019-01-30 15:42:55
【问题描述】:

这里是测试类:

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 作为参数传递,以便以后可以使用 += 运算符订阅它。或者也许使用 Actions 对这个目的无效?

【问题讨论】:

  • 传递by reference 不适用于您的方案吗?
  • action += () => { Console.WriteLine("2"); };action = action + () => { Console.WriteLine("2"); }; 的简写 所以你实际上是在创建一个新动作,而这个新动作永远不会离开 Subscribe 的范围
  • 因此,结合前面的两个 cmets 似乎意味着将 Subcribe 方法更改为 void Subscribe(ref Action action) 可能会起作用。你传入一个动作,引用将被修改为一个现在有两个委托的新动作,并传回给调用者(通过Subscribe(ref _action);

标签: c# events lambda delegates


【解决方案1】:

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

+=运算符前后调用Console.WriteLine(action.GetHashCode());可以查看

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

解释:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-06
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 2023-04-02
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    相关资源
    最近更新 更多