在析构函数中使用 null 条件运算符是不好的做法吗?

Is using the null conditional operator in a destructor bad practice?

我有一个 class 和一个 析构函数 我从中得到一个 空引用异常 因为我销毁的变量是有时 null.

析构函数中使用空条件运算符是否合适?

我什至不确定这是否是对析构函数本身的适当使用,因为它不是用于处理调用它的实际对象,而是它的一个变量。

~clsSAPSettings()
{

    mtbTemp?.Close();
}

这段代码是从 VB6 转换而来的,所以我想弄清楚如何处理这个问题。欢迎任何信息。


编辑: class mtbTemp 属于法器 IDisposable 没有一个finaliser/desctructor。它只是关闭 ORM 模型中使用的连接。


对于经过详细解释后的任何人,我找到了一个很好的答案,Proper use of the IDisposable interface,它详细介绍了终结器的使用以及垃圾收集的实际工作原理。

当您关闭流或其他非托管对象时,您应该使用 Dispose 模式,而不是析构函数。你永远不知道那个析构函数什么时候会触发。

关于在析构函数中使用 null 条件运算符:我认为运算符本身没有问题。我会引用其他可能已经被破坏或正在破坏的对象。

请不要在终结器中使用任何引用类型字段:GC(垃圾收集器)收集它们的顺序是不可预测 这就是为什么

 ~clsSAPSettings()
 {
     mtbTemp?.Close();
 }

代码可以很好地被GC执行如下:

  1. 收集mtbTemp个实例
  2. 开始收集this个实例:
  3. 致电~clsSAPSettings()
  4. 调用mtbTemp?.Close(); 即调用已收集(已销毁)实例
  5. 的方法

并且您将遇到 不稳定且难以发现的错误。看来你要找的是IDisposable接口:

 public class clsSAPSettings: IDisposable {
   private MyTemp mtbTemp;

   ... 

   protected virtual void Dispose(bool disposing) {
     if (disposing) { 
        mtbTemp?.Close();

        GC.SuppressFinalize(this); 
     }
   }  

   public void Dispose() {
     Dispose(true);
   }

   //TODO: do you really want finalizer?
   ~clsSAPSettings() {
     Dispose(false);
   } 
 }

考虑:

~clsSAPSettings()
{    
    mtbTemp?.Close();
}

这里的问题不是 null 条件用法。这本身并不存在任何问题。

最大的问题是在终结器中你不应该接触任何其他对象。当终结器触发时,您的对象就烤好了。您不再对 mtbTemp 的生命周期有任何保证,包括它是否已经被垃圾回收,因此您 不应触摸它 。它可能会起作用;它可能会导致暂时的复活,或者它可能会可怕地崩溃。

执行此类操作的正确位置是 IDisposable.Dispose。在 Dispose 方法中,这绝对没问题:

public void Dispose() // where your class : IDisposable
{    
    mtbTemp?.Close();
    mtbTemp = null;
}

您可能根本不需要终结器。它们非常罕见。