Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best way of returning constructed IDisposables safely?

Tags:

c#

idisposable

Edit: Two options shown below.

If you're just using the functionality that an IDisposable provides, the aptly named using clause works fine. If you're wrapping an IDisposable in an object, the containing object itself needs to be IDisposable and you need to implement the appropriate pattern (either a sealed IDisposable class, or the messier but standard virtual pattern).

But sometimes a helper factory method is good for cleanliness. If you return an IDisposable directly after construction, you're fine, but if you first construct it and then modify it or otherwise execute code that can throw an exception before returning, you need to safely call .Dispose() - but only if there was an error.

For example, unsafe code could look like this...

DbCommand CreateCommandUnsafely(string commandText)
{
    var newCommand = connection.CreateCommand();
    newCommand.CommandText = commandText;  //what if this throws?
    return newCommand;
}    

Solutions Two safe variants follows...

DbCommand CreateCommandSafelyA(string commandText)
{
    DbCommand newCommand = null;
    bool success = false;
    try    {
        newCommand = connection.CreateCommand();
        newCommand.CommandText = commandText; //if this throws...
        success=true;
        return newCommand;
    } finally{
        if (!success && newCommand != null )
            newCommand.Dispose(); //...we'll clean up here.
    }
}


DbCommand CreateCommandSafelyB(string commandText)
{
    DbCommand newCommand = null;
    try    {
        newCommand = connection.CreateCommand();
        newCommand.CommandText = commandText; //if this throws...
        return newCommand;
    } catch {
        if (newCommand != null)
            newCommand.Dispose(); //...we'll clean up here.
        throw;
    }
}

Safe variant A is just one line longer, but seems to be the idiomatic approach. There don't seem to be any really concise solutions, although some posters below give some lambda-using options that extract the encapsulate this logic.

The code bloat with any of the above safe methods remains, and is particularly aggravating with code that originally looked like...

return new MyDisposableThing {
    OptionA = "X",
    OptionB = B.Blabla,
    Values = src.Values.Where(priority => priority > 1.0),
};

The above code gets written safely is quite a bit longer and less readable because you can no longer safely use the shortened setter syntax.

like image 850
Eamon Nerbonne Avatar asked Dec 16 '09 15:12

Eamon Nerbonne


People also ask

Can disposable packaging be recycled?

Most of the time, single-use packaging such as disposable containers and cups also cannot be recycled as they are contaminated with food. Unnecessary packaging is not only a drain on resources, it also adds to the production of waste.

What are the five ways of waste management?

The 5 R's: Refuse, Reduce, Reuse, Repurpose, Recycle.


1 Answers

No - I think there isn't a better way.

However, you could write a helper class:

public static class DisposeHelper
{
  public static TDisposable DisposeOnError<TDisposable>(TDisposable dispoable, Action<TDisposable> action)
     where TDisposable : IDisposable
  {
    try
    {
       action(dispoable);
    }
    catch(Exception)
    {
       disposable.Dispose();
       throw;
    }

    return disposable;
  }
}

So you could write:

return DisposeHelper.DisposeOnError(connection.CreateCommand(), cmd => cmd.CommandText = commandText);

I am not sure, however, if that is really a better way.

like image 187
Matthias Avatar answered Nov 15 '22 23:11

Matthias