0

In C# 5, what is the behavior of the -= operator when unsubscribing from events.

Assume subscribing to the same event multiple times is valid for this application logic, such as follows:

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

Now we are subscribed three times.

After unsubscribing with the following one line of code:

Property_Saved -= Property_Saved_Handler;

How many subscriptions are left? 2? none? ...?

Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
mlandgraf
  • 61
  • 1
  • 3
  • 2
    Have you tried it? – Gilad Green Oct 12 '16 at 18:43
  • 2
    Well, what if those were numbers? `int x = 0; x += 1; x += 1; x += 1; x -= 1;`. Is `x` 2? or 0? – Morgan Thrapp Oct 12 '16 at 18:43
  • 9
    @MorganThrapp What if they were `IEnumerable`? If grandma had wheels she'd be a train. Since a multicast delegate isn't an integer, it's fair to wonder if it acts like one or not. That's not to say he couldn't have found out for himself just by running his own code. – 15ee8f99-57ff-4f92-890c-b56153 Oct 12 '16 at 18:46
  • 2
    I didn't realize such an easy question would be so offensive. – mlandgraf Oct 13 '16 at 12:46
  • 5
    It's a legitmate question. Why is the underlying method called `Delegate.Combine` and not `Delegate.Add`? Kinda sounds like a HashSet of delegates to me...And how does `-=` know which delegate to remove if the target is the same method? Wouldn't it be the same address in memory? Understanding event delegates, even with sample code, is not always obvious. Might seem simple after an explanation is given. – mdisibio Oct 13 '16 at 16:11

4 Answers4

15

Two are left after that. Each -= only removes one subscription. At least, that's the case if it's using just a regular delegate to back the event.

You can see this easily without really involving events:

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
    }
}

Strictly speaking, an event subscription could do anything. You could implement an event like this:

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

But normally events just delegate to Delegate.Combine and Delegate.Remove, which are the methods that += and -= are syntactic sugar for in C#.

My article on events and delegates contains more details about exactly what happens with combination and removal.

mdisibio
  • 3,148
  • 31
  • 47
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
3
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);

Invoking the click event will show the breakpoint hit twice.

mdisibio
  • 3,148
  • 31
  • 47
Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
  • 1
    @Abion47 Anybody who's bright enough to engage in programming at all is bright enough figure out where the breakpoint goes. – 15ee8f99-57ff-4f92-890c-b56153 Oct 12 '16 at 18:52
  • 2
    @EdPlunkett First, you seriously overestimate the mental capacity for some programmers. Second, there are plenty of beginner programmers who don't even know what a breakpoint is. Third, relying on the existence of breakpoints to illustrate the workings of a code snippet isn't helpful since you first have to assume where the breakpoints are, which is why the vast majority of code examples use basic math, print statements, and comments. – Abion47 Oct 12 '16 at 18:59
  • 4
    If u keep pulling others leg they will stop helping others, I think everybody knows what are breakpoints. and if somebody does not know what are breakpoints, they will start exploring new thing. after all stackoverflow is a platform where you learn new things by questing and answering. – Vivek Nuna Oct 12 '16 at 18:59
3

This should be also safe.

Property_Saved += Property_Saved_Handler;
Property_Saved -= Property_Saved_Handler;
Property_Saved -= Property_Saved_Handler;
Peter M.
  • 1,028
  • 2
  • 10
  • 27
1

Just make your own test using 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);

This will print

1
2
1
L.B
  • 114,136
  • 19
  • 178
  • 224
  • The `GetInvocationList()` method is a **great** tip here. I've seen memory-leak clean-up code that simply loops thru the returned collection and removes each delegate. – mdisibio Oct 13 '16 at 15:57