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

时间:2010-08-02 09:12:41

标签: c#

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语句以及为什么这是一个坏主意?

5 个答案:

答案 0 :(得分:15)

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

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

答案 1 :(得分:6)

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

但是,如果您尝试在using之后再次使用它,它将被处理掉,因此您无法知道该实例是否可以安全继续使用,因为dispose不必重置对象状态。此外,如果在构造期间发生异常,它将不会遇到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.

答案 2 :(得分:1)

using背后的想法是define a scope, outside of which an object or objects will be disposed

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

我写了一些代码以及一些单元测试。当我可以验证有关手头问题的陈述时,我喜欢它。我的发现:

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

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

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

您可能想要了解的另一个项目是某些类可以在 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)) { } } 实现中引发异常。虽然指南不是这样做的,但即使微软也有这种情况,例如:正如所讨论的那样here

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

{{1}}