Dispose应该做什么 - 只清理资源还是执行一些“业务逻辑”?

3
考虑到IDisposable的语义,除了清理资源之外,Dispose方法应该做什么?
我发现许多类使用Dispose方法来启动关闭进程和其他一些操作,而不仅仅是进行“资源清理”工作。例如,如果您在类中有一个需要关闭的线程,您是否希望调用Dispose时能够优雅地关闭这个线程?
在我看来,我会在对象上实现一个Stop / Shutdown方法,并在那里优雅地关闭线程,而在Dispose中,我会检查线程是否仍然存活,然后简单地调用Abort方法(暗杀式 :))。
同样的例子也可以用于定时器和所有其他资源,在完成对象的工作之前都可以有某种终止过程。
.NET中两个设计上的冲突示例:
- ServiceBase用于Windows服务,具有OnStop和Dispose可重写。 - OWIN的WebApp.Start用于自托管,返回一个IDisposable,我猜想它在Dispose过程中执行服务关闭。
2个回答

2

为了更加严谨,我仅在业务逻辑中使用dispose。例如:

public class UsingBase : IDisposable
{
    protected Action end;
    public UsingBase(Action start, Action end) { this.end = end; if (start != null) start(); }
    public void Dispose() { if (end != null) end(); }
    public void Cancel() { end = null; }
}

class HideCursor : UsingBase { public HideCursor() : base(() => Console.CursorVisible = false, () => Console.CursorVisible = true) { } }

public static void DisplayTitle(...)
{
    ...
    using (new HideCursor())
    {
       //Display the title
    }
    ...
}

虽然有些人可能认为这是对语言的滥用,但我从ASP.net API中得到了这个想法。

@using (Html.BeginForm())
{
    ...
}

回到你的问题

例如,如果您在类中有一个线程需要关闭,您是否希望调用Dispose方法来优雅地关闭此线程?

如果有意义的话,我希望是这样的 - 因为我经常使用带有using的对象,让它自己处理清理。当然,如果您清楚地记录了逻辑,并且有明确的理由区分“优雅停止”和“dispose”,那么这也可以。

换句话说 - 我不想没有理由地这样做:

using (var blah = new Blah())
{
...
...
...
Blah.SpecialDispose();
}//Blah.Dispose();

因为这会导致使用尝试解决的确切问题 - 人们忘记编写正确的包装代码(例如,try finally)。


编辑:

总之,是的,业务逻辑可以在Dispose中完成。是的,Dispose可以'很好地'处理事情。这些是您提出的主要问题。然而,这并不一定否定了Dispose作为“最后手段”并且其行动更加突然的可能用例。听起来您对自己的用例很有信心,如果是这样,请继续。如果您仍然需要反馈,请发布与您实际用例相关的更具体的问题。


是的,你说得有道理。有时,对于某些用途,滥用“using”也可以很好地发挥作用。然而,我也可以说我正在从“关闭”真正成为对象定义/身份/众所周知的一部分的角度来看待这个问题,而不是可能会被遗忘的东西。 - Denis Biondic
你知道使用dispose和using的不同方式,你已经查看了备选用例,并且仍然相信你的使用是有效和合理的吗?除非有人发布内容来改变上述情况,否则似乎你正在做出明智的决定 - 我又有什么资格去争论呢? - NPSF3000

1
与其按照IDisposable契约思考,更有帮助的是将IDisposable视为一般Object契约的一部分,该契约规定对象在被弃用之前不应需要任何形式的通知(未实现IDisposable的对象根本不需要任何通知)。Dispose方法应执行所有必要的操作,以便在可以安全地放弃对象之前完成清理。
唯一具有问题的方面是,在执行清理时可能会出现异常,而Dispose无法知道在这种情况下它应该做什么。如果在正常(非异常)程序执行期间调用Dispose,则如果清理失败,它应抛出异常。但是,如果在从异常中解开堆栈的过程中调用它,则它可能抛出的任何异常通常都会销毁早期异常的所有证据(因此最好是默默地失败而不是抛出异常)。不幸的是,Dispose无法告诉哪种情况适用,因此在清理失败的情况下无法始终执行正确的操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接