With... End With vs Using in VB.NET

yu_ominae picture yu_ominae · Mar 21, 2013 · Viewed 17k times · Source

I just found out that like C#, VB.NET also has the using keyword.

Until now I thought it didn't have it (stupid of me, I know...) and did stuff like this instead:

With New OleDbConnection(MyConnectionString)
   ' Do stuff
End With

What are the implications of this compared to doing it with a using statement like this

Using cn as New OleDBConnection(MyConnectionString)
    With cn
        ' Do stuff with cn
    End With
End using

UPDATE:

I should add that I am familiar with what the using statement does in that it disposes of the object when the construct is exited.

However, as far as I understand the With New ... construct will have the object mark the object as ready for garbage collection when it goes out of scope.

So my question really is, is the only difference the fact that with using I will release the memory right away, whereas with the With construct it will be released whenever the GC feels like it? Or am I missing something bigger here?

Are there any best practice implications? Should I go and rewrite all the code with I used With New MyDisposableObject() ... End With as Using o as New MyDisposableObject()?

Answer

Cody Gray picture Cody Gray · Mar 21, 2013

With Statements/Blocks

However, as far as I understand the With New ... construct will have the object mark the object as ready for garbage collection when it goes out of scope.

This is both true and not true. It is true in the sense that all objects are "marked" (purists might quibble with this terminology, but the details are not relevant) as ready for garbage collection when they go out of scope. But then in that sense, it is also not entirely true, as there's nothing special about the With keyword with respect to this behavior. When an object goes out of scope, it is eligible for garbage collection. Period. That's true for method-level scope and it's true for block-level scope (e.g., With, For, Using, etc.).

But that's not why you use With. The reason is that it allows you to set multiple properties in sequence on a deeply-nested object. In other words, assume you have an object on which you want to set a bunch of properties, and you access it this way: MyClass.MemberClass.AnotherMemberClass.Items(0). See all those dots? It can (theoretically) become inefficient to write code that has to work through that series of dots over and over to access the exact same object each time you set a property on it. If you know anything about C or C++ (or any other language that has pointers), you can think of each of those dots as implying a pointer dereference. The With statement basically goes through all of that indirection only once, storing the resulting object in a temporary variable, and allowing you to set properties directly on that object stored in the temporary variable.

Perhaps some code would help make things clearer. Whenever you see a dot, think might be slow!

Assume that you start out with the following code, retrieving object 1 from a deeply-nested Items collection and setting multiple properties on it. See how many times we have to retrieve the object, even though it's exactly the same object every time?

MyClass.MemberClass.AnotherMemberClass.Items(0).Color   = Blue
MyClass.MemberClass.AnotherMemberClass.Items(0).Width   = 10
MyClass.MemberClass.AnotherMemberClass.Items(0).Height  = 5
MyClass.MemberClass.AnotherMemberClass.Items(0).Shape   = Circle
MyClass.MemberClass.AnotherMemberClass.Items(0).Texture = Shiny
MyClass.MemberClass.AnotherMemberClass.Items(0).Volume  = Loud

Now we modify that code to use a With block:

With MyClass.MemberClass.AnotherMemberClass.Items(0)
    .Color   = Blue
    .Width   = 10
    .Height  = 5
    .Shape   = Circle
    .Texture = Shiny
    .Volume  = Loud
End With

The effect here, however, is identical to the following code:

Dim tempObj As MyObject = MyClass.MemberClass.AnotherMemberClass.Items(0)
tempObj.Color   = Blue
tempObj.Width   = 10
tempObj.Height  = 5
tempObj.Shape   = Circle
tempObj.Texture = Shiny
tempObj.Volume  = Loud

Granted, you don't introduce a new scope, so tempObj won't go out of scope (and therefore be eligible for garbage collection) until the higher level scope ends, but that's hardly ever a relevant concern. The performance gain (if any) attaches to both of the latter two code snippets.

The real win of using With blocks nowadays is not performance, but readability. For more thoughts on With, possible performance improvements, stylistic suggestions, and so on, see the answers to this question.

With New?

Adding the New keyword to a With statement has exactly the same effect that we just discussed (creating a local temporary variable to hold the object), except that it's almost an entirely pointless one. If you need to create an object with New, you might as well declare a variable to hold it. You're probably going to need to do something with that object later, like pass it to another method, and you can't do that in a With block.

It seems that the only purpose of With New is that it allows you to avoid an explicit variable declaration, instead causing the compiler to do it implicitly. Call me crazy, but I see no advantage in this.

In fact, I can say that I have honestly never seen any actual code that uses this syntax. The only thing I can find on Google is nonsense like this (and Call is a much better alternative there anyway).

Using Statements/Blocks

Unlike With, Using is incredibly useful and should appear frequently in typical VB.NET code. However, it is very limited in its applicability: it works only with objects whose type implements the IDisposable interface pattern. You can tell this by checking to see whether the object has a Dispose method that you're supposed to call whenever you're finished with it in order to release any unmanaged resources.

That is, by the way, a rule you should always follow when an object has a Dispose method: you should always call it whenever you're finished using the object. If you don't, it's not necessarily the end of the world—the garbage collector might save your bacon—but it's part of the documented contract and always good practice on your part to call Dispose for each object that provides it.

If you try to wrap the creation of an object that doesn't implement IDisposable in a Using block, the compiler will bark at you and generate an error. It doesn't make sense for any other types because its function is essentially equivalent to a Try/Finally block:

Try
    ' [1: Create/acquire the object]
    Dim g As Graphics = myForm.CreateGraphics()
    
    ' [2: Use the object]
    g.DrawLine(Pens.Blue, 10, 10, 100, 100)
    ' ... etc.
End Try
Finally
    ' [3: Ensure that the object gets disposed, no matter what!]
    g.Dispose()
End Finally

But that's ugly and becomes rather unwieldy when you start nesting (like if we'd created a Pen object that needed to be disposed as well). Instead, we use Using, which has the same effect:

' [1: Create/acquire the object]
Using g As Graphics = myForm.CreateGraphics()
    ' [2: Use the object]
    g.DrawLine(Pens.Blue, 10, 10, 100, 100)
    ' ...etc.
End Using  ' [3: Ensure that the object gets disposed, no matter what!]

The Using statement works both with objects you are first acquiring (using the New keyword or by calling a method like CreateGraphics), and with objects that you have already created. In both cases, it ensures that the Dispose method gets called, even if an exception gets thrown somewhere inside of the block, which ensures that the object's unmanaged resources get correctly disposed.

It scares me a little bit that you have written code in VB.NET without knowing about the Using statement. You don't use it for the creation of all objects, but it is very important when you're dealing with objects that implement IDisposable. You definitely should go back and re-check your code to ensure that you're using it where appropriate!