What happens if a C # event handler is deleted and I call it?

This question is a continuation of the C # Events and Thread Safety question (I am not the author of this) and Eric Lippert Events and Racing related blog. There are other similar questions on SO, but none of them address this case, the general consensus is that as long as you unsubscribe, you are safe, but I do not believe that this is true all the time.

According to the discussion on the SO question and the blog, the template to be used looks something like this:

var ev = NotifyPropertyChanged; if (ev != null) ev(this, new PropertyChangedEventArgs("Foo")); 

But what if the following situation arises:
1) I sign the listener:

 mytype.NotifyPropertyChanged += Handler; // Handler is instance method in SomeObject class 

2) I (or the runtime, due to visibility) dispose of SomeObject, which contains the listener and unsubscribes the listener at about the same time that the property is notified.

3) Although this is unlikely due to the very short period of time in which this can happen, it is theoretically possible that since ev saves an old subscriber that no longer exists, it will call the function on an object that no longer exists.

According to Eric Lippert, "event handlers must be reliable in the face of the challenge, even after the event has been canceled." But if the handler is not signed and located , it can no longer take care of the call. What is the right way to deal with this situation?

Wrap code from (1) in try-catch? Which exception should be caught? ObjectDisposedException seems likely, but not the only thing that can happen, I think.

+7
source share
5 answers

I believe that you wanted to say an object that was already GC'd, and not disposed of. Well, that cannot be; MultiCastDelegate ( EventHandler ) maintains a reference to an object through its signed method, that is, it cannot be GC'd until the handler is deleted.


Dispose() has nothing to do with the fact that the method is unavailable, it is a template used to clear its own resources, i.e. resources that cannot be processed by the GC.

The object itself is still alive and healthy, although it can throw an exception if you call a method that depends on this own resource (depending on the implementation of the course. The fact is that the object still exists, like the method).

There is nothing magical when you call Dispose() . I could easily crack a class that implements IDisposable and has a completely empty Dispose() method. Call everything you want, it does nothing and does not change the state of the object.

+10
source

Everything that Ed Smith said is accurate. I want to clarify that the behavior you see will depend on the implementation of the function / event handler. It may or may not throw an exception, and it may or may not behave strangely.

Dispose does nothing magical to make an object inaccessible. Dispose usually does some things, for example, if one of the fields / properties was a File descriptor, then it would in turn call the descriptor of this file descriptor to immediately free the resource (see http://blogs.msdn.com/b/kimhamil /archive/2008/11/05/when-to-call-dispose.aspx )

If the event handler tried to do something with this file, then an exception might be thrown.

It is possible that the code for this event handler deals only with member fields, such as ints and strings, and therefore can behave as usual.

It is also possible that the person who wrote this class / function has an explicit if(Disposed){throw blah; } if(Disposed){throw blah; } , so if you try to call a function, it will throw an exception, telling you that the operation is not valid on the remote instance.

It is important to unsubscribe from events, otherwise your object will never be GC'd.

+2
source

I believe that in most situations, you should not catch any exceptions thrown by subscribers. You cannot expect to handle all possible exceptions that may be caused by client code. In short, I think that the principle of quick rejection should be applied here.

+1
source

Before the object, to decide what should happen if it has already been Disposed (it can still execute the code in the "Disposed" state, since Dispose is just what you voluntarily call to inform the object about it.) I was discovered these errors when the application was stopped earlier, when events were processed in another thread, while the object was cleared; event handlers were not written correctly to prevent this race condition. It is worth a look.

+1
source

This code always works ...

 var ev = NotifyPropertyChanged; if (ev != null) ev(this, new PropertyChangedEventArgs("Foo")); 

... even if the other thread does not sign the event handler between the first and last lines. This is because delegates are immutable. Each time event handlers are added or removed, a new multicast delegate is created. Therefore, you can consider them as value types. Therefore, ev will never be changed after it has been assigned in the first line.

+1
source

All Articles