【问题标题】:C# -= operator to unsubscribe from a single event multiple timesC# -= 运算符多次取消订阅单个事件
【发布时间】:2023-03-29 02:55:01
【问题描述】:

在 C# 5 中,取消订阅事件时 -= 运算符的行为是什么。

假设多次订阅同一个事件对该应用逻辑有效,如下:

Property_Saved += Property_Saved_Handler;
Property_Saved += Property_Saved_Handler;
Property_Saved += Property_Saved_Handler;

现在我们订阅了 3 次。

退订后用以下一行代码:

Property_Saved -= Property_Saved_Handler;

还剩多少订阅? 2?没有? ...?

【问题讨论】:

  • 你试过了吗?
  • 好吧,如果这些是数字呢? int x = 0; x += 1; x += 1; x += 1; x -= 1;。是x 2?还是 0?
  • @MorganThrapp 如果他们是IEnumerable<DateTime> 怎么办?如果奶奶有轮子,她会是一列火车。由于多播委托不是整数,因此怀疑它是否像整数一样是公平的。这并不是说他无法通过运行自己的代码自己发现。
  • 我没想到这么简单的问题会如此冒犯。
  • 这是一个合法的问题。为什么底层方法叫Delegate.Combine而不是Delegate.Add?对我来说,有点像代表的 HashSet ......如果目标是相同的方法,-= 如何知道要删除哪个代表?它不会是内存中的相同地址吗?即使使用示例代码,理解事件委托也并不总是显而易见的。给出解释后可能看起来很简单。

标签: c# events unsubscribe


【解决方案1】:

这应该也是安全的。

Property_Saved += Property_Saved_Handler;
Property_Saved -= Property_Saved_Handler;
Property_Saved -= Property_Saved_Handler;

【讨论】:

    【解决方案2】:
    private void button1_Click(object sender, EventArgs e)
    {
      // set breakpoint
    }
    
    this.button1.Click += new System.EventHandler(this.button1_Click);
    this.button1.Click += new System.EventHandler(this.button1_Click);
    this.button1.Click += new System.EventHandler(this.button1_Click);
    this.button1.Click -= new System.EventHandler(this.button1_Click);
    

    调用点击事件将显示两次断点命中。

    【讨论】:

    • @Abion47 任何足够聪明可以从事编程的人都足够聪明,可以弄清楚断点在哪里。
    • @EdPlunkett 首先,你严重高估了一些程序员的心智能力。其次,有很多初学者甚至不知道断点是什么。第三,依靠断点的存在来说明代码 sn-p 的工作是没有帮助的,因为您首先必须假设断点在哪里,这就是为什么绝大多数代码示例使用基本数学、打印语句和厘米。
    • 如果你一直拉着别人的腿,他们就会停止帮助别人,我想每个人都知道什么是断点。如果有人不知道什么是断点,他们就会开始探索新事物。毕竟 stackoverflow 是一个通过提问和回答来学习新事物的平台。
    【解决方案3】:

    之后剩下两个。每个-= 只删除一个订阅。至少,如果它仅使用常规委托来支持事件,情况就是这样。

    您可以很容易地看到这一点,而无需真正涉及事件:

    using System;
    
    public class Program
    {
        public static void Main(string[] args)
        {
            Action action = () => Console.WriteLine("Foo");
            // This is a stand-in for the event.
            Action x = null;
            x += action;
            x += action;
            x += action;
            x -= action;
            x(); // Prints Foo twice
        }
    }
    

    严格来说,事件订阅可以做任何事情。您可以实现这样的事件:

    private EventHandler weirdEvent;
    public event EventHandler WeirdEvent
    {
        add { weirdEvent += value; } // Subscribe as normal
        remove { weirdEvent = null; } // I'm bored with *all* the handlers
    }
    

    但通常事件只是委托给Delegate.CombineDelegate.Remove,这是+=-= 在C# 中的语法糖。

    我的article on events and delegates 包含有关组合和删除的确切情况的更多详细信息。

    【讨论】:

      【解决方案4】:

      只需使用 GetInvocationList 进行自己的测试

      public delegate void MyEventHandler(string s);
      public event MyEventHandler MyEvent;
      

      MyEventHandler @event = s => { };
      
      MyEvent += @event;
      Console.WriteLine(MyEvent.GetInvocationList().Length);
      
      MyEvent += @event;
      Console.WriteLine(MyEvent.GetInvocationList().Length);
      
      MyEvent -= @event;
      Console.WriteLine(MyEvent.GetInvocationList().Length);
      

      这将打印出来

      1
      2
      1
      

      【讨论】:

      • GetInvocationList() 方法在这里是一个很棒的技巧。我见过内存泄漏清理代码,它只是循环通过返回的集合并删除每个委托。
      猜你喜欢
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-05
      • 2014-09-10
      • 2021-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多