views:

412

answers:

8
+13  Q: 

Using vs lambda.

Is it equivalent?

        public static void Using<T>(this T disposable, Action<T> action)
            where T:IDisposable
        {
            try {
                action(disposable);
            }
            finally {
                disposable.Dispose();
            }
        }

        new SqlConnection("").Using(conn => {

        });

        using(var conn = new SqlConnection("")){

        };

in other words, can we replace using keyword with this method?

+17  A: 

I don't think this is a particularly good idea, because it would also allow us to write

var conn = new SqlConnecction("");
conn.Using(c => /* do something with c */);
conn.Open();

This would compile, but when you reach the last line, it will throw an ObjectDisposedException.

In any case, the using coding idiom is well-known, so why make it harder for your fellow developers to read your code?

Mark Seemann
That can also happen with a "classic" `using` statement: `var conn = ...; using(conn) {...}; conn.Open();`
Heinzi
I consider this replacement only in theory, as using Occam's razor principle.
dotneter
@Heinzi That's why we enforce in our coding standards that the object in a using clause is also defined in the using clause - i.e. `using( IDbConnection con = ... ) { ...}`
Rowland Shaw
@RowlandShaw: Sometimes that is not possible, e.g. before using block (and resource allocation )needed to access the variable (to create a closure which would be passed to the resource).
Richard
+2  A: 

One limitation: you cannot acces out parameters of the containing method in your lambda.

Henrik
+2  A: 

The using statement checks for the disposable being null. Other than that at runtime you will get mostly the same behaviour (other than the additional stack frame, and the delegate call).

Richard
+1  A: 

As Richard said, there is an extra method call between creation of the disposable object and the start of the try. This increases the time frame in which a asynchronous exception can take place, which leads to your disposable object not being disposed. However, this isn't a big issue, because types that implement IDisposable should not acquire their resources in the constructor (SqlConnection aquires its resources during the call to the Open() method).

So while your new way of writing the using statement would work, it makes reading for other developers much harder and could easily be abused in the way Mark Seemann notes.

So can you replace the using keyword with your Using extension method? Yes. Should you do it? No.

Steven
+2  A: 

Some reasons why I'd rather not use this.

Compile-Time foolproofing

using (var foo=new Bar())
{
  foo=new Bar();// throws an error at compile time
}

Structs implementing IDisposable

Structs implementing interfaces are boxed when an interface method is invoked on them. The using statement is smart enough to recognize when its target is a struct at compile time and forgoes the boxing.

Florian Doyon
Technically, the bit about structs is actually a compiler bug. The spec says to do the boxing conversion; the implementation does not do so; it just calls Dispose() directly. This is arguably the "more correct" thing to do anyway; if the disposer mutates the struct, then it probably should be mutating the original, not a boxed copy.
Eric Lippert
It's not a bug, it's a feature of future CLR versions then ;)
Florian Doyon
+9  A: 

I note that your method does not do everything that a "using" statement does. For example, it does not introduce a new local variable declaration space in which a variable that holds on to the resource may be declared.

Nor does it allow for multiple disposable resources of the same type to be declared all at one go.

And finally, it does not seem to compose particularly elegantly with itself. One of the nice things about "using" as a statement is that you can say:

using(something)
using(someotherthing)
using(somethirdthing)
{
   use all three, they'll all get disposed
}

How would that look in your proposal?

something.Using(
x=>someotherthing.Using(
y=>somethirdthing.Using(
z=> { use all three } )));

A bit gross, frankly.

Eric Lippert
A: 

It's almost functionally equivalent, but practically speaking it does not provide any benefit. The semantics of the "traditional" using statement are well known and standard. While your approach may behave the same (although there are some differences) it introduces unnecessary overhead and complexity - not only do you have additional method invocations you are replicating code yourself that the compiler will automatically generate.

Scott Dorman
A: 

No. You are demoting a language keyword [meaning compiler can do somethings around it as pointed out by Florian] to a normal method call.

Although semantically they may be the same, compiler won't know what the method will do. Compile time checks are better than runtime exceptions, as we all know.

Fakrudeen