C # 2008

我已经在这方面工作了一段时间,但我仍然对在代码中使用finalize和dispose方法感到困惑。我的问题如下:

I know that we only need a finalizer while disposing unmanaged resources. However, if there are managed resources that make calls to unmanaged resources, would it still need to implement a finalizer? However, if I develop a class that doesn't use any unmanaged resource - directly or indirectly, should I implement the IDisposable to allow the clients of that class to use the 'using statement'? Would it be feasible to implement IDisposable just to enable clients of your class to use the using statement? using(myClass objClass = new myClass()) { // Do stuff here } I have developed this simple code below to demonstrate the Finalize/dispose use: public class NoGateway : IDisposable { private WebClient wc = null; public NoGateway() { wc = new WebClient(); wc.DownloadStringCompleted += wc_DownloadStringCompleted; } // Start the Async call to find if NoGateway is true or false public void NoGatewayStatus() { // Start the Async's download // Do other work here wc.DownloadStringAsync(new Uri(www.xxxx.xxx)); } private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { // Do work here } // Dispose of the NoGateway object public void Dispose() { wc.DownloadStringCompleted -= wc_DownloadStringCompleted; wc.Dispose(); GC.SuppressFinalize(this); } }

关于源代码的问题:

Here I have not added the finalizer, and normally the finalizer will be called by the GC, and the finalizer will call the Dispose. As I don't have the finalizer, when do I call the Dispose method? Is it the client of the class that has to call it? So my class in the example is called NoGateway and the client could use and dispose of the class like this: using(NoGateway objNoGateway = new NoGateway()) { // Do stuff here } Would the Dispose method be automatically called when execution reaches the end of the using block, or does the client have to manually call the dispose method? i.e. NoGateway objNoGateway = new NoGateway(); // Do stuff with object objNoGateway.Dispose(); // finished with it I am using the WebClient class in my NoGateway class. Because WebClient implements the IDisposable interface, does this mean that WebClient indirectly uses unmanaged resources? Is there a hard and fast rule to follow this? How do I know that a class uses unmanaged resources?


当前回答

另一个答案的某些方面有点不正确,原因有二:

首先,

using(NoGateway objNoGateway = new NoGateway())

Actually相当于:

try
{
    NoGateway = new NoGateway();
}
finally
{
    if(NoGateway != null)
    {
        NoGateway.Dispose();
    }
}

This may sound ridiculous since the 'new' operator should never return 'null' unless you have an OutOfMemory exception. But consider the following cases: 1. You call a FactoryClass that returns an IDisposable resource or 2. If you have a type that may or may not inherit from IDisposable depending on its implementation - remember that I've seen the IDisposable pattern implemented incorrectly many times at many clients where developers just add a Dispose() method without inheriting from IDisposable (bad, bad, bad). You could also have the case of an IDisposable resource being returned from a property or method (again bad, bad, bad - don't 'give away your IDisposable resources)

using(IDisposable objNoGateway = new NoGateway() as IDisposable)
{
    if (NoGateway != null)
    {
        ...

如果'as'操作符返回null(或返回资源的属性或方法),并且'using'块中的代码可以防止'null',那么在尝试对空对象调用Dispose时,您的代码不会因为'内置'空检查而崩溃。

你的回复不准确的第二个原因是由于以下stmt:

在GC销毁对象时调用终结器

First, Finalization (as well as GC itself) is non-deterministic. THe CLR determines when it will call a finalizer. i.e. the developer/code has no idea. If the IDisposable pattern is implemented correctly (as I've posted above) and GC.SuppressFinalize() has been called, the the Finalizer will NOT be called. This is one of the big reasons to properly implement the pattern correctly. Since there is only 1 Finalizer thread per managed process, regardless of the number of logical processors, you can easily degrade performance by backing up or even hanging the Finalizer thread by forgetting to call GC.SuppressFinalize().

我已经在我的博客上发布了Dispose模式的正确实现:如何正确地实现Dispose模式

其他回答

1) WebClient是一个托管类型,所以你不需要终结器。如果您的用户不Dispose()您的NoGateway类,并且本机类型(不被GC收集)之后需要清理,则需要结束器。在这种情况下,如果用户没有调用Dispose(),所包含的WebClient将在NoGateway调用之后被GC丢弃。

间接地,是的,但你不必担心。你的代码是正确的,你不能防止你的用户很容易忘记Dispose()。

using(NoGateway objNoGateway = new NoGateway())

等于

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

在GC销毁对象时调用终结器。这可能与你离开方法时完全不同。Dispose of IDisposable在你离开using块后立即被调用。因此,这种模式通常是在您不再需要资源后立即使用using来释放它们。

使用lambdas代替IDisposable。

我从来没有对使用/IDisposable这个想法感到兴奋。问题是它要求调用者:

知道他们必须使用IDisposable 记住使用“using”。

我新的首选方法是使用工厂方法和lambda代替

假设我想用SqlConnection(应该用using包装的东西)做一些事情。通常你会这么做

using (Var conn = Factory.MakeConnection())
{
     conn.Query(....);
}

新方法

Factory.DoWithConnection((conn)=>
{
    conn.Query(...);
}

在第一种情况下,调用者不能简单地使用using语法。在第二种情况下,用户没有选择。没有创建SqlConnection对象的方法,调用者必须调用DoWithConnection。

DoWithConnection看起来像这样

void DoWithConnection(Action<SqlConnection> action)
{
   using (var conn = MakeConnection())
   {
       action(conn);
   }
}

MakeConnection现在是私有的

我同意pm100的观点(我应该在之前的文章中明确地说明这一点)。

除非需要,否则永远不要在类中实现IDisposable。具体来说,大约有5次你需要/应该实现IDisposable:

Your class explicitly contains (i.e. not via inheritance) any managed resources which implement IDisposable and should be cleaned up once your class is no longer used. For example, if your class contains an instance of a Stream, DbCommand, DataTable, etc. Your class explicitly contains any managed resources which implement a Close() method - e.g. IDataReader, IDbConnection, etc. Note that some of these classes do implement IDisposable by having Dispose() as well as a Close() method. Your class explicitly contains an unmanaged resource - e.g. a COM object, pointers (yes, you can use pointers in managed C# but they must be declared in 'unsafe' blocks, etc. In the case of unmanaged resources, you should also make sure to call System.Runtime.InteropServices.Marshal.ReleaseComObject() on the RCW. Even though the RCW is, in theory, a managed wrapper, there is still reference counting going on under the covers. If your class subscribes to events using strong references. You need to unregister/detach yourself from the events. Always to make sure these are not null first before trying to unregister/detach them!. Your class contains any combination of the above...

对于使用COM对象并且必须使用Marshal.ReleaseComObject(),推荐的替代方法是使用System.Runtime.InteropServices.SafeHandle类。

BCL(基类库团队)在这里有一篇关于它的很好的博客文章http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx

一个非常重要的注意事项是,如果你正在使用WCF并清理资源,你几乎总是应该避免使用'using'块。有很多博客和MSDN上的文章都在解释为什么这是一个坏主意。我也在这里发布了关于它的帖子-不要在WCF代理中使用'using()'

据我所知,强烈建议不要使用终结器/析构函数:

public ~MyClass() {
  //dont use this
}

大多数情况下,这是因为不知道何时或是否会调用它。处置方法是更好的,特别是如果你使用或直接处置。

使用是好的。使用它:)