在try{}中设置字段值,并在finally{}中调用基类

本文关键字:并在 finally 调用 基类 字段 try 设置 | 更新日期: 2023-09-27 18:20:26

我正在浏览.NET Framework源代码,试图理解另一个问题,我看到了以下代码(在System.Net.PeerToPeer.CollaborationPeerNearMe.cs中):

private bool m_Disposed; 
protected override void Dispose(bool disposing)
{ 
    if (!m_Disposed){
        try{
            m_Disposed = true;
        } 
        finally{
            base.Dispose(disposing); 
        } 
    }
}

是否有任何理由将变量赋值放在try块中?除了一个例外,它可能会以任何方式失败吗?!起初我认为这是因为即使线程被Thread.Abort():中止,finally也会被执行

未执行的finally块在线程中止之前执行。

在这种情况下,我希望try块包含所有方法体:

try {
    if (!m_disposed)
        m_disposed = true;
}
finally {
    base.Dispose(disposing)
}

然而,他们也说:

如果被中止的线程位于代码的受保护区域中,例如catch块、finally块或受约束执行区域,则调用Abort的线程可能会阻塞。如果调用Abort的线程持有中止线程所需的锁,则可能发生死锁。

在这种情况下,IMO调用基类虚拟方法有点像黑暗中的跳跃。

简而言之:这段代码的意义何在?它真正想要实现什么?如果是因为Thread.Abort(),那么这难道不是错误的吗?

EDIT:如果它真的只是因为Thread.Abort(),那么如果中止发生在if (!m_Disposed) {之后但在try {之前,它就不会完成它的工作。请注意,Dispose()必须对多个调用具有弹性,并且不执行任何操作(无论何时调用)。

在try{}中设置字段值,并在finally{}中调用基类

唯一可能发生的是异步异常——Thread.Abort就是一个例子,但也有类似的Thread.InterruptOutOfMemoryException

你的建议实际上破坏了代码,因为不管实例是否已经被处理,你都会调用base.Dispose——这不是目的。

现在,Thread.Abort应该只在终止应用程序域时使用——所以你不在乎m_disposed = true是否成功,反正域很快就会被拆除。然而,您仍然关心释放任何本机资源,因为这些资源通常与进程绑定,而不是与应用程序域绑定(有时,它们甚至超越进程或整个机器)。

即使在Thread.Abort的中间,finally中的代码也有机会运行——没有其他方法可以确保代码在异步异常期间运行。通过在finally子句中调用base.Dispose,您可以确保它至少有机会执行,并且不会在操作过程中终止(但请注意,所有执行的finally子句都有固定的时间限制-您不想在finally中做任何复杂的事情)。

现在,在这种特殊的情况下,没有真正的理由这样做——基类也不做任何事情。因此,这可能只是团队使用的一种常见的Dispose模式:)由于Dispose是为本机资源的确定性发布而设计的,因此在finally子句中调用它是完全安全的——它不应该做任何工作,只应该发布本机资源。当然,Dispose经常被滥用,但你只能收获你所播种的。

最后,不要忘记这也是using子句的作用,所以如果您使用using,那么您已经在finally子句中运行了Dispose方法!

using (var bmp = new Bitmap())
{
  ...
}

翻译成

Bitmap bmp = null;
try
{
  bmp = new Bitmap();
  ...
}
finally
{
  if (bmp != null) bmp.Dispose();
}

总而言之,这个实现没有什么可疑之处,真的:)

我不确定为什么它只是在尝试,因为它只分配了一个变量,也许其他人可以告诉我们。

我想说,你的期望会有所不同。将整个try finally放在if(!m_Dispose)中意味着,如果m_Dispose为true,则对象不会调用Dispose,而不管m_Dispose值如何,您的期望都会调用Dispose。