2009-05-02 94 views
6

我使用C#連續運行,在.NET 2中創建了一個Windows Forms應用程序。對於大多數帳戶我很高興,但據我所知,它偶爾失敗了。我能夠在50%的時間內監控其性能,我從未注意到失敗。如何確保在.NET中正確處理對象?

在這一點上,我擔心的可能是程序使用的資源太多,並且在不再需要時不會處理資源。

正確處理已創建定時器和圖形對象(如圖形路徑,SQL連接等)的創建對象的最佳做法是什麼?或者我可以依靠dispose方法來處理所有垃圾收集?

另外: 有沒有一種方法可以監視應用程序使用的資源?

+0

是你的代碼,而無需不安全,dllimport的所有託管代碼等等? – VolkerK 2009-05-02 13:00:29

+0

這些關於失敗的報告,它們是否包含異常信息或任何可用於重現問題的信息?整個應用程序是否失敗,或者只是一個方法調用?您可以嘗試偵聽AppDomain.UnhandledException以記錄未處理的任何異常,但任何無提示catch都將被視爲處理,因此不會被記錄。 – sisve 2009-05-02 20:59:49

回答

14

最佳做法是確保在不再需要對象時,實現接口IDisposable的所有對象都被稱爲Dispose。

這可以通過using關鍵字或try/finally構造來實現。

在爲窗體的生命週期分配資源的WinForms表單中,需要一種稍微不同的方法。由於表單本身實現了IDisposable,這表明在某個時間點Dispose將在此表單上調用。您希望確保您的可支配資源能夠同時處理。要做到這一點,你應該覆蓋形式Dispose(bool disposing)方法。實施應該是這個樣子:

protected override void Dispose(bool disposing) 
{ 
    if (disposing) 
    { 
     // dispose managed resources here 
    } 
    // dispose unmanaged resources here 
} 

的形式A於組件注意:如果你的對象實現了IComponent界面,您可以放置​​該實例的形式Container。當容器本身被處理時,容器將負責處理部件。

+0

嗨,它不允許我處理它。它說「具有相同簽名的成員已被申報」。你如何覆蓋從窗體繼承的Dispose?謝謝 – Houman 2009-09-18 16:32:34

2

你應該致電Dispose稀有資源釋放他們。您可以使用using聲明爲此事:

using (var resource = new MyScarceObject()) 
{ 
    // resource will be used here... 
} // will free up the resources by calling Dispose automatically 
1

的一些技巧:使用的()關鍵字

-Take優勢,只要你能。 如果你有單元測試,我會建議重構你的代碼來實現這個改變。

http://msdn.microsoft.com/en-us/library/yh598w02.aspx

- 請注意,以明確註銷所有事件處理程序和自住爲您的應用程序的整個持續時間列表中刪除的所有對象。這是程序員在.NET中最常犯的錯誤,導致這些項目未被收集。

4

除了已經說過的內容之外,如果您使用COM組件,確保它們完全發佈是一個非常好的主意。我有我使用所有的COM釋放的時間段:

private void ReleaseCOMObject(object o) 
{ 
    Int32 countDown = 1; 
    while(countDown > 0) 
     countDown = System.Runtime.InteropServices.Marshal.ReleaseCOMObject(o); 
} 
1

當一個對象不可訪問時,Object.Finalize Method將被調用。在實現IDisposable的類中抑制這種不必要的調用是有幫助的。您可以通過調用GC.SuppressFinalize Method

public void Dispose() 
{ 
    // dispose resources here 

    GC.SuppressFinalize(this); 
} 
2

有保證這幾方面做到這一點。我找到的主要幫助是利用「使用」關鍵字。該應用爲這樣的:

using(SqlConnection connection = new SqlConnection(myConnectionString)) 
{ 
    /* utilise the connection here */ 
} 

這基本上轉化爲:

SqlConnection connection = null; 
try 
{ 
    connection = new SqlConnection(myConnectionString); 
} 
finally 
{ 
    if(connection != null) connection.Dispose(); 
} 

因此,它只能與實現IDisposable類型的作品。

此關鍵字在處理GDI對象(如筆和畫筆)時非常有用。但是,有些情況下您會希望將資源保留更長時間,而不僅僅是方法的當前範圍。作爲一個規則,如果可能的話最好避免這種情況,但例如在處理SqlCe時,保持與db的連接一直處於打開狀態的性能更高。因此,人們無法擺脫這種需求。

