using System; using System.Collections.Generic; using System.Linq; using Chernobyl.Collections.Generic; namespace Chernobyl.Destruction { /// /// A collection of represented as a single instance. /// public interface ICompositeDisposable : IDisposable { /// /// The instances to be disposed of. /// IEnumerable Disposables { get; } } /// /// Wraps an object to make it disposable. When is invoked /// on this object, the type is checked to see if it is before attempting /// to dispose of it. /// public class TryDisposable : IDisposable { /// The instance to be disposed. public TryDisposable(T value) { Value = value; } /// public void Dispose() => (Value as IDisposable)?.Dispose(); /// /// The instance to be disposed. /// T Value { get; } } /// /// Invokes a method when is called. /// public class ActionDisposable : IDisposable { /// The method to invoke when disposed is called. public ActionDisposable(Action action) { _action = action; } /// m public void Dispose() => _action(); readonly Action _action; } /// /// A collection of represented as a single instance. /// public class CompositeDisposable : ICompositeDisposable where TDisposable : IDisposable { /// The instances to dispose of. public CompositeDisposable(IEnumerable disposables) { Disposables = disposables; } /// public void Dispose() { // Convert to array in case the Disposables enumerable is modified during the dispose // process which would cause a InvalidOperationException. Disposables.ToArray().Dispose(); } /// /// The instances to be disposed of. /// public IEnumerable Disposables { get; } IEnumerable ICompositeDisposable.Disposables => Disposables.Select(x => x); } /// /// An where the instance being disposed of is separate of the object /// doing the disposing (the disposal is delegated to another object). /// /// The type being disposed of. public class DelegatedDisposable : IDisposable { /// The instance to dispose in place of . /// The instance whose disposal is represented by . public DelegatedDisposable(IDisposable disposable, T item) { _disposable = disposable; Item = item; } /// public void Dispose() => _disposable.Dispose(); /// /// The item being disposed of. /// public T Item { get; } readonly IDisposable _disposable; } /// /// Extension and utility methods for . /// public static class Disposable { /// /// Returns a disposable that invokes when disposed. /// public static IDisposable From(Action action) => new ActionDisposable(action); /// /// Wraps an object to make it disposable. When is invoked /// on this object, the type is checked to see if it is before attempting /// to dispose of it. /// public static IDisposable AsDisposable(this T value) => new TryDisposable(value); /// /// Invokes when is called. /// public static IDisposable AsDisposable(this Action action) => From(action); /// /// Returns a single instance that disposes of all provided instances. /// public static IDisposable Join(this IEnumerable disposables) where T : IDisposable => new CompositeDisposable(disposables); /// /// Returns a single instance that disposes of all provided instances. /// public static IDisposable Join(this IEnumerable disposables) => Join(disposables); /// /// Returns a single instance that disposes of all provided instances. /// public static IDisposable Join(params T[] disposables) where T : IDisposable => Join(disposables.AsEnumerable()); /// /// Returns a single instance that disposes of all provided instances. /// public static IDisposable Join(params IDisposable[] disposables) => Join(disposables.AsEnumerable()); /// /// Returns an that invokes and then /// disposes of . /// public static IDisposable BeforeDispose(this IDisposable disposable, Action action) => new[] { new ActionDisposable(action), disposable }.Join(); /// /// A non-existent disposable object. Useful for avoiding the use of nulls. /// public static readonly IDisposable Empty = new EmptyDisposable(); class EmptyDisposable : IDisposable { public void Dispose() { } } /// /// Disposes of all instances in . /// public static void Dispose(this IEnumerable disposables) where T : IDisposable => disposables.For(d => d.Dispose()); /// /// Disposes of if it has been created. /// public static void Dispose(this Lazy disposable) where T : IDisposable { if(disposable.IsValueCreated) disposable.Value.Dispose(); } /// /// Returns all of the instances under and including . /// Instances are casted to to create the tree being /// generated. /// public static IEnumerable Decompose(this IDisposable disposable) { return disposable is ICompositeDisposable composite ? composite.Disposables.SelectMany(Decompose) : disposable.ToEnumerable(); } } }