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();
}
}
}