在這種情況下,您不能使用「使用」,但您仍然希望能夠輕鬆回收連接所持有的資源。 有兩種機制可以用來獲取這些資源。

一個是通過一個finaliser。超出範圍的所有託管對象最終都會被垃圾收集器收集。如果你已經定義了一個終結者,那麼GC在收集這個對象時會調用它。

public class MyClassThatHoldsResources 
{ 
    private Brush myBrush; 

    // this is a finaliser 
    ~MyClassThatHoldsResources() 
    { 
     if(myBrush != null) myBrush.Dispose(); 
    } 
} 

但是,上面的代碼是不幸的廢話。原因是因爲在最後確定的時間你不能保證哪些管理對象已被收集,哪些沒有。 Ergo上例中的「myBrush」可能已被垃圾收集器丟棄。因此,最好不要使用終結器來收集管理對象,其用途是收集非託管資源的

終結者的另一個問題是它不是確定性的。比方說,例如我有一個通過串口進行通信的類。一次只能打開一個到串口的連接。因此,如果我有以下類:

class MySerialPortAccessor 
{ 
    private SerialPort m_Port; 

    public MySerialPortAccessor(string port) 
    { 
     m_Port = new SerialPort(port); 
     m_Port.Open(); 
    } 

    ~MySerialPortAccessor() 
    { 
     if(m_Port != null) m_Port.Dispose(); 
    } 
} 

然後,如果我使用的對象是這樣的:

public static void Main() 
{ 
    Test1(); 
    Test2(); 
} 

private static void Test1() 
{ 
    MySerialPortAccessor port = new MySerialPortAccessor("COM1:"); 
    // do stuff 
} 

private static void Test2() 
{ 
    MySerialPortAccessor port = new MySerialPortAccessor("COM1:"); 
    // do stuff 
} 

我有一個問題。問題是終結者不是確定性的。這就是說我不能保證它什麼時候會運行,因此可以繞過我的串口對象。因此,當我運行測試2時,我可能會發現端口仍處於打開狀態。 雖然我可能調用GC.Collect()之間的Test1()和Test2()這將解決這個問題它不是建議。如果你想從收集器中獲得最佳性能,那就讓它自己做。

所以我真正想要做的是這樣的:

class MySerialPortAccessor : IDispable 
{ 
    private SerialPort m_Port; 

    public MySerialPortAccessor(string port) 
    { 
     m_Port = new SerialPort(port); 
     m_Port.Open(); 
    } 

    public void Dispose() 
    { 
     if(m_Port != null) m_Port.Dispose(); 
    } 
} 

,我會重寫我的測試是這樣的:

public static void Main() 
{ 
    Test1(); 
    Test2(); 
} 

private static void Test1() 
{ 
    using(MySerialPortAccessor port = new MySerialPortAccessor("COM1:")) 
    { 
     // do stuff 
    } 
} 

private static void Test2() 
{ 
    using(MySerialPortAccessor port = new MySerialPortAccessor("COM1:")) 
    { 
     // do stuff 
    } 
} 

這將現在的工作。 那麼最終決定者是什麼?爲什麼要使用它?

非託管資源和不調用Dispose的可能實現。

作爲其他人使用的組件庫的作者;他們的代碼可能會忘記處理該對象。有可能是別的東西可能會終止進程,因此.Dispose()不會發生。由於這些情況,應該實施終結器來清理任何非託管資源作爲「最壞情況」的情況,但是Dispose應該也會整理這些資源,以便您擁有「確定性清理」例程。

所以在最後,在.NET Framework Guidelines book推薦的模式是同時實現如下:

public void SomeResourceHoggingClass, IDisposable 
{ 
    ~SomeResourceHoggingClass() 
    { 
     Dispose(false); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
    } 

    // virtual so a sub class can override it and add its own stuff 
    // 
    protected virtual void Dispose(bool deterministicDispose) 
    {  
     // we can tidy managed objects 
     if(deterministicDispose) 
     { 
       someManagedObject.Parent.Dispose(); 
       someManagedObject.Dispose(); 
     } 

     DisposeUnmanagedResources(); 

     // if we've been disposed by .Dispose() 
     // then we can tell the GC that it doesn't 
     // need to finalise this object (which saves it some time) 
     // 
     GC.SuppressFinalize(this); 
    } 
}