Using Statements

From a completely unrelated Google search I came across this post by Fritz Onion over on pluralsight mentioning that he was happy to see Visual Basic 2005 adopting the C# using statement to make resource management a little easier in VB. He went on to concede that it wasn’t quite as elegant as the corresponding C#. Here is the VB example he provided:

Using conn As New SqlConnection(dsn)
  Using cmd As New SqlCommand("SELECT * FROM Employees", conn)
    conn.Open()
      Using rdr As SqlDataReader = cmd.ExecuteReader()
        While rdr.Read()
          Console.WriteLine(rdr(0))
        End While
      End Using
  End Using
End Using

And here is the C# version he compared it to:

using (SqlConnection conn = new SqlConnection(dsn))
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Employees", conn))
{
  conn.Open();
  using (SqlDataReader rdr = cmd.ExecuteReader())
  {
    while (rdr.Read())
       Console.WriteLine(rdr[0]);
  }
}

I can’t help but point out that VB and C# have nothing on C++ when it comes to resource management. Here is the corresponding code written in C++:

SqlConnection conn(dsn);
SqlCommand cmd("SELECT * FROM Employees", %conn);
conn.Open();
msclr::auto_handle<SqlDataReader> rdr(cmd.ExecuteReader());

while (rdr->Read())
{
    Console::WriteLine(rdr->GetValue(0));
}

Not only is this easier to write IMHO, but more importantly the code is correct by default. More on C++ in my upcoming book: Applied Visual C++ 2005. I’ve made a general rule for myself as I write the book that I should stay away from comparing C++ to other languages, with the exception of IL. So this blog helps me to vent.

:)

Don’t get me wrong. C# is a fun language which I’ve been forced to had the pleasure of using a fair amount over the last few years. It certainly has some language and compiler features (in version 2.0) that I wish we had in C++. VB on the other hand… [insert some Don Box crack about a semicolon-starved language].


© 2005 Kenny Kerr

10 Comments

  • How are the resources of the connection and command managed in this case ?

  • Will the VB implemenation support aliasing like the C# implementation?

  • Mischa Kroon: since an object that implements IDisposable is treated as if it has a C++ destructor, the Dispose method for these objects will be called when the variables go out of scope. Similarly to the VB and C# examples, the reader is disposed followed by the command which is followed by the connection. The last object to be created is the first to be destroyed. It follows standard C++ rules here.

  • You can use Imports in VB to import namespaces or define aliases.

  • The corresponding code in C++ needs an extra pair of braces in order to force the desired end of scope:



    { // begin additional scope

    SqlConnection conn(dsn);

    SqlCommand cmd(&quot;SELECT * FROM Employees&quot;, %conn);

    conn.Open();

    msclr::auto_handle&lt;SqlDataReader&gt; rdr(cmd.ExecuteReader());



    while (rdr-&gt;Read())

    {

    Console::WriteLine(rdr-&gt;GetValue(0));

    }

    } // end additional scope



    Just yesterday I had to do something like that for a pinned pointer that I didn't want to keep pinned until the caller exited. I wanted to pin the pointer only until an unmanaged callee exited. But VC++2005beta1 doesn't allow casting a managed pointer to a pinned pointer in a parameter to a function call. I had to add a declaration of a named local (auto) variable. To keep it really local, I put an extra pair of braces around the added variable declaration and the function call.

  • The only advantage that the C# version has over Kenny's C++ version is that the scope of the usage is arguably clearer in the C# version.



    Of course, being an &quot;old C++&quot; developer (and relative C# newbie), I am still somewhat biased to the elegance of the C++ version ;-)

  • Thanks for your comments Norman and Ashley.



    The point behind the C++ design is that you don’t have to worry about when the objects are destroyed/disposed (in most cases) as C++ will do the right thing *by default*. This is in contrast to C# which does the wrong thing by default, requiring the programmer to insert the using statement to achieve correct behavior. There are two cases where you might want to change this default behavior:



    1. When you have exceedingly long functions and keeping the connection open until the function returns is wasteful. The solution is simple and is considered good practice in general: keep functions short, single-purposed and highly focused.



    2. When you want to execute a second command using the same connection but the reader needs to be closed before you can do so. The solution is again simple. Call the reset method on the auto_handle. This will dispose of the reader and allow you to execute a new command.



    Clearly there are exceptions to this and that’s where you can just throw in an extra scope as needed. Most of the time this should not be necessary and you avoid all the indentation and other syntactic overhead witnessed in the VB and C# examples.

  • In one experiment at present I have one function that does a sequence of around 10 calls to Windows APIs. I want this to be 10 C++ statements, each doing one function call. I want to cast the caller's managed pointers to pinned pointer types in parameters in the function calls. But as mentioned, the compiler requires naming local (auto) variables for the pinned pointers.



    One way is to add around 15 pinned pointers at my function's scope, resulting in around 20 added statements (15 declarations can contain initializers, and around 5 of the named variables can be reassigned new values when necessary). But this keeps the objects pinned for the entire duration of my function.



    Another way is to throw in an extra scope around each Windows API call, and declare and assign pinned pointers inside each scope. This adds around 50 statements.



    My function didn't used to be exceedingly long. A sequence of 10 Windows API calls was coherent and reasonable. But now it's 60 statements instead of 10.



    Somehow I think that readability would not be improved by breaking it into 11 separate functions (10 wrappers plus the original 1 to call the 10 wrappers in sequence), plus either duplicating all the parameter passing or adding class member variables for them.



    C++2005 would have something on VB-- and VC# if we didn't have to add extra stuff just to pin an object going to a native function for the duration of the call. But as it is, I don't quite see it, they all seem cumbersome.

  • Don’t take it so personally Norman. I wasn’t talking about your specific example but rather the general problem of resource management and deterministic finalization. If anything I was speaking to Ashley’s point on clarity.



    As for your specific problem, a common solution is to limit the amount of transitions from managed code or data to native code or data. This also improves performance. If you have many values that need to be passed to many native functions consider storing the values in a (separate) data structure, such as a value struct, that can then be pinned and passed to a native function in a single call. The native function can then call the necessary API calls with the addresses of the members of the argument. You would then only need a single pin_ptr. I can provide some examples in a future post on how to deal with interop issues more elegantly if there is interest.



    No language is perfect for every scenario and that is why different languages exist. I have never said that C++ is the only language you’ll ever need, but it sure comes close.



    :)

  • I see, I only need to split off one additional function to wrap the entire sequence 10 calls to Windows APIs. However, this means that the main part of the class has to put a bunch of its variables, presently at class member level, into a new nested structure.



    Besides the joy of what this does for the rest of the code in the class, the original issue still partly remains: all of the data involved in this still get pinned for the entire duration of a function call, even though now it's the split-off function.



    I still think it would be better to allow casting to pinned pointers in parameters to unmanaged function calls, so the pinning remains localized and doesn't require muddying the rest of the code.

Comments have been disabled for this content.