What to do with delegate / event links in a class that implements IDisposable

I read about memory management and came across a situation in a project where a book or Google came up with the exact answer. I already know that delegates manage objects, and events control instances of a delegate. Having said that, delegate instances will be deleted from memory after the application terminates.

I cannot figure out how to ensure that the external code releases all event references until the moment my class is deleted (explicitly or GC). As an example, class A provides an event, and class B consumes it. Class B calls Dispose on class A without releasing delegate references. Of course, we cannot throw an error from the Dispose method itself.

Below is a class with a delegate and another that uses it.

 public class ClassB { private ClassA A { get; set; } public ClassB() { this.A = new ClassA(); this.A.OnProcessed += new ClassA.DelegateProcessed(this.ClassA_Processed); } public void Process() { this.A.Process(); } public void ClassA_Processed (ClassA sender, EventArgs e) { // Do something. // Code written by another developer does not free up events before calling Dispose. this.A.Dispose(); this.A = null; } } public class ClassA: IDisposable { public delegate void DelegateProcessed (A sender, EventArgs e); public event DelegateProcessed OnProcessed = null; ~ClassA() { this.Dispose(false); } public void Dispose () { this.Dispose(true); System.GC.SuppressFinalize(this); } private void Dispose (bool disposing) { if (!this.Disposed) { if (disposing) { // Dispose managed resources here. // Is it possible / advisable to dispose of delegates / events here? // Will this adversely affect the consumer class? this.OnProcessed -= new ClassA.DelegateProcessed(this.ClassA_Processed); } } this.Disposed = true; } public void Process () { this.OnProcessed(this, new EventArgs()); } public void ClassA_Processed (ClassA sender, EventArgs e) { } } 

We need to make sure that ClassA is suitable for garbage collection no matter what the developer does with ClassB. The bottom line is to minimize the amount of time that ClassA spends in memory, even if the consumer is sloppy.

UPDATE . From the responses it is clear that the events should not be explicitly deleted from ClassA. Regarding the main issue, the methods seem to be weak recommendations, as shown below. The goal is to minimize the time that ClassA remains in memory. Please let me know in case I missed any of them.

+4
source share
4 answers

Instead of a โ€œclassicโ€ event subscription, you should take a look at โ€œWeak Event Templatesโ€.

Subscriptions by subscription can keep objects alive even if these links are the only links on the left, and the link object itself is already out of scope. In this case, the referenced object will never be collected by GarbageCollector and will remain alive until the end of your application.

This leads to serious memory leaks.

If you use the Weak Events pattern, you let GabageCollector better determine if an object refers to an object, or if events are the only links. In this case, the objects are collected and your resources are freed.

+1
source

IDisposable used to deterministically free unmanaged resources.

There is no need to remove event handlers. For example, if you look at the Windows Forms Form and UserControl classes, or the ASP.NET Page and UserControl classes, all of which are IDisposable , you will see widespread use of events and not during recycling.

+2
source

This section of code:

 private ClassA A { get; set; } public ClassB() { this.A = new ClassA(); this.A.OnProcessed += new ClassA.DelegateProcessed(this.ClassA_Processed); } 

means you do not have to do anything.

A B instance has an instance of A , and A again contains ref (via an event) to B

When a B becomes unavailable, then A (GC and circular links) will also be collected.

When โ€œAโ€ is selected (longer) before B , then โ€œAโ€ (directivity) will also be collected.

The IDispoable interface on A pointless.


And regarding implementation:

  // class B this.A.OnProcessed += new ClassA.DelegateProcessed(this.ClassA_Processed); // in classA this.OnProcessed -= new ClassA.DelegateProcessed(this.ClassA_Processed); 

This will not work, 2 different this mean they are two different ways.

+1
source

An actually written class must in its IDisposable.Dispose method unsubscribe from any events for which it has signed. If the object whose event is signed has a GC lifetime comparable to the useful life of the object that signed (which is a very common case), it does not matter if the subscription is cleared or not. Unfortunately, if A left unsubscribed from event B and something preserves a long-term reference to B (intentionally or not), everything that keeps B alive will also continue to live A and everything that A contains direct or indirect to link (including objects that have active event subscriptions from A ). It is very easy to finish large forests of interconnected objects that usually become suitable for garbage collection, but they should all be kept as long as any of them are needed.

This is too bad an event subscription and cancellation, so inconvenient. If there was an event type object, an object that was about to subscribe to various events could use the event dispatcher object to manage subscriptions (so you could say something like MyEventManager.Subscribe(SomeObject.SomeEvent, someProc) and then unsubscribe from MyEventManager.Dispose from all the events to which he has subscribed .. Unfortunately, there is no suitable way for the method to accept the event as a parameter, and therefore had no way to have a common target class to manage incoming subscriptions. The best ie, that could be done, it would probably be rated Class CleanupManager , which takes a couple of delegates and called something like `MyCleaner.Register (() => { SomeObject.SomeEvent + = someProc;}, () => {SomeObject. SomeEvent - = someProc ();}), but it seems rather inconvenient.

0
source

Source: https://habr.com/ru/post/1435938/


All Articles