2010-08-02 34 views
17

大部分在C#中使用語句的例子聲明括號內的對象是這樣的:可以將對象上方using語句,而不是在括號內聲明

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

如果我用用,會發生什麼

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

它是一個壞主意,使用using語句的方式,我在第二個例子,爲什麼:在與物體的下面的方式聲明using語句外聲明?

回答

15

在using語句的控制表達式中聲明變量會將變量的作用域限制在using語句中。在你的第二個例子中,變量cmd可以在using語句之後繼續使用(當它被處置時)。

通常建議只爲一個目的使用一個變量,限制它的作用域允許另一個具有相同名稱的命令稍後在作用域中(可能在另一個使用表達式中)。也許更重要的是它會告訴讀者你的代碼(並且維護需要比初始寫入更多的努力),cmd不在using語句之外使用:你的代碼更容易理解。

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. 
+0

它仍然是可用的'using'後。 – Anax 2010-08-02 09:22:12

+2

它將可用但不可用 - 它的內部狀態將被處置。你不知道內部狀態是什麼,或者配置如何影響類的實例,所以它不是一個安全的過程。我沒有說它不可用,-1不適用。 – 2010-08-02 09:24:16

+0

我相信,即使構造函數在using塊中執行,如果構造函數拋出,Dispose也不會被調用。 – Henrik 2010-08-02 09:36:40

1

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

如果您事先聲明要在using之內使用的對象,根本沒有必要使用using語句。

+1

從技術上講,仍然有一點,因爲它仍然會正確地處理對象。 *初始化*在使用塊外部可能會很危險,因爲這不會被使用塊的行爲所覆蓋。 – 2010-08-02 09:27:59

1

已經得到回答,答案是:是的,這是可能的。
但是,從程序員的角度來看,不這樣做!它會混淆任何程序員誰將在這個代碼上工作,誰不期望這樣的結構。基本上,如果你把代碼交給其他人來處理,那麼如果他們使用之後的「cmd」變量,那麼其他人可能會非常困惑。如果在創建對象和「使用」部分之間存在更多的代碼行,這會變得更糟。

1

我寫了一些代碼以及一些單元測試。當我可以驗證關於手頭問題的陳述時,我喜歡它。我的發現:

  • 是否在using聲明之前創建對象或無關緊要。它必須實現IDisposableDispose()將在離開使用語句塊(右大括號)時被調用。
  • 如果構造函數在使用語句Dispose()中調用時拋出異常將不會被調用。這是合理的,因爲在構造函數中引發異常時,對象沒有成功構建。因此,此時不存在實例,並且調用該對象上的實例成員(非靜態成員)是沒有意義的。這包括Dispose()

要重現我的發現,請參閱下面的源代碼。

因此,您可以 - 就像其他人指出的那樣 - 在using語句之前實例化一個對象,然後在using語句中使用它。然而,我也同意,將構造移到使用語句之外會導致代碼不易讀。

您可能想要知道的另外一件事是某些類可能會在Dispose()實現中拋出異常。儘管指導原則並非如此,但即使是微軟也有這種情況,例如如here所述。

因此,這裏是我的源代碼包括:(長篇)測試:

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

    } 
    } 
+0

如果構造函數拋出,則引用永遠不會分配給該變量 - 不會引用通過其調用「Dispose」的對象。 – Richard 2010-08-02 18:52:38

相關問題