
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:
- Do not own resources at all
- Owning one unmanaged resource, that is, just convert it to a managed one
- Own one or many managed resources
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 usedThis 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() {
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();
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();
It became:
lifetime.Add(myOwnedResourceField = new Resource());
Unsubscribe from events
It was:
sender.Event += Handler;
It became:
sender.Event += Handler; lifetime.Add(() => sender.Event -= Handler);
Unsubscribe from IObservable
It was:
subscription = observable.Subscribe(Handler);
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:
- universal: guaranteed to cover any managed resources, even such as “when cleaning, execute the following code”
- expressive: the additional code is small in volume
- habitual: an ordinary class from a very popular library is used, which, in addition, if necessary, is easy to write on your own
- transparent: the cleanup code of each resource is located close to the capture code, most of the potential leaks will be immediately noticed during the review
- degrades performance: adds “memory traffic” by creating new objects
- does not affect the safety of using a “dead” object: its own resources will be cleared only once, but any checks with an ObjectDisposedException overrun must be done manually
I would be glad if the described method is useful to readers.