问题描述:
问题
这里最好的事情是什么?我们应该
请详细说明你的答案。谢谢
public class FooClass : IDisposable
{
private readonly OtherDisposableClass _disposable;
private readonly string _imageSource;
private readonly string _action;
private readonly string _destination;
private bool _isInitialized;
public FooClass(OtherDisposableClass disposable)
{
_disposable = disposable;
}
~FooClass()
{
// Finalizer calls Dispose(false)
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// Not possible because _disposable is marked readonly
_disposable = null;
}
}
答案 0 :(得分:4)
创建OtherDisposableClass
的事情应该是处理它...在FooClass
中单独留下(如果你愿意,你可以拥有它readonly
。)
将它设置为null无论如何都不会产生任何影响,它只会将FooClass中的引用设置为null(无论如何都要处理)。
修改强>
从给出的代码看起来你甚至不需要在这里实现IDisposable - 这只是让事情变得复杂。如果您在OtherDisposableClass
的构造函数中创建了FooClass
的实例,那么您可以实现IDisposable,只需在OtherDisposableClass.Dispose()
内调用FooClass.Dispose()
。
这里创建的FooClass依赖于OtherFooClass - OtherFooClass将比FooClass更长 - 因此FooClass应首先进行GC,然后不会引用剩余的OtherDisposableClass - 这将允许它为GC'd
答案 1 :(得分:0)
在VB.NET中,如果实现IDisposable
的类型具有任何声明为WithEvents
的变量,那么将这些变量设置为“通常是个好主意 - 有时非常重要” null
方法中的Dispose
,因为如果不这样做将意味着这些变量引用的对象将保留对已处置对象的反向引用。在许多短期对象从长期存在的对象订阅事件的情况下,这可能导致严重的内存泄漏。
虽然C#不允许VB.NET样式的WithEvents
声明,但是可以在C#中编写具有类似行为的属性:
// Handle a property Wobbler which identifies an object whose Wibbled event
// I want to handle with my WibbleWobble method.
Woozle _wobble;
Woozle Wobbler {
get { return _wobbler; }
set {
var wasWobbler = _wobbler
if (wasWobbler == value) return; // Don't unsubcribe and resubscribe same object
if (wasWobbler != null)
wasWobbler.Wibbled -= WibbleWobble; // Unsubscribe old event
if (value != null)
value.Wibbled += WibbleWobble; // Subscribe new event
_wobbler = value;
}
};
如果一个人使用这样的模式(这可能是确保订阅与unsubscribes配对的好方法),那么应该在一个人的Wobbler
方法中将null
设置为Dispose
,以确保附加到它的任何事件都被分离。
除了前面提到的场景之外,Dispose
是否将事物设置为null
通常无关紧要,因为对于这些字段引用的对象,唯一应该处理的是处理它们和取消他们的事件,在编码良好的对象上多次调用Dispose
应该是无害的。但是,在少数情况下,在设置中将字段设置为null
可能会有所帮助。一些集合使用数组存储数据,并指示哪些插槽保存有意义的数据;如果一个集合通过指示一个插槽没有任何有意义的东西来“删除”一个项目,但不会使该引用无效,该项目以及它拥有直接或间接引用的任何内容将由GC保持活力。如果处置的对象使其对其他对象的引用无效,则可以最小化集合保持活动的数据量。
顺便提一下,请注意上面的代码不是线程安全的,并且在没有锁定的情况下不能非常干净地成为线程安全的,即使subscribe / unsubscribe方法是线程安全的,并且只使用_wobbler
通过{{{ 1}}方法。如果在处理订阅/取消订阅之前更新Interlocked
,那么如果_wobbler
是一个线程中的某个对象George而另一个线程中的其他对象,则第二个线程可能会在第一个线程之前向George发送取消订阅请求已发送其订阅请求(导致悬空订阅)。如果在处理订阅/取消订阅之前没有更新,那么两次同时尝试将Wobbler设置为George可能会导致George连续两次订阅请求(最终可能导致订阅悬空)。
答案 2 :(得分:0)
IDisposable
接口允许您以确定的方式释放非托管资源。非托管资源可以是文件句柄,数据库连接等。处置对象不会释放任何内存。内存只能由垃圾收集器回收,而您几乎无法控制何时以及如何完成。 处置对象不会释放任何内存。
垃圾收集和IDisposable
之间仍然存在联系。如果您的类直接控制非托管资源,则应在最终确定期间释放此资源。但是,在类上使用终结器会对性能产生影响,应尽可能避免使用。 FooClass
不直接控制非托管资源,也不需要终结器。但是,要强制执行非托管资源的确定性发布,FooClass.Dispose
在OtherDisposableClass.Dispose
被明确调用时(Dispose
为disposing
时)调用true
非常重要。如果从终结器调用Dispose
,则不应在其他托管对象上调用方法,因为这些对象可能已经完成。
总结一下:
垃圾收集和回收内存不同于释放非托管资源。
Dispose
方法应始终释放非托管资源。
Dispose
方法应该在聚合对象上调用Dispose
,但仅在直接调用时(不是从终结器调用时)。
应该可以多次调用Dispose
而不会出现任何问题。
在IDisposable
方法中将null
字段设置为Dispose
没有意义。
如果您不直接控制任何非托管资源,请避免实施终结器。
实现IDisposable
时,必须考虑继承,因为基类和派生类可能希望在字段上调用Dispose
和/或释放非托管资源。封闭课程可以避免很多这种复杂性。
详细说明将字段设置为null
:
我认为你的代码是理智的。例如,只有FooClass
具有对OtherDisposableClass
的永久引用,并且处置后的两个对象都将无法使用(例如,在除ObjectDisposedException
之外的所有公共方法中抛出Dispose
。
如果将引用设置为OtherDisposableClass
到null
,那么该实例就有资格进行垃圾回收,因为没有对该实例的引用。但是,在处置FooClass
之后,您肯定不会保留对该实例的引用,因为它不可用。这意味着FooClass
和OtherDisposableClass
都无法再从GC root到达,整个对象图现在可以进行垃圾回收。因此,无需切断FooClass
和OtherDisposableClass
之间的链接,因为它们将同时符合垃圾回收条件。
答案 3 :(得分:0)
您在IDisposable
中将成员Dispose(bool disposing)
设置为空是正确的,请参阅:https://msdn.microsoft.com/en-us/library/ms244737.aspx
如果字段标记为readonly
,则无法解决问题,我担心会删除readonly
。