2009-05-02 105 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); 
    } 
}