12

Given a method

public static bool Connection.TryCreate(out Connection connection) {}

And a piece of calling code:

Connection connection;
if (!Connection.TryCreate(out connection))
    // handle failure gracefully.

/*
 * work with connection
 *
 * …
 *
 */

connection.Dispose();

I'm using the same pattern as bool.TryParse and friends, i.e. TryCreate returns whether the operation was successful.

I realize the using() variable needs to be read-only within its block, but is there a way to turn the above into a using() {} block (TryCreate only sets it once), like so:

using (Connection connection)
{
    if (!Connection.TryCreate(out connection))
        // this would leave the using() block prematurely

    /*
     * work with sconnection
     *
     * …
     *
     */
}

(This doesn't compile:

error CS1657: Cannot pass 'connection' as a ref or out argument because it is a 'using variable'

)

Sören Kuklau
  • 19,454
  • 7
  • 52
  • 86
  • Did you do a simple test with NULL/non-NULL values for `connection` already? Did it compile? Did it run? – Uwe Keim Nov 19 '11 at 18:28
  • Yeah, sorry, I should have mentioned that my fictitious example wouldn't compile. I've amended the post accordingly. – Sören Kuklau Nov 19 '11 at 18:33

5 Answers5

8

No, that is not possible.

The using (x) {...} construct makes a copy of x when it enters the block, so you can do this:

var x = new FileStream(...);
using (x)
{
    x = null;
}

The stream will still be disposed when the using block ends.

The corollary is that this won't work either:

Stream x = null;
using (x)
{
    x = new FileStream(...);
}

here the stream you construct inside the using block will not be disposed.

What you can do, however, is this:

Connection connection;
if (Connection.TryCreate(out connection))
    using (connection)
    {
    }

In C# 7.0 and onwards you can combine this with "out variables" to form:

if (Connection.TryCreate(out var connection))
    using (connection)
    {
    }
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
4

Looks like a bad use of the Try* pattern (some would argue this is an anti-pattern).

Instead of a TryCreate, just have a Create method that throws an exception if not successful and that returns the created connection.

Then you could do the usual:

using(Connection connection = Connection.Create())
{
}

Alternatively, if you want to avoid an exception being thrown and the required try{}catch{}, have the Create method return null when a connection could not be created and test for that.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • Arguably, a TryCreate that sometimes returns null would also serve – Marc Gravell Nov 19 '11 at 18:34
  • @MarcGravell - Possibly. But the semantics of `Try*` (at least in the BCL) would be to return a success boolean. Or do you mean the `out` parameter? – Oded Nov 19 '11 at 18:36
  • I don't see how this is any different than using `bool.TryParse()` over `bool.Create()`, or Uri.TryCreate over `new Uri()`. It avoids dealing with exceptions, which may sometimes be desirable. – Sören Kuklau Nov 19 '11 at 18:36
  • I meant a regular return, non-null on success, else null – Marc Gravell Nov 19 '11 at 18:37
  • ↑↑↑ It *does* return success (or failure, as the case may be). – Sören Kuklau Nov 19 '11 at 18:37
  • @MarcGravell - Absolutely agree - returning a `null` is a good alternative. – Oded Nov 19 '11 at 18:43
  • @Oded - I assumed that is what it did when I wrote my wrong answer -- This is because the `Try*` used in Parse does not make sense for a Create operation IMHO. – Hogan Nov 19 '11 at 18:48
  • @Hogan - I agree, which is why my answer steers clear from a `Try` method. – Oded Nov 19 '11 at 18:50
  • Having a factory "try" method is entirely reasonable. The all-to-common pattern of having a "try" method return the created object as an out-reference parameter, however, seems much more dubious. An interface `IVehicleFactory` which defines a method `bool TryBuildCarToSpecs(CarSpects theSpecs, out T result)` can only be used by code that wants to build something of precise type T. By contrast, if the function were e.g. `T TryBuildCarToSpecs(CarSpecs theSpecs, ref BuildProblemReport faults)` the interface could be declared covariant, and code which wanted an `IVehicleFactory` could... – supercat Nov 23 '11 at 18:31
  • ...perfectly happily accept an `IVehicleVactory` without any problem. If the factory is returning a class type, and if no problem indication is needed beyond pass/fail, one could leave off the 'faults' parameter and simply return 'null' in the problem case. If the factory produces something that's possibly a value type, but pass/fail reporting is adequate, the 'ref' parameter could be of type 'bool.' – supercat Nov 23 '11 at 18:36
3

You can do it like this:

Connection connection;
if (Connection.TryCreate(out connection))
{
    using (connection)
    {
        …
    }
}

But it might be better if you just returned null on failure:

using (Connection connection = Connection.Create())
{
    if (connection != null)
    {
        …
    }
}

The finally block that is created by the using checks whether connection is null and doesn't do anything if it is.

Also, if you're not declaring the variable in the using, then it doesn't have to be read-only.

svick
  • 236,525
  • 50
  • 385
  • 514
  • 1
    From MSDN - [using Statement](http://msdn.microsoft.com/en-us/library/yh598w02.aspx): `You can instantiate the resource object and then pass the variable to the using statement, but this is not a best practice.` – Oded Nov 19 '11 at 18:32
  • Yeah, but I think it's the best solution, if you don't want to modify `TryCreate()` for some reason. – svick Nov 19 '11 at 18:35
  • What happens when TryCreate fails? – Hogan Nov 19 '11 at 18:36
  • @Hogan, I forgot about that at first. The necessary `if` is there now. – svick Nov 19 '11 at 18:39
  • That compiles, but does it really guarantee that the resources are cleaned up in all cases? – Sören Kuklau Nov 19 '11 at 18:40
  • I'll accept the answer — the first part, that is. The second part seems bad in that, if connection *does* end up being `null`, the code would silently do nothing, and I wouldn't even be able to check for `null` since the block would never get entered. – Sören Kuklau Nov 19 '11 at 18:43
  • @SörenKuklau, of course not. [There are several corner cases when `using` doesn't call `Dispose()`](http://stackoverflow.com/questions/7595372/is-there-a-situation-in-which-dispose-wont-be-called-for-a-using-block), like a power failure or uncatchable exception. But I think the only thing that can happen here (except for the corner cases) is something like `ThreadAbortException`. – svick Nov 19 '11 at 18:46
  • @SörenKuklau, in the second case, if you want to react to failure, just add `else` to the `if`. – svick Nov 19 '11 at 18:47
  • Ah, I misunderstood. So it *does* enter the `using` block, but with a null object. Never mind. – Sören Kuklau Nov 19 '11 at 18:54
1

No. If you are concerned about exceptions in the gap between a method call and the using, you could use try/finally:

Connection conn = null;
try {
    if(!conn.TryCreate(out conn)) return;
    ...
} finally {
    if(conn != null) conn.Dispose();
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
0

A step sideways ?

public class ConnectTo : IDisposable
{

  public Connection CurrentConnection {get; private set;}

  public ConnectTo()
  {
    CurrentConnection = null;
    // Connect up to whatever.
  }


  #region IDisposable

  // Blah blah

  #endregion
}

then

using( ConnectedTo conn = new ConnectTo())
{
  if (conn.CurrentConnection != null)
  {
    //Do Stuff
  }
}
Tony Hopkinson
  • 20,172
  • 3
  • 31
  • 39