HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

AsyncLazy disposal

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
disposalasynclazystackoverflow

Problem

I've made this extension method. Its purpose is to trigger the disposal of a value, stored in a Nito.AsyncEx AsyncLazy, as authored by @StephenCleary.

Is this an approach that makes sense? Is the code good?

using System;
using System.Threading.Tasks;

using Nito.AsyncEx;

/// 
/// Extensions to types in the  namespace.
/// 
public static class NitoAsyncEx
{
    /// 
    /// Disposes the specified  value.
    /// 
    /// The type of the lazy value.
    ///  The asynchronous lazy.
    /// When disposal is complete.
    public static async Task Dispose(
            this AsyncLazy asyncLazy) where T : IDisposable
    {
        if (asyncLazy.Id != 0)
        {
            (await asyncLazy).Dispose();
        }
    }
}

Solution

It's potentially dangerous to only conditionally dispose of the resource. If you don't dispose it if the resource was never initialized in the first place then you run the risk of some code actually initializing it later, and then not disposing of that resource.

The safer design would be to wrap the object, rather than just having this one extension method.

public class DisposableAsyncLazy : IDisposable
    where T : IDisposable
{
    private AsyncLazy lazy;
    private bool disposed = false;
    public DisposableAsyncLazy(Func factory)
    {
        lazy = new AsyncLazy(factory);
    }
    public DisposableAsyncLazy(Func> factory)
    {
        lazy = new AsyncLazy(factory);
    }
    public int Id
    {
        get
        {
            return lazy.Id;
        }
    }
    public async Task GetValue()
    {
        if (!disposed)
            return await lazy;
        else
            throw new ObjectDisposedException("DisposableAsyncLazy");
    }
    public void Start()
    {
        if (!disposed)
            lazy.Start();
        else
            throw new ObjectDisposedException("DisposableAsyncLazy");
    }

    public async void Dispose()
    {
        disposed = true;
        if (lazy.Id != 0)
            (await lazy).Dispose();

    }
}


(Note you can make DisposableAsyncLazy awaitable, rather than having a method/property produce a task, if you want to take the time to do so.)

Code Snippets

public class DisposableAsyncLazy<T> : IDisposable
    where T : IDisposable
{
    private AsyncLazy<T> lazy;
    private bool disposed = false;
    public DisposableAsyncLazy(Func<T> factory)
    {
        lazy = new AsyncLazy<T>(factory);
    }
    public DisposableAsyncLazy(Func<Task<T>> factory)
    {
        lazy = new AsyncLazy<T>(factory);
    }
    public int Id
    {
        get
        {
            return lazy.Id;
        }
    }
    public async Task<T> GetValue()
    {
        if (!disposed)
            return await lazy;
        else
            throw new ObjectDisposedException("DisposableAsyncLazy");
    }
    public void Start()
    {
        if (!disposed)
            lazy.Start();
        else
            throw new ObjectDisposedException("DisposableAsyncLazy");
    }

    public async void Dispose()
    {
        disposed = true;
        if (lazy.Id != 0)
            (await lazy).Dispose();

    }
}

Context

StackExchange Code Review Q#61947, answer score: 4

Revisions (0)

No revisions yet.