Skip to content

Convenience methods for creating Owned<T> instances #156

@jnm2

Description

@jnm2

Once a lot of a codebase has necessarily taken a dependency on Owned<T> so that StrongInject can wire everything up, many places remain that need to create their own Owned instances. This is handy for many tests and smaller executables in a project that don't really need StrongInject and can do their own wiring with a few calls.

Instead of: new Owned<SomePotentiallyLongTypeName>(value, () => { })
Friendly: Owned.WithoutDisposal(value)

Instead of: new Owned<SomePotentiallyLongTypeName>(value, value.Dispose)
Friendly: Owned.WithDisposal(value)

Instead of: new Owned<T>(value, () => (value as IDisposable)?.Dispose())
Friendly: Owned.WithDisposal(value)

Instead of: new Owned<SomeType>(value, () => { value.Dispose(); dependency1.Dispose(); dependency2.Dispose(); })
Friendly: Owned.WithDisposal(value, dependency1, dependency2) or
Friendly: Owned.WithDisposal(value, alsoDispose: new[] { dependency1, dependency2 })

Here's a rough draft for what I've already found useful, and I'm happy to do the implementation:

namespace StrongInject;

public static class Owned
{
    /// <summary>
    /// Nothing happens when the <see cref="Owned{T}"/> is disposed, even if <paramref name="value"/>
    /// is disposable.
    /// </summary>
    public static Owned<T> WithoutDisposal<T>(T value)
    {
        return new(value, dispose: null);
    }

    /// <summary>
    /// <para>
    /// When the <see cref="Owned{T}"/> is disposed, <paramref name="value"/> is disposed. If
    /// <paramref name="value"/> does not implement <see cref="IDisposable"/>, the behavior is
    /// the same as <see cref="WithoutDisposal{T}(T)"/>.
    /// </para>
    /// </summary>
    public static Owned<T> WithDisposal<T>(T value)
    {
        // T is not constrained to IDisposable. If it was, call sites using generic types would be penalized.
        return new(value, dispose: value is IDisposable disposable
            ? disposable.Dispose
            : null);
    }

    /// <summary>
    /// <para>
    /// When the <see cref="Owned{T}"/> is disposed, <paramref name="value"/> is disposed first and then
    /// the items in <paramref name="alsoDispose"/> are disposed in the order they appear.
    /// </para>
    /// <para>
    /// If <paramref name="value"/> does not implement <see cref="IDisposable"/>, the items in
    /// <paramref name="alsoDispose"/> will still be disposed.
    /// </para>
    /// </summary>
    public static Owned<T> WithDisposal<T>(T value, params IDisposable[] alsoDispose)
    {
        return new(value, dispose: () =>
        {
            (value as IDisposable)?.Dispose();

            foreach (var disposable in alsoDispose)
                disposable.Dispose();
        });
    }
}

An AsyncOwned static class would be similar.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions