1

Suppose I have a class DisposableObject which implements IDisposable. There is a risk it can throw an exception from any constructor and from the DoStuff function. I want to do something like this, so that no matter what happens, the object is disposed properly:

edit: I would like to avoid duplicate code, so a using inside each if/else block is not desirable (the code is a lot more complex than here)

edit2: Yes the try/finally error was wrong, changed it to use of unassigned local variable.

using (DisposableObject worker)
{
    if(/*condition*/)
        worker = new DisposableObject(/*args*/)
    else
        worker = new DisposableObject(/*other args*/)
    worker.DoStuff();
}

But I can't, because the compiler says I must provide an initializer in the using statement. I can't use try/finally either:

DisposableObject worker;
try
{
    if(/*condition*/)
        worker = new DisposableObject(/*args*/)
    else
        worker = new DisposableObject(/*other args*/)
    worker.DoStuff();
}
finally
{
    worker.Dispose();
}

The compiler says use of unassigned local variable 'worker'.

So what can I do? Obviously I can't simply go like this...

DisposableObject worker;
if (/*condition*/)
    worker = new DisposableObject(/*args*/)
else
    worker = new DisposableObject(/*other args*/)
worker.DoStuff();
worker.Dispose();

Because I can't be sure the Dispose() function is ever reached.

5
  • I'm not sure how the compiler could throw such an error for your try/finally example, since the variable is declared (albeit not initialized until in the try block). What's your actual code? Commented Feb 21, 2012 at 15:30
  • What BoltClock pointed out, your try/finally code should be working. Try adding a catch { } in there and see if the problem persists. Commented Feb 21, 2012 at 15:32
  • constructors that throw exceptions are very evil. Is there any way to refactor the code so that this doesn't happen. Also if a instance is so broken that it failed to initialize, why do you trust it enough to want to try to dispose it? Commented Feb 21, 2012 at 15:33
  • @AakashM you are correct, I changed it to use of unassigned local variable. Commented Feb 21, 2012 at 15:34
  • @BoltClock exact error would be "use of unassigned local variable" he's just missing an DisposableObject worker = null; instantiation outside the try/finally Commented Feb 21, 2012 at 15:36

6 Answers 6

7

Actually, the compiler says that you are using a potentially uninitialized variable, so just initialize it:

DisposableObject worker = null;
try
{
    if(/*condition*/)
        worker = new DisposableObject(/*args*/)
    else
        worker = new DisposableObject(/*other args*/)
    worker.DoStuff();
}
finally
{
    if(worker != null)
        worker.Dispose();
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, I changed the message.
I had no idea assigning null was considered initalizing. How about that... :) Thanks.
5

I find it useful to move the creation and usage to separate methods (often using a factory, but private method shown here for sake of brevity):

private DisposableObject CreateWorker() {
  if (condition) return new DisposableObject(/*args*/);
  return new DisposableObject(/*other args*/);
}

This means that a using block can be used (rather than having to intialise to null and use a try... finally):

using (DisposableObject worker = CreateWorker())
{
  worker.DoStuff();
}

NB If the factory method needs to do any work with the created entity after it is instantiated the object should still be disposed and the exception re-thrown:

public DisposableObject Create() 
{
  DisposableObject entity = new DisposableEntity();
  try 
  {
    //Some operations involving entity
  }
  catch
  {
    entity.Dispose();
    throw;
  }
}

Comments

4

There are two fairly straightforward options with using:

{
    DisposableObject worker;
    if(/*condition*/)
        worker = new DisposableObject(/*args*/);
    else
        worker = new DisposableObject(/*other args*/);
    using (worker)
    {
        worker.DoStuff();
    }
}

or

using (DisposableObject worker = /* condition */
    ? new DisposableObject(/*args*/)
    : new DisposableObject(/*other args*/))
{
    worker.DoStuff();
}

Others have given other equally valid possibilities that either avoid using or refactor code, but I believe this is closest to what you have already.

1 Comment

I really like the latter option; very consise. Not sure if it's fitting a much more complicated version of the problem though.
1

How about:

DisposableObject worker = null;
try
{
    if(/*condition*/)
        worker = new DisposableObject(/*args*/)
    else
        worker = new DisposableObject(/*other args*/)

    worker.DoStuff();
}
finally
{
    if (worker != null)
    {
        worker.Dispose();
    }
}

Comments

1

If the arguments are too complex, you can create a wrapper class that contains the arguments. Then create the arguments in another method that contains your complex If/else. From there you can properly use the using block from your first example & you'll be sure that Dispose() is called.

1 Comment

I like the concept. Could be uglier sometimes (or even cleaner), but still another interesting way to address it.
0

you could write a wrapper class around DisposableObject that takes the arguments, and does the try/catch internally somehow and notifies you of the error?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.