C# 调用 IDisposable.Dispose() 与使对象为空
C# calling IDisposable.Dispose() vs making object null
考虑以下代码:
一个。玩具Class
class Toy
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ToyAddon addOn;
public ToyAddon AddOn
{
get { return addOn; }
set { addOn = value; }
}
public void RemoveAddons()
{
/*
* someone said: this could lead to possible memory leak
* if this class implements IDisposable,
* then we are not disposing any of the resource
* */
//this.addOn = null;
/* instead use this */
var disposableToyAddOn = this.addOn as IDisposable;
if (disposableToyAddOn != null)
disposableToyAddOn.Dispose();
this.addOn = null;
}
public override string ToString()
{
return String.Format("{0}, {1}",
this.name,
(this.AddOn == null) ? "" : addOn.AddOnName);
}
}
乙。玩具插件
class ToyAddon
{
private string addOnName;
public string AddOnName
{
get { return addOnName; }
set { addOnName = value; }
}
}
C。主程序
class Program
{
static void Main(string[] args)
{
ToyAddon tAdd = new ToyAddon();
tAdd.AddOnName = "Amazing AddOn";
Toy t = new Toy();
t.Name = "Amazing Toy";
t.AddOn = tAdd;
t.RemoveAddons();
Console.WriteLine(t.ToString());
}
}
现在有人建议我检查 "having-a" 对象是否正在实现 IDisposable,然后调用它的 dispose 方法。
(请查看玩具class中的评论)
恕我直言,如果我将引用设为空,那么堆上的对象将被标记为由 GC 收集。
如果可以进行此推理,堆和堆栈上发生的情况以及 GC 的作用,如果 class(如 ToyAddOn ) 实现 IDisposable 与不实现相比。
如果您不手动处理 class,它持有的非托管资源(如果有的话)只会在 GC 检测到有足够的内存压力需要垃圾收集和对象时被清理获得 GC 和最终确定。
并非所有需要处理的东西都会增加内存压力,它们可能会占用 Window 句柄之类的东西。
如果内存压力永远不会变得足够高,那么您可能永远不会在 运行 从处置释放的资源中垃圾收集这些值。当人们将 Bitmap 置于紧密循环中但不处置它们时,您经常会看到这种情况,每个位图都使用 GDI+ 句柄并且不计入托管内存压力并且不会触发 GC。这通常会导致人们在仍有大量内存时出现 OutOfMemoryExceptions,因为他们 运行 真正用掉的是 GDI+ 句柄。
通过显式处置您的资源,您不需要 "hope you get lucky" 并让您的对象在您 运行 之前完成其持有的非托管资源。
堆栈和堆与此无关。
Now it was suggested to me that check if a "having-a" object is
implementing IDisposable then call the dispose method of it.
IDisposable
是一种模式,用于释放需要显式释放的非托管资源,因为 GC 不知道它们。通常,持有非托管资源的 class 也会实现终结器,因此它实际上延长了对象的生命周期,这比必要的要多。 Dispose
在这些情况下通常调用对 GC.SupressFinalize
的调用,以从终结队列中删除所述对象。
如果您有一个 IDisposable
对象,您自己实施 IDisposable
以确保底层资源得到处置通常是一个很好的模式。
IMHO, if I make the reference null, then the object on heap would be
marked for Collection by GC.
如果您想要 "null out" 的成员不是 static
成员,那么(通常)没有理由这样做。编译器足够聪明,可以优化掉它,而 GC 也足够聪明,知道不再有对变量的引用并清理它。
编辑:
正如@ScottChamberlain 指出的那样,在某些情况下,一次性对象仍然持有引用,因此 GC 不会将其计入 GC。在处理它并将其清零时,您提示 GC 它是 "ready for collection".
尝试将某些东西转换为 IDisposable
并在它实现该接口时将其处置的模式高度表明存在设计缺陷。如果持有的参考代码类型未实现 IDisposable
,则强烈表明:
持有该类型的参考资料的人不应该处理它;即使派生类型实现 Disposable
,清理它们的责任也应该由创建这些类型实例(并保存这些类型的引用)的代码承担。
基本类型应该实现 IDisposable
(即使 99% 的实现什么都不做)因为某些实例的生命周期 超出任何知道的人的控制范围他们是否真的需要清理。后一种情况的一个典型例子是 IEnumerator<T>
。尽管 IEnumerator
省略了 IDisposable
,但在发布后不久就很明显需要它;当 Microsoft 添加 IEnumerator<T>
时,他们直接包含了 IDisposable
。尽管 IEnumerator<T>
的大多数实现可以在不进行清理的情况下被放弃,但是在集合上调用 GetEnumerator
的代码无法知道 IEnumerator<T>
的实现是否可能是其中之一需要清理的稀有对象,并且没有实际的方法 IEnumerable<T>
其 GetEnumerator
方法创建 IEnumerator<T>
可以预期知道客户端何时完成它,因此除了客户端之外没有人会能够 Dispose
IEnumerator<T>
.
确定实现 Disposable
的 ToyAddOn
的任何实例是否有可能在生命周期结束时结束,而唯一的引用由不知道它们是否需要的代码持有清理。如果是这样,使 ToyAddOn
实现 Disposable
。如果没有,请将清理留给知道它是必需的代码。
我同意发布的这三个答案。只是为了简化您问题的答案:
/*
* someone said: this could lead to possible memory leak
* if this class implements IDisposable,
* then we are not disposing any of the resource
* */
//this.addOn = null;
某人所说的不太适用于您的示例。但是总的来说是绝对正确的。
假设玩具 class 的寿命比 ToyAddon 长得多。 ToyAddon class 订阅了 Toy Class 的事件。如果是这样,ToyAddon 应该在其 Dispose 方法中取消订阅这些订阅。如果您在将 ToyAddon 从 Toy 实例中移除后没有调用它的 Dispose 方法,则只要 Toy 实例存在,ToyAddon 实例就会存在于内存中。
而且,即使您在上述情况下调用了 Dispose,仍然存在通过 'addOn' 对 ToyAddon 的引用。所以需要将addOn设置为null才能彻底去掉它。
IMHO, if I make the reference null, then the object on heap would be
marked for Collection by GC.
你的情况是正确的,正如我上面所解释的,一般情况下是错误的。
考虑以下代码:
一个。玩具Class
class Toy
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ToyAddon addOn;
public ToyAddon AddOn
{
get { return addOn; }
set { addOn = value; }
}
public void RemoveAddons()
{
/*
* someone said: this could lead to possible memory leak
* if this class implements IDisposable,
* then we are not disposing any of the resource
* */
//this.addOn = null;
/* instead use this */
var disposableToyAddOn = this.addOn as IDisposable;
if (disposableToyAddOn != null)
disposableToyAddOn.Dispose();
this.addOn = null;
}
public override string ToString()
{
return String.Format("{0}, {1}",
this.name,
(this.AddOn == null) ? "" : addOn.AddOnName);
}
}
乙。玩具插件
class ToyAddon
{
private string addOnName;
public string AddOnName
{
get { return addOnName; }
set { addOnName = value; }
}
}
C。主程序
class Program
{
static void Main(string[] args)
{
ToyAddon tAdd = new ToyAddon();
tAdd.AddOnName = "Amazing AddOn";
Toy t = new Toy();
t.Name = "Amazing Toy";
t.AddOn = tAdd;
t.RemoveAddons();
Console.WriteLine(t.ToString());
}
}
现在有人建议我检查 "having-a" 对象是否正在实现 IDisposable,然后调用它的 dispose 方法。 (请查看玩具class中的评论)
恕我直言,如果我将引用设为空,那么堆上的对象将被标记为由 GC 收集。
如果可以进行此推理,堆和堆栈上发生的情况以及 GC 的作用,如果 class(如 ToyAddOn ) 实现 IDisposable 与不实现相比。
如果您不手动处理 class,它持有的非托管资源(如果有的话)只会在 GC 检测到有足够的内存压力需要垃圾收集和对象时被清理获得 GC 和最终确定。
并非所有需要处理的东西都会增加内存压力,它们可能会占用 Window 句柄之类的东西。
如果内存压力永远不会变得足够高,那么您可能永远不会在 运行 从处置释放的资源中垃圾收集这些值。当人们将 Bitmap 置于紧密循环中但不处置它们时,您经常会看到这种情况,每个位图都使用 GDI+ 句柄并且不计入托管内存压力并且不会触发 GC。这通常会导致人们在仍有大量内存时出现 OutOfMemoryExceptions,因为他们 运行 真正用掉的是 GDI+ 句柄。
通过显式处置您的资源,您不需要 "hope you get lucky" 并让您的对象在您 运行 之前完成其持有的非托管资源。
堆栈和堆与此无关。
Now it was suggested to me that check if a "having-a" object is implementing IDisposable then call the dispose method of it.
IDisposable
是一种模式,用于释放需要显式释放的非托管资源,因为 GC 不知道它们。通常,持有非托管资源的 class 也会实现终结器,因此它实际上延长了对象的生命周期,这比必要的要多。 Dispose
在这些情况下通常调用对 GC.SupressFinalize
的调用,以从终结队列中删除所述对象。
如果您有一个 IDisposable
对象,您自己实施 IDisposable
以确保底层资源得到处置通常是一个很好的模式。
IMHO, if I make the reference null, then the object on heap would be marked for Collection by GC.
如果您想要 "null out" 的成员不是 static
成员,那么(通常)没有理由这样做。编译器足够聪明,可以优化掉它,而 GC 也足够聪明,知道不再有对变量的引用并清理它。
编辑:
正如@ScottChamberlain 指出的那样,在某些情况下,一次性对象仍然持有引用,因此 GC 不会将其计入 GC。在处理它并将其清零时,您提示 GC 它是 "ready for collection".
尝试将某些东西转换为 IDisposable
并在它实现该接口时将其处置的模式高度表明存在设计缺陷。如果持有的参考代码类型未实现 IDisposable
,则强烈表明:
持有该类型的参考资料的人不应该处理它;即使派生类型实现
Disposable
,清理它们的责任也应该由创建这些类型实例(并保存这些类型的引用)的代码承担。基本类型应该实现
IDisposable
(即使 99% 的实现什么都不做)因为某些实例的生命周期 超出任何知道的人的控制范围他们是否真的需要清理。后一种情况的一个典型例子是IEnumerator<T>
。尽管IEnumerator
省略了IDisposable
,但在发布后不久就很明显需要它;当 Microsoft 添加IEnumerator<T>
时,他们直接包含了IDisposable
。尽管IEnumerator<T>
的大多数实现可以在不进行清理的情况下被放弃,但是在集合上调用GetEnumerator
的代码无法知道IEnumerator<T>
的实现是否可能是其中之一需要清理的稀有对象,并且没有实际的方法IEnumerable<T>
其GetEnumerator
方法创建IEnumerator<T>
可以预期知道客户端何时完成它,因此除了客户端之外没有人会能够Dispose
IEnumerator<T>
.
确定实现 Disposable
的 ToyAddOn
的任何实例是否有可能在生命周期结束时结束,而唯一的引用由不知道它们是否需要的代码持有清理。如果是这样,使 ToyAddOn
实现 Disposable
。如果没有,请将清理留给知道它是必需的代码。
我同意发布的这三个答案。只是为了简化您问题的答案:
/* * someone said: this could lead to possible memory leak * if this class implements IDisposable, * then we are not disposing any of the resource * */ //this.addOn = null;
某人所说的不太适用于您的示例。但是总的来说是绝对正确的。
假设玩具 class 的寿命比 ToyAddon 长得多。 ToyAddon class 订阅了 Toy Class 的事件。如果是这样,ToyAddon 应该在其 Dispose 方法中取消订阅这些订阅。如果您在将 ToyAddon 从 Toy 实例中移除后没有调用它的 Dispose 方法,则只要 Toy 实例存在,ToyAddon 实例就会存在于内存中。
而且,即使您在上述情况下调用了 Dispose,仍然存在通过 'addOn' 对 ToyAddon 的引用。所以需要将addOn设置为null才能彻底去掉它。
IMHO, if I make the reference null, then the object on heap would be marked for Collection by GC.
你的情况是正确的,正如我上面所解释的,一般情况下是错误的。