2009-09-02 19 views
15

using(...)語句是try {} finally {}的語法糖。使用語句和try-catch() - 最終重複?

但是,如果我再有一個using語句象下面這樣:

using (FileStream fs = File.Open(path)) 
{ 


} 

現在我想趕上那打開此文件可能會導致異常(這是相當高的風險代碼,它可能因失敗環境),但如果我寫try-catch內部會不會重複?當代碼被編譯爲IL時,我假設代碼被打亂時重複會被刪除?但是,我想要捕捉異常打開文件可能會導致(所以我應該包裝在使用語句的範圍之外的try-catch),以及我在使用塊內部做的任何異常,所以我應該添加塊內的try-catch。

這看起來好像我爲CLR可能在裏面做了很多重複。 CLR是否添加catch子句?

我的同事爭辯說使用語句很混亂(但這是因爲一行代碼稍微長一點,因爲我編寫代碼很困難,因爲我需要快速更改代碼,並且無法訪問代碼的其他部分基礎)。所述同事不使用使用語句,但在使用語句和try-finally/try-catch-finally之間是否存在任何功能差異?我曾看到過這種情況,WCF服務有一個關於最終使用和返回值(最終有關)的不爲人知的案例。解決方案是使用一個校驗塊。在C#中有這樣的東西嗎?

在另一個說明中,是否實現了非託管資源的IDisposale所有者的所有類型?與朋友的討論指出了否定的答案。 (我也在這個論壇的使用部分閱讀了一些主題,那裏有一些非常好的知識)。

回答

7

如果您確實需要處理某些異常,您可以自己實施該模式。就我個人而言,我仍然覺得它更簡單(更重要的是,更清晰),只是將使用包裝在try/catch塊中。

但是,如果你自己做,確保你做對了。 Using塊也會創建一個匿名範圍塊,以便您的變量能夠儘早獲得收集資格。在Using塊末尾調用的.Dispose()方法只會清理非託管資源,因此您的對象所擁有的任何內存可能會稍長一點。這不太可能是一個大問題,但值得記住以防萬一。

所以,做模式的一種直接適應,你的代碼需要看起來更像是這樣的:

{ 
    FileStream fs; 
    try 
    { 
     fs = File.Open(path); 

    } 
    catch (FileNotFoundException e) { /* ... */ } 
    catch (IOException e) { /* ... */ } 
    catch (Exception e) {/* ... */} 
    finally 
    { 
     if (fs != null) fs.Dispose(); 
    } 
} 

就個人而言,我希望看到Using擴展,以支持CatchFinally塊。由於他們已經對代碼進行了轉換,似乎並不會增加額外的複雜性。

+0

儘管最終使用支持嗎?你在哪裏發現了使用匿名範圍塊的使用方法?我想更多地瞭解這一點。所以當我在一個使用塊(例如FileSream.Open())中打開一個文件時,這個異常就會冒泡。如果using語句實現try/finally,那麼我必須在try/catch中包裝它才能獲得該catch。 – dotnetdev 2009-10-08 22:44:03

4

如果您需要明確處理在聲明中可能發生的不同例外情況,您可以用try/catch/finally代替using,並在終端中明確呼叫Dispose()。或者您可以在using區塊周圍放置一個try/catch以區分特殊情況和確保處置。

only thing using確保即使在塊內引發異常,也會調用Dispose()。這是一般的try/[catch]/finally結構的非常有限的,高度具體的實現。

重要的是,這些選項都沒有任何實際影響 - 只要它滿足您的需求,可讀性和可理解性,誰關心?這不是一個額外的嘗試將是一個瓶頸或任何東西!

要回答您的最後一個問題 - 不,IDisposable絕對不一定意味着實施人員已處理非託管資源。這是一個比這更簡單,更通用的模式。這裏有一個有用的例子:

public class Timer : IDisposable 
{ 
    internal Stopwatch _stopwatch; 
    public Timer() 
    { 
     this._stopwatch = new Stopwatch(); 
     this._stopwatch.Start(); 
    } 

    public void Dispose() 
    { 
     this._stopwatch.Stop(); 
    } 
} 

我們可以利用這個時間的事情,而無需顯式依賴於啓動和停止使用被稱爲:

using(Timer timer = new Timer()) 
{ 
    //do stuff 
} 
4

使用和嘗試,終於之間最大的區別是該使用只會調用Dispose()方法。如果你實現自己的finally塊,可以做其他邏輯,例如日誌記錄,這將不會包含在使用中。

+0

+1 for shortness – 2012-06-01 12:50:17

7

我希望保留使用語句並將其包裝在try/catch中。外部try/catch將捕獲需要注意的異常,同時忽略Dispose()。如果稍後將try/catch移動到其他地方(如在調用函數中),則可以保護您免受自己的攻擊。

至於你關於IDisposable的問題:任何人都可以因爲他們喜歡的任何原因來實現它。沒有技術理由將其限制在非託管資源上。 (是否它應該被限制在的非託管資源你的代碼是一個不同的問題)。

3

using構造是try/finally塊的簡寫。也就是說,編譯器生成:

FileStream fs = null; 
try 
{ 
    fs = File.Open(path); 
    // ... 
} 
finally 
{ 
    if (fs != null) 
     fs.Dispose(); 
} 

因此,它是適當的使用using如果你不需要catch,但如果這樣做,那麼你應該只使用一個正常try/catch/finally

+2

在'try' /'catch'中使用''本身並沒有什麼錯誤。它比'finally'更清晰,因爲立即明白在block退出後釋放哪些資源,而不必看'finally'。 – 2009-09-02 21:03:22