开发者

Can an object be declared above a using statement instead of in the brackets

Most of the examples of the using statement in C# declare the object inside the brackets like this:

using (SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", connection))
{
   // Code goes here
}

What happens if I use the using statement in the following way with the object declared outside the using statement:

SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", connection);
using (cmd)
{
   // Code goes here
}

Is it a bad idea to use the using stat开发者_Go百科ement in the way I have in the second example and why?


Declaring the variable inside the using statement's control expression limits the scope of the variable to inside the using statement. In your second example the variable cmd can continue to be used after the using statement (when it will have been disposed).

Generally it is recommended to only use a variable for one purpose, limiting its scope allows another command with the same name later in scope (maybe in another using expression). Perhaps more importantly it tells a reader of your code (and maintenance takes more effort than initial writing) that cmd is not used beyond the using statement: your code is a little bit more understandable.


Yes, that is valid - the object will still be disposed in the same manner, ie, at the end and if execution flow tries to leave the block (return / exception).

However if you try to use it again after the using, it will have been disposed, so you cannot know if that instance is safe to continue using as dispose doesn't have to reset the object state. Also if an exception occurs during construction, it will not have hit the using block.

I'd declare and initialize the variable inside the statement to define its scope. Chances are very good you won't need it outside the scope if you are using a using anyway.

MemoryStream ms = new MemoryStream(); // Initialisation not compiled into the using.

using (ms) { } 

int i = ms.ReadByte(); // Will fail on closed stream.

Below is valid, but somewhat unnecessary in most cases:

MemoryStream ms = null;

using (ms = new MemoryStream())
{ }

// Do not continue to use ms unless re-initializing.


I wrote a little code along with some unit tests. I like it when I can validate statements about the question at hand. My findings:

  • Whether an object is created before or in the using statement doesn't matter. It must implement IDisposable and Dispose() will be called upon leaving the using statement block (closing brace).
  • If the constructor throws an exception when invoked in the using statement Dispose() will not be invoked. This is reasonable as the object has not been successfully constructed when an exception is thrown in the constructor. Therefore no instance exists at that point and calling instance members (non-static members) on the object doesn't make sense. This includes Dispose().

To reproduce my findings, please refer to the source code below.

So bottom line you can - as pointed out by others - instantiate an object ahead of the using statement and then use it inside the using statement. I also agree, however, moving the construction outside the using statement leads to code that is less readable.

One more item that you may want to be aware of is the fact that some classes can throw an exception in the Dispose() implementation. Although the guideline is not to do that, even Microsoft has cases of this, e.g. as discussed here.

So here is my source code include a (lengthy) test:

  public class Bar : IDisposable {
     public Bar() {
        DisposeCalled = false;

     }
     public void Blah() {
        if (DisposeCalled) {
           // object was disposed you shouldn't use it anymore
           throw new ObjectDisposedException("Object was already disposed.");
        }
     }

     public void Dispose() {
        // give back / free up resources that were used by the Bar object
        DisposeCalled = true;
     }

     public bool DisposeCalled { get; private set; }
  }

  public class ConstructorThrows : IDisposable {
     public ConstructorThrows(int argument) {
        throw new ArgumentException("argument");
     }

     public void Dispose() {
        Log.Info("Constructor.Dispose() called.");
     }
  }

  [Test]
  public void Foo() {
     var bar = new Bar();
     using (bar) {
        bar.Blah(); // ok to call
     }// Upon hitting this closing brace Dispose() will be invoked on bar.

     try {
        bar.Blah(); // Throws ObjectDisposedException
        Assert.Fail();
     }
     catch(ObjectDisposedException) {
        // This exception is expected here
     }

     using (bar = new Bar()) { // can reuse the variable, though
        bar.Blah(); // Fine to call as this is a second instance.
     }

     // The following code demonstrates that Dispose() won't be called if 
     // the constructor throws an exception:
     using (var throws = new ConstructorThrows(35)) {

     }
  }


The idea behind using is to define a scope, outside of which an object or objects will be disposed.

If you declare the object you are about to use inside using in advance, there's no point to use the using statement at all.


It has been answered and the answer is: Yes, it's possible.
However, from a programmers viewpoint, don't do it! It will confuse any programmer who will be working on this code and who doesn't expect such a construction. Basically, if you give the code to someone else to work on, that other person could end up being very confused if they use the "cmd" variable after the using. This becomes even worse if there's even more lines of code between the creation of the object and the "using" part.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