【问题标题】:C# and what not to do in a finalizer [closed]C# 以及在终结器中不应该做什么 [关闭]
【发布时间】:2019-12-16 16:02:15
【问题描述】:

我读到了一些关于在 C# 终结器中不要做什么的文章,但我终其一生都找不到链接。

调用析构函数时,在拥有析构函数的类中使用引用对象是否安全?

我相信这就是我正在阅读的内容。但如果是这样的话,析构函数就不能调用 Dispose 方法来清理非托管资源,对吗?

注意:我知道 IDisposable 和常见的实现。

【问题讨论】:

  • 经验法则:不要在终结器中做任何事情。有一些可能的用途(例如,如果您忘记返回资源,则可以使用安全网),但它们也值得怀疑。
  • 那为什么还要有终结器呢? IF 在终结器中使用任何引用成员对象都是不安全的,这意味着您甚至不能调用引用属性的 Dispose 方法并依赖类的用户来调用 Dispose 或使用 Using声明。
  • 您在终结器中处理非托管资源,例如 Win32 句柄。 IIRC 使用托管对象是不安全的,因为它们可能已经被释放了。
  • @R.J.Dunnill 如果我有一个成员引用对象(如 StreamWriter)并想确保它被处理掉怎么办?我真的不能从终结器中这样做吗?
  • 如果问题是非托管资源,请记住 IDisposable 是您的朋友,并且在对象超出范围后几秒钟内可能不会调用终结器 - 仅在特定的 GC 传递时(这取决于您的垃圾生成,可能或多或少频繁)收集终结器对象。

标签: c# idisposable finalizer


【解决方案1】:

“规则”的基本要点是在一个终结器中,您只能调用具有已知活动根的对象(例如所有静态对象)和没有活动根的对象,这些对象也派生自 @987654322 @,除非你是CriticalFinalizerObject,否则你不能打电话给其他CriticalFinalizerObjects。

终结器所在的类中的大多数对象可能无法触摸,因为它们不在活动根上(毕竟你的类正在终结)并且它们不是从 CriticalFinalizerObject 继承的。

有一篇非常好的文章“What Your Mother Never Told You About Resource Deallocation”解释了在终结器期间可以做什么和不可以做什么,并解释了通过使用SafeHandle 对所有非托管资源的模式摆脱了您需要自己写一个终结器。这篇文章读起来很长,但试着通读一遍并理解它,它真的让我对我在处理托管和非托管对象方面所做的一切正确和错误的做法大开眼界。

本文通过并描述了如何使用SafeHandle,(从CriticalFinalizerObject 派生的类并且是IDisposable),因此您无需担心编写自己的终结器。

【讨论】:

  • 不过,在这种情况下,问题在于处置拥有非托管资源的托管资源。我没有操作系统级别的系统句柄。
  • 文章对此进行了介绍。 SafeHandle 不仅适用于具有句柄的资源。针对您的特定用例的文章部分称为“包装非托管资源 - 为非指针数据定义 0 级类型(硬案例)”
  • 刷新这个答案,我复制了错误的部分标题并修复了它,在该部分中您正在描述第二个实现,即不使用强制转换和 IntPtr 的那个。
  • 我想我不明白,我理解那部分是在谈论指针或可以用 ushort 表示的东西。在我的情况下,我有一个 StreamWriter 对象(整个对象)的实例,它自己的 Dispose 方法负责处理它在那个黑盒子中打开的任何资源,实际上它自己的 Dispose 必须是清理它自己的唯一东西资源。
  • 我想我不明白这个问题。但是,我可以在 cmets 中回答您的最后两个问题,对于 MVC,通常您使用的 IoC 容器将在对象的生命周期结束时处理对象的处置。对于Dispose(bool disposing) 问题,不,您不应该调用base.Dispose(),因为它可能会尝试使用可能已经完成的托管资源。但是,如果你有一个base.Dispose(disposing),你可以调用,那很好,因为你可以传入 false 来告诉基础不允许使用托管对象。
猜你喜欢
  • 2015-05-04
  • 2014-07-07
  • 2015-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-23
  • 2018-11-12
相关资源
最近更新 更多