📜 ⬆️ ⬇️

The most simple and reliable implementation of the Dispose design pattern


It would seem that this template is not just simple, but very simple, it has been analyzed in detail not in one well-known book.

However, still even within the framework of one project, it can often be implemented in different ways, creating a zoo from bicycles, crutches and leaks.

I want to share my method of implementation, which is based on minimizing the invention of bicycles, minimizing the amount of code and increasing its expressiveness and transparency.

Preconditions


No confusion of managed and unmanaged resources


I never implement myself and do not advise colleagues to use the possession of managed and unmanaged resources in the same class.
')
Thus, one class can:

Inheritance implementations undesirable


I do not use inheritance from classes unless absolutely necessary; the proposed implementation assumes an instance of the sealed class as the owner of the resources.
This does not mean that it cannot be modified to support inheritance.

Wrappers for unmanaged resources are implemented using Janitor.Fody


Update: comments quite rightly state that for this purpose it is better to use classes inherited from CriticalFinalizerObject and SafeHandle .

What I used
This plugin for Fody , a free tool for modifying the build code after compilation, will allow you not to manually write out the thousand and one implementation details necessary for the correct release of resources.
Your code (example from documentation ):
public class Sample : IDisposable { IntPtr handle; public Sample() { handle = new IntPtr(); } public void Method() { //Some code } public void Dispose() { //must be empty } void DisposeUnmanaged() { CloseHandle(handle); handle = IntPtr.Zero; } [DllImport("kernel32.dll", SetLastError=true)] static extern bool CloseHandle(IntPtr hObject); } 

Postprocessing result:
 public class Sample : IDisposable { IntPtr handle; volatile int disposeSignaled; bool disposed; public Sample() { handle = new IntPtr(); } void DisposeUnmanaged() { CloseHandle(handle); handle = IntPtr.Zero; } [DllImport("kernel32.dll", SetLastError = true)] static extern Boolean CloseHandle(IntPtr handle); public void Method() { ThrowIfDisposed(); //Some code } void ThrowIfDisposed() { if (disposed) { throw new ObjectDisposedException("TemplateClass"); } } public void Dispose() { if (Interlocked.Exchange(ref disposeSignaled, 1) != 0) { return; } DisposeUnmanaged(); GC.SuppressFinalize(this); disposed = true; } ~Sample() { Dispose(); } } 


Now you can go to the most common case for which this article was written.

Implement Dispose Design Pattern for Managed Resources


Training


First we need the CompositeDisposable class from the Reactive Extensions library.
It is necessary to add a small extension method:
 public static void Add(this CompositeDisposable litetime, Action action) { lifetime.Add(Disposable.Create(action)); } 

Adding a field responsible for cleaning


 private readonly CompositeDisposable lifetime = new CompositeDisposable(); 

Dispose method implementation


 public void Dispose() { lifetime.Dispose(); } 

Nothing more and you never need to add to this method.

Clearing obviously constructed resources


Simply add the simplest code directly to the resource allocation point.
It was:
 myOwnResourceField = new Resource(); //  -   if (myOwnResourceField != null) { myOwnResourceField.Dispose(); myOwnResourceField = null; } 

It became:
 lifetime.Add(myOwnedResourceField = new Resource()); 


Unsubscribe from events


It was:
 sender.Event += Handler; //  -   sender.Event -= Handler 

It became:
 sender.Event += Handler; lifetime.Add(() => sender.Event -= Handler); 


Unsubscribe from IObservable


It was:
 subscription = observable.Subscribe(Handler); //  -   if (subscription != null) { subscription.Dispose(); subscription = null; } 

It became:
 lifetime.Add(observable.Subscribe(Handler)); 


Performing arbitrary cleanup actions


 CreateAction(); lifetime.Add(() => DisposeAction()); 


Check object status


 if (lifetime.IsDisposed) 


findings


The proposed method:

I would be glad if the described method is useful to readers.

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


All Articles