【问题标题】:How to properly dispose locally created object in another method?如何以另一种方法正确处理本地创建的对象?
【发布时间】:2020-01-28 08:05:26
【问题描述】:

所以我有一个实现 IDisposable 的类,并且我有几个方法(在另一个类中)遵循以下模式:

public void SomeMethod()
{
    DisposableObject disposableObject = new DisposableObject();

    // Do some stuff with the object

    SomeOtherMethod(disposableObject);
    disposableObject.Dispose();
}

虽然所有这些方法都做不同的事情,但它们最后都会调用SomeOtherMethod,这会在不再需要一次性对象之前对它做更多的事情。 当我将disposableObject.Dispose(); 移动到SomeOtherMethod 时,Visual Studio 会给我一条消息说: "使用推荐的 dispose 模式来确保 'new DisposableObject()' 创建的对象在所有路径上都被释放:using statement/declaration 或 try/finally"

无论我是否使用 ref 关键字将一次性对象传递给 SomeOtherMethod,都会显示此消息。

我的问题是,只要SomeOtherMethod 调用Dispose() 就可以处理该对象吗?我假设它会,并且 Visual Studio 继续发送消息只是因为它没有“知道”在后续方法中该对象发生了什么,但我很想得到一些确认!

【问题讨论】:

  • 无论哪个 scope 分配对象都拥有它,并且应该是处置它的对象。简单的。在上面放一个using 块。使用“转移语义”,即赋予SomeOtherMethod 所有权并让其处置对象,可能会导致混淆,最好避免。
  • 即IDE0067,代码分析警告。它试图告诉您,如果该方法抛出异常,则该对象可能不会被释放。尽管代码分析通常难以生成关于 IDisposable 使用情况的良好诊断,但这可能是正确的。您确实喜欢这里的 using 语句。只有在 Dispose() 调用后该方法需要很长时间才能执行时,在被调用方法中处理它才是明智之举。

标签: c# idisposable


【解决方案1】:

它可能被释放或不被释放,取决于执行是否会到达Dispose 调用这一事实,这是因为在调用Dispose 之前可能会引发异常。通过 using 关键字显式或隐式使用 try finally 构造可确保在任何情况下都会调用它,这就是 VS 向您发出警告的原因。

【讨论】:

    【解决方案2】:

    该对象会被处置

    抱歉,这是一个毫无意义的问题。 CLR 不会跟踪对象是否已调用其“dispose”方法(请参阅Will the Garbage Collector call IDisposable.Dispose for me?

    作为一般规则,创建问题的方法也应该是自行清理的方法总是更好(可读/可维护/不易出错/等)。正如您刚刚发现的那样,这种模式还允许编译器进行自动检查 - 同样,这也是确保您的代码干净地编译而没有错误或警告的好规则。

    在这种情况下,警告为您提供了几种干净地实现此功能的方法;就个人而言,我更喜欢“使用”子句(因此避免必须明确调用“处置”),例如:

     public void SomeMethod()
     {
         using (DisposableObject disposableObject = new DisposableObject() )
          {
    
               // Do some stuff with the object
    
               SomeOtherMethod(disposableObject);
           }
    

    }

    【讨论】:

    • 谢谢,这或多或少是我正在寻找的答案。所有的答案基本上都指出了同一件事,但这对我来说最有意义。
    【解决方案3】:

    无论在哪里调用Dispose(),都会被调用。

    不使用语言关键字using 作为一次性模式,因此将 dispose 移动到另一种方法中,是一种反模式,因此它是一种不好的做法,也是潜在问题的根源。

    您只能通过在项目构建设置中添加警告编号来删除警告。

    Dispose() 方法不会破坏对象。

    dispose 模式仅用于释放非托管资源,例如窗口句柄和共享内存。

    在调用Dispose() 之后,您仍然拥有对象,因此对对象的引用仍然在托管内存中被引用。

    Dispose() 在对象使用结束时被调用一次,仅此而已。

    编译器会向您发送警告,因为您违反了使用 using 关键字的模式用法的标准行为。

    违反标准可能是问题的根源。

    使用一次性对象标准是为了避免错误,让编译器生成try { ... } finally { Dispose() } 块以确保在正确的位置正确调用Dispose() 以避免错误。

    所以避免直接调用Dispose()

    除非您确定自己在做什么,否则最好使用:

    public void SomeMethod()
    {
      using ( DisposableObject disposableObject = new DisposableObject() )
      {
        // Do some stuff with the object
        SomeOtherMethod(disposableObject);
      }
    }
    

    而且你的代码可能很健壮。

    【讨论】:

    • "Dispose() 在对象使用结束时被调用一次不再调用。"。不同意“不再”部分。 Dispose 方法应该是 idempotent
    • @Artur 我同意...如果 Dispose 实现正确完成...但不要认为情况总是如此,因为在计算机科学中,永远不应该相信实现能够做到如果规范没有明确引用它...IDispose 是一个没有实现的接口,适用于所有编码人员。 .NET Framework 类当然可以正确完成这些事情,但是任何人都可以为自己或 IDisposeusing 关键字实现好的或坏的 Dispose()... 因此存在模式和编译器警告.
    • 我不是在谈论规则是否总是得到满足,我是说您陈述了错误的陈述,例如 it is 规则。但是你回答的主要问题是它根本没有回答这个问题。 OP 询问有关将 Dispose 调用移动到 SomeOtherMethod 而不是“实现 Disposable 模式的正确方法”。
    • OP 对编译器警告感到恼火。无论在哪里调用 dispose,它都会被调用。我回答说不使用语言关键字using 作为一次性模式,因此以另一种方法移动 dispose 是一种反模式,因此它是一种不好的做法,也是潜在问题的根源。 OP 只能通过在项目构建设置中添加警告号来消除警告。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    • 1970-01-01
    相关资源
    最近更新 更多