【问题标题】:Cancel dispose inside Dispose method在 Dispose 方法中取消 dispose
【发布时间】:2013-03-20 19:21:00
【问题描述】:

有没有一种方法可以在 Dispose 方法中取消处理,换句话说,在调用 A.Dispose() 时不处理 A。

更新 我没有很好地解释,因为我想不出一个很好的说法。我所拥有的是一个在单独线程中运行的弹出窗口中显示进度条(选取框)的类。当创建进度弹窗时,会返回一个类似控制器的一次性对象,所以我们可以这样做:

using (var progress = Dialogs.ShowProgress("Please wait..."))
{
    // do lots of stuff here
    progress.UpdateStatus("Please wait some more...");
    // do more stuff
}

当 using 块退出时,进度窗口关闭并销毁。

现在我遇到这样的情况,在这个块中发生的事情也会创建一个进度窗口,而不是返回一个新对象并创建一个新窗口,我想返回第一个实例创建的对象,并且只在一审处理。

更新 好的,我所做的是为每个 Dialogs.ShowProgress() 调用创建一个新的 IDisposable 对象,并将原始控制器对象附加到它上面。这些对象一旦完成就会被释放,而控制器只有在它是唯一需要释放的对象时才会被释放。

【问题讨论】:

  • 你到底为什么要这么做?基本上你是在问“我有这份合同说我必须做 X,但我不想 - 我该如何摆脱它?”
  • 我在这里闻到了框架滥用的味道。如果整个 .NET 框架不需要取消 Dispose() 调用,您认为您真的需要吗?
  • 您将 Dispose 方法与 finalizer混淆了,该方法是一种约定(在 .NET Framework 中广泛使用,但不是一种语言,只是一种约定) >(具有不同的语法),并且 仍然 你不能取消终结器(这没有意义)... "cancel" Dispose只是……不打电话给Dispose
  • @Jcl:实际上有一种方法可以“取消”终结器。当 GC 将引用归类为“已死且需要终结”时,它会将其放入终结器队列中,此时它再次变为“活动”。如果终结器导致对该对象的引用保持活动状态,那么您再次拥有一个真正的活动对象,GC 不会清理,直到它再次死亡。 (事实上​​,如果对象希望终结器在第二次死亡时再次运行,它可以将自己标记为“将我视为从未终结”。)这些技术仅适用于高级玩家。
  • @Jcl:你可以用普通的 C# 来做。池化策略经常使用这种技术。当对象即将被 GC 释放时,GC 将其放入终结器队列中,当终结器运行时,它将对象重新放回池中,使其处于活动状态,并告诉对象“你还没有已完成”,以便下一次它绕过这个业力循环,它会再次复活。

标签: c# dispose


【解决方案1】:

正如其他人所说,以可能不会真正处置的方式实施IDisposable 是一个坏主意。这是实现您正在尝试做的事情的另一种方法:

interface IMaybeDisposable
{
    bool TryDispose(); //return false if dispose was canceled
}

你可以这样使用它:

IMaybeDisposable maybe = //something
try
{
    // do something with maybe
}
finally
{
    if (maybe.TryDispose())
        // yay, it disposed!
    else
        // hm, it didn't dispose; do something else
}

【讨论】:

  • +1 这实际上是一个可靠的解决方案,可以避免在using中放错对象
  • +1 何时有人真正需要这个仍然值得怀疑,但至少这个界面清楚地传达了正在发生的事情。
  • @DanielHilgarth 我同意。其他地方的糟糕设计需要这一点
  • 也许重命名方法TryDispose 以更清楚地表明它不是常规的处理。因为可以很容易地调用这个Dispose 而没有意识到有一个返回值。
  • @Servy 我喜欢这样,并将其添加到我的答案中。谢谢!
【解决方案2】:

当然。只是不要做任何你会做的事情来处理对象。你不需要积极地做任何特别的事情。

不过要小心;当有人处置一个对象时,他们希望它实际处置它的非托管资源,而不是无视您并无论如何都持有它们。

【讨论】:

  • 这在什么方面不能完全按照要求回答问题?
  • 哦,我根本不会敲你的,@Servy - OP 问题是我的目标,抱歉 :)
  • @JerKimball 是的,这就是我没有回复你的原因。它是针对那些不赞成答案的人。
  • 那里,我可以让你回到 null,至少 :)
【解决方案3】:

Dispose 所需的后置条件是客户端代码应该能够放弃对对象的所有引用,而不会导致任何必要的清理工作未完成。如果该条件在调用Dispose 之前适用,那么Dispose 方法可能什么也不做。如果该条件不适用,Dispose 必须这样做,并且可能不会合法返回,除非或直到它这样做 [注意 Dispose 不需要实际执行清理,如果它启动了一系列事件清理将在稍后进行]。 Dispose 接口的合约中没有任何内容允许它有时将对象置于需要进一步清理的状态,除非它有理由相信清理将通过其他方式进行。

【讨论】:

    【解决方案4】:

    我会小心这样做。如果您在 using 块的末尾取消处置方法,那么您将丢失对该对象的引用,并且您将永远没有机会再次处置其内容,这意味着您可能会泄漏资源不小心。特别是,如果 dispose 方法释放了对我不希望出于任何原因立即收集垃圾的大对象的引用,我只会自己执行此操作。我不会这样做来跳过释放文件、数据库连接、网络套接字或其他任何不能被垃圾收集的句柄。

    正如评论者指出的那样,我会尝试重构我的代码,以便我试图通过取消处置来解决的任何问题都可以通过其他不那么危险的方式来解决。

    如果您完全确定自己有需要跳过处理某些东西的边缘条件,这里有一种方法:

    public void Dispose()
    {
         if(condition)
         {
             // do disposing stuff
         }
    }
    

    【讨论】:

    • 尽管有解释,但仍然是个可怕的想法。 using 不支持有条件的一次性对象
    • @StenPetrov !condition 本身就是一种条件,所以你的代码完全一样。
    • @StenPetrov:答案显示了如何按照 OP 的要求去做,然后详细解释了为什么这是一个坏主意。我真的不明白你的评论是针对什么的。
    • @Servy 我也这样做了。但是有一种常见的“期望偏差”仍然会误导提出问题的人选择错误的答案,而不是认为他们的问题是错误的
    • @StenPetrov 如果您的目标是传达这一点,而不是让 Dispose 有条件地处置对象,则应重构代码,以便调用者只需有条件地对该对象调用处置,那么您应该说那个,因为您在评论中没有特别清楚地传达它。如果这就是你的意思,那么我会说这可能是一个有效的解决方案,它肯定比 OP 所要求的要好,尽管在不了解上下文的情况下很难讨论什么是真正合适的。
    【解决方案5】:

    根据您的更新,我会这样做:

     public  void  DoStuff()
    {
         DoPrepWork();
         using( var  controller =  TheMethodYouWroteThatShouldReturnAController())
        {
             DoStuffOverControllerLifetime();
        }
    }
    

    【讨论】:

    • 这很好,但是创建嵌套使用的东西也可以在其他地方调用,然后它们将成为初始控制器的创建者。当然,我也可能误解了你的回答。
    猜你喜欢
    • 2013-10-12
    • 1970-01-01
    • 2023-04-10
    • 2017-04-25
    • 2020-04-22
    • 1970-01-01
    • 2014-10-12
    • 1970-01-01
    相关资源
    最近更新 更多