📜 ⬆️ ⬇️

Disposable without borders


In my previous article, I explained how an object can be simply and reliably responsible for its resources.

But there are many ownership options that are not the personal responsibility of the object:


There are no ready solutions among standard classes and .NET interfaces. But, fortunately, this bike is very easy to assemble yourself and it will be able to give a convincing answer to all the requirements regarding the release of resources.

New IDisposable <T>: now with generalization


public interface IDisposable<out T> : IDisposable { T Value { get; } } 

The semantics of generalized IDisposable differs from the usual in much the same way as “you can be free” from “immediately vacate the room”. Now, resource cleanup is separated from the implementation of the core functionality and can be determined by both the dependency provider and its consumer.
')
The implementation is as simple as mooing:
  public class Disposable<T> : IDisposable<T> { public Disposable(T value, IDisposable lifetime) { _lifetime = lifetime; Value = value; } public void Dispose() { _lifetime.Dispose(); } public T Value { get; } private readonly IDisposable _lifetime; } 


We use steroids


And now I will show how using a new bicycle and a few single-line pieces of syntactic sugar, you can simply, cleanly and elegantly solve all the options considered for freeing resources.

First, save yourself from calling the constructor with an explicit type indication using the extension method:
  public static IDisposable<T> ToDisposable<T>(this T value, IDisposable lifetime) { return new Disposable<T>(value, lifetime); } 

To use, simply write:
  var disposableResource = resource.ToDisposable(disposable); 

Types compiler in the lion’s share of cases will successfully display itself.

If the object already inherits IDisposable and this implementation suits us, then it is possible without arguments:
  public static IDisposable<T> ToSelfDisposable<T>(this T value) where T : IDisposable { return value.ToDisposable(value); } 

If you don’t need to delete anything, but they are expecting from us what we can do (remember about the harmful StreamReader?):
  public static IDisposable<T> ToEmptyDisposable<T>(this T value) where T : IDisposable { return value.ToDisposable(Disposable.Empty); } 

If you want to automatically unsubscribe from the object's events upon parting:
  public static IDisposable<T> ToDisposable<T>(this T value, Func<T, IDisposable> lifetimeFactory) { return value.ToDisposable(lifetimeFactory(value)); } 

... and apply like this:
  var disposableResource = new Resource().ToDisposable(r => r.Changed.Subscribe(Handler)); 

If cleaning requires the execution of a special code, then one-liner will come to the rescue:
  public static IDisposable<T> ToDisposable<T>(this T value, Action<T> dispose) { return value.ToDisposable(value, Disposable.Create(() => dispose(value))); } 

And even if the special code is also needed for initialization:
  public static IDisposable<T> ToDisposable<T>(this T value, Func<T, Action> disposeFactory) { return new Disposable<T>(value, Disposable.Create(disposeFactory(resource))); } 

Using is even easier than telling:
  var disposableViewModel = new ViewModel().ToDisposable(vm => { observableCollection.Add(vm); return () => observableCollection.Remove(vm); }); 

But what if we already have a finished wrapper, but we need to add a little more responsibility for cleaning up resources?
No problems:
  public static IDisposable<T> Add<T>(this IDisposable<T> disposable, IDisposable lifetime) { return disposable.Value.ToDisposable(Disposable.Create(disposable, lifetime)); } 


Results


Having stumbled upon this idea right in the course of solving a business problem, I immediately wrote and with a feeling of deep satisfaction applied all the considered one-liners.

What is surprising, despite the presence of at least one complete IDisposable <T> analogue in the person Owned <T> from Autofac , a cursory googling did not reveal similar extension methods.

I hope the article and the use of its materials in practice will give readers no less pleasure than the author.
Any additions and criticism are welcome.

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


All Articles