2016-12-14 50 views
0

我一直在尝试增加对垃圾收集的理解,管理非托管资源以及关于内存管理的“正确设计原则”,因为我有兴趣进入“低层次”编程和这类事情。调用Dispose到底做了什么?

我明白,你应该使用using块或确保这些非托管资源都在事实上得到处置一些其他的解决办法,但我不明白什么是引擎盖下发生。

我看这篇文章:Implementing a Dispose Method在MSDN上,并通过这个特殊的线路困惑:

为了确保资源总是清理适当,Dispose方法应该是可调用多次,未抛出例外。

让我们看看他们的Dispose模式提供的示例代码:

class DerivedClass : BaseClass 
{ 
    // Flag: Has Dispose already been called? 
    bool disposed = false; 

    // Instantiate a SafeHandle instance. 
    SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true); 

    // Protected implementation of Dispose pattern. 
    protected override void Dispose(bool disposing) 
    { 
     if (disposed) 
     return; 

     if (disposing) 
     { 
     handle.Dispose(); 
     // Free any other managed objects here. 
     } 

     // Free any unmanaged objects here. 

     disposed = true; 

     // Call base class implementation. 
     base.Dispose(disposing); 
    } 
} 

相信什么上面引述的文本基本上说的是,“我们增加了一个bool disposed属性,以便我们能检查是否是truereturn如果是这样,如果是false那么我们处理实际的资源。这样我们就不会实际上处理多次的东西“

但这对我没有意义。如果我们已经摆脱了一个对象,你怎么能第二次打电话给Dispose

探讨,我写了包含以下3行控制台应用程序:

var cmd = new SqlCommand(); 
cmd.Dispose(); 
cmd.Dispose(); 

这编译和不执行的问题 - 这是有道理的,因为从文章中引用的文字。但我不明白什么实际上发生。

我设置了一个断点并跨过每一行。在调用第一个Dispose之后,我期望Visual Studio中的Locals窗口告诉我cmdnull。接着这一系列的想法,我问自己:“你怎么能在null上拨打Dispose?”显然,你不能。那么发生了什么?为什么cmd在第一次被处理后仍然是SqlCommand对象?

enter image description here

什么究竟Dispose做的,如果我布置我的对象为什么它似乎仍然存在的所有意图和目的是什么?

为什么/以下编译和运行没有问题?

var cmd = new SqlCommand(); 
cmd.Dispose(); 
cmd.CommandText = "I figured this would throw an exception but it doesn't"; 
+1

有没有“引擎盖下”。没有涉及的魔法,它只是一种像其他任何方法一样的方法。编写'Dispose'方法的人必须正确实现 - 调用所有合适的'CloseHandle'函数等。你能用其他方法将“this reference”改为null吗?当然不是。你为什么期望'Dispose'可以做到这一点?这只是一种方法。 – Luaan

+1

除了Luaan的解释之外,另请参阅http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface以获得更大的图片。 –

+1

你没有选择最糟糕的例子,[它什么都没有用](https://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlCommand.cs,1101)。可能在.NET 1.0中做了非常不同的事情。但是,在你处理它之后,你肯定不应该继续使用一个对象,但希望这很明显。可能抛出一个ObjectDisposedException。或者,因为会员没有做任何“沉重的”事情,所以可能不会。有意识地错误并不是那么有用。 –

回答

2

IDisposable接口是由一些消费类和所谓的 - 因为已经暗隼说 - 当你封装类的使用在使用块。这样可以更轻松地保持非托管资源的清洁。 但Dispose就像任何其他方法,不要与析构函数/终结器(这是你显然预期它是混淆)

+0

“...而不是与析构函数/终结器混淆“〜我认为这是这里的问题。阅读关于['Object.Finalize'](https://msdn.microsoft.com/en-us/library/system.object.finalize (v = vs.110).aspx)我想我认为'Dispose'(通过它的一系列'base.Dispose()'调用)最终调用了'Finalize'或做了*某事*除了我可能明确的在我的'Dispose'重写中定义,因此'Dispose'实际上只是语法糖的一部分,它就是'using'语句,仅此而已⋯⋯ – sab669

+0

是的,你去!:-) –

+1

@ sab669实际上,它实际上通常是反过来 - 终结器(根本不用用户可调用)经常调用Dispose方法,只是为了将所有的清理逻辑保存在一个地方。.NET中的终结器是最终的安全网络,用于确保非托管即使出现了问题,资源也会被丢弃(并且您忘记了调用正确的Dispose方法),而且它们也被滥用于其他薄gs,但这是他们唯一设计的,并且在终结器中失去了很多保证,所以编写安全的终结器并非完全无关紧要。即使'this'可以是'null' :) – Luaan

1

没有什么特别之处Dispose除了这样一个事实:using声明可以调用它。除此之外,它就像任何其他.NET函数一样。它不会神奇地将任何变量设置为null,如果再次使用该对象或者类似的东西,会导致引发异常。

同样,IDisposable不是任何特殊的接口,除非using声明中的对象必须实现它。你可以考虑使用的语句是这样的:

// using(var a = somethingDisposable()) {otherCode();} 
var a = somethingDisposable(); 
try 
{ 
    otherCode(); 
} 
finally 
{ 
    if(a != null) 
     ((IDisposable)a).Dispose(); 
} 
+0

但是......这是什么意思。 Dispose做什么。它只是递增对象的Generation,以便每当垃圾收集器运行时它会照顾这些项目? – sab669

+1

不,正如我所说的,它与垃圾回收器完全没有任何交互作用。这只是另一种.NET方法。没有什么特别的,除了'using'语句的好语法。 –

+1

'Dispose'does,你告诉它做什么。这只是一种方法。您通常使用它来以受控方式摆脱非托管资源(即,在使用块内处理您的实例时)。 –

相关问题