可以在using语句之上而不是在括号中声明对象

C#中using语句的大多数示例都在括号内声明对象,如下所示:

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

如果我使用using语句以下方式使用using语句之外的对象会发生什么:

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

在第二个例子中使用using语句是不是一个坏主意,为什么?

在using语句的控制表达式中声明变量会将变量的范围限制在using语句中。 在第二个示例中,变量cmd可以在using语句之后继续使用(当它将被释放时)。

通常,建议仅将变量用于一个目的,限制其范围允许稍后在范围内使用具有相同名称的另一个命令(可能在另一个使用表达式中)。 也许更重要的是,它告诉读者您的代码(并且维护比初始编写需要更多的努力) cmd不会在using语句之外使用:您的代码更容易理解。

是的,这是有效的 – 对象仍将以相同的方式处理,即在结束时,如果执行流程试图离开块(返回/exception)。

但是,如果您在使用后尝试再次使用它,它将被处理掉,因此您无法知道该实例是否可以安全继续使用,因为dispose不必重置对象状态。 此外,如果在构造期间发生exception,则不会触及using块。

我将在语句中声明并初始化变量以定义其范围。 如果你正在使用一个using ,你很可能不会在范围之外需要它。

 MemoryStream ms = new MemoryStream(); // Initialisation not compiled into the using. using (ms) { } int i = ms.ReadByte(); // Will fail on closed stream. 

以下是有效的,但在大多数情况下有些不必要:

 MemoryStream ms = null; using (ms = new MemoryStream()) { } // Do not continue to use ms unless re-initializing. 

使用背后的想法是定义一个范围,在该范围之外将放置一个或多个对象 。

如果您提前声明要使用的对象,则根本不需要使用using语句。

它已被回答,答案是:是的,这是可能的。
但是,从程序员的角度来看, 不要这样做! 它将使任何将编写此代码并且不期望这样的构造的程序员感到困惑。 基本上,如果您将代码提供给其他人来处理,那么如果他们在使用使用“cmd”变量,那么另一个人可能会非常困惑。 如果在创建对象和“使用”部分之间存在更多代码行,则会变得更糟。

我写了一些代码和一些unit testing。 当我可以validation有关手头问题的陈述时,我喜欢它。 我的发现:

  • 是否在using语句之前或之中创建对象无关紧要。 它必须实现IDisposable并在离开using语句块(右大括号)时调用Dispose() )。
  • 如果构造函数在using语句中调用时抛出exception,则不会调用Dispose() 。 这是合理的,因为在构造函数中抛出exception时尚未成功构造对象。 因此,此时不存在任何实例,并且对象上的调用实例成员(非静态成员)没有意义。 这包括Dispose()

要重现我的发现,请参阅下面的源代码。

因此,您可以 – 如其他人所指出的那样 – 在使用语句之前实例化一个对象,然后在using语句中使用它。 我也同意,但是,在using语句之外移动构造会导致代码的可读性降低。

您可能想要了解的另一个项目是某些类可以在Dispose()实现中抛出exception。 虽然指南不是这样做的,但即使微软也有这种情况,例如这里讨论的。

所以这里是我的源代码包括(冗长)测试:

  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)) { } }