2011-09-17 50 views
11

對處置對象調用方法是否合法?如果是,爲什麼?爲什麼配置對象在使用後不拋出異常?

在下面的演示程序,我已經一次性類A(它實現IDisposable接口)。由於據我所知,如果我通過一次性對象using()構建體,然後Dispose()方法得到在閉合托架自動調用:

A a = new A(); 
using (a) 
{ 
    //... 
}//<--------- a.Dispose() gets called here! 

//here the object is supposed to be disposed, 
//and shouldn't be used, as far as I understand. 

如果這是正確的,那麼請解釋該程序的輸出:

public class A : IDisposable 
{ 
    int i = 100; 
    public void Dispose() 
    { 
     Console.WriteLine("Dispose() called"); 
    } 
    public void f() 
    { 
     Console.WriteLine("{0}", i); i *= 2; 
    } 
} 

public class Test 
{ 
     public static void Main() 
     { 
       A a = new A(); 
       Console.WriteLine("Before using()"); 
       a.f(); 
       using (a) 
       { 
        Console.WriteLine("Inside using()"); 
        a.f(); 
       } 
       Console.WriteLine("After using()"); 
       a.f(); 
     } 
} 

輸出(ideone):

Before using() 
100 
Inside using() 
200 
Dispose() called 
After using() 
400 

如何在a上撥打f()?這是否允許?如果是,那爲什麼?如果不是,那麼爲什麼上面的程序在運行時不會出現異常?


我知道,使用using流行的結構是這樣的:

using (A a = new A()) 
{ 
    //working with a 
} 

但我只是嘗試,這就是爲什麼我寫的不同。

+3

我看到有人錯過了C++中內存管理的確定性本質。 :) – ChaosPandion

+7

所以你說的是:我寫了一個程序,沒有實現一次性對象的合同,當我運行它時,它沒有實現一次性對象的合同。 **您**負責實施該行爲。你沒有這樣做。去這樣做。 –

回答

11

處置並不意味着沒有了。處置僅意味着任何非託管資源(如文件,任何類型的連接...)都已發佈。雖然這通常意味着該對象不提供任何有用的功能,但仍可能存在不依賴於該非託管資源並仍照常工作的方法。

處理機制以.net的形式存在(並且繼承,C#.net)是一個垃圾收集環境,這意味着你不能響應內存管理。但是,垃圾收集器無法確定非託管資源是否已完成使用,因此您需要自行完成此操作。

如果你想方法拋出一個異常對象被diposed後,你需要一個布爾捕捉處置狀態,一旦物體被放置,你拋出該異常:

public class A : IDisposable 
{ 
    int i = 100; 
    bool disposed = false; 
    public void Dispose() 
    { 
     disposed = true; 
     Console.WriteLine("Dispose() called"); 
    } 
    public void f() 
    { 
     if(disposed) 
     throw new ObjectDisposedException(); 

     Console.WriteLine("{0}", i); i *= 2; 
    } 
} 
+0

處置*應該*表示任何非託管資源被釋放。但是,如果班級執行不力,這並不是必須的。 – svick

+0

顯然。但是任何關鍵行爲都是如此,我假設了理想情況。 – Femaref

+0

但是根據Dispose模式配置,當從外部調用客戶端時,都會釋放託管資源和非託管資源。在這種情況下,我希望沒有方法可行。對? – YakRangi

6

不會拋出異常,因爲在調用Dispose之後,您尚未設計拋出ObjectDisposedException的方法。

一旦Dispose被調用,clr不會自動知道它應該拋出ObjectDisposedException。如果Dispose已釋放成功執行方法所需的任何資源,則拋出異常是您的責任。

2

C#中的disposer與C++中的析構函數不同。處理器用於在對象保持有效時釋放託管(或非託管)資源。

根據類的實現拋出異常。如果f()不需要使用已處理的對象,則不一定需要拋出異常。

3

一個典型的Dispose()實現只對其存儲在其一次性字段中的任何對象調用Dispose()。後者依次釋放非託管資源。如果你實現了IDisposable而不是實際做任何事情,就像你在代碼片段中做的那樣,那麼對象狀態根本不會改變。沒有什麼可以出錯。不要混淆處理和最終確定。

2

調用Dispose()不會將對象引用設置爲null,並且您的自定義一次性類不包含任何拋出異常的邏輯,如果在調用Dispose()之後訪問其函數,那麼它當然法律。

在現實世界中,Dispose()釋放非託管資源,並且之後這些資源將不可用,並且/或者類作者在調用Dispose()後嘗試使用該對象時拋出ObjectDisposedException。通常,在Dispose()體內將類級布爾值設置爲true,並在該類的其他成員檢查該值之前檢查該值,如果bool爲true,則拋出異常。

2

IDisposable的目的是允許一個對象修復任何外部實體的狀態,這些外部實體爲了其好處被置於一種不太理想的狀態以用於其他目的。例如,Io.Ports.SerialPort對象可能已將串行端口的狀態從「可用於任何需要它的應用程序」更改爲「僅可由一個特定的Io.Ports.SerialPort對象使用」。 SerialPort.Dispose的主要目的是將串行端口的狀態恢復爲「可用於任何應用程序」。

當然,一旦一個實現了IDisposable的對象重置了一些爲了其好處而一直保持某種狀態的實體,它將不再享有這些實體的維護狀態。例如,一旦串行端口的狀態設置爲「可用於任何應用程序」,與其相關聯的數據流就不能再用於發送和接收數據。如果一個對象能夠正常運作,而沒有外部實體爲了它的利益而進入特殊狀態,那麼沒有理由首先將外部實體留在一個特殊的狀態中。

通常,在對象上調用IDisposable.Dispose之後,不應期望對象能夠做很多事情。試圖在這樣的對象上使用大多數方法會指示一個錯誤;如果方法不能合理地工作,那麼通過ObjectDisposedException來指示正確的方法。

Microsoft建議幾乎所有實現IDisposable的對象上的所有方法,如果它們用於已處置的對象上,都應拋出ObjectDisposedException。我建議這樣的建議是過分的。對於設備來說,暴露方法或屬性以查明對象存活時發生的事情通常非常有用。雖然可以給通信類一個Close方法以及一個Dispose方法,並且只允許在關閉之後但在Dispose之後查詢諸如NumberOfPacketsExchanged之類的東西,但這看起來過於複雜。讀取對象之前發生的事件相關的屬性Disposed似乎是一個完全合理的模式。

相關問題