如何在C#中使用C++样式的析构函数?

bil*_*lpg 2 c# dispose idisposable using

我有一个带有Dispose函数的C#类IDisposable.它旨在用于using块内,因此它可以立即释放它所处理的昂贵资源.

问题是在Dispose调用之前抛出异常时发生了一个错误,并且程序员忽略了使用usingfinally.

在C++中,我从来不必担心这一点.对类的析构函数的调用将自动插入到对象范围的末尾.避免这种情况发生的唯一方法是使用new运算符并将对象保持在指针后面,但这对于程序员来说需要额外的工作并不是他们偶然会做的事情,比如忘记使用using.

是否有任何方法using可以在C#中自动使用块?

非常感谢.

更新:

我想解释为什么我不接受终结者答案.这些答案本身在技术上是正确的,但它们不是C++风格的析构函数.

这是我发现的错误,简化为必需品......

try
{
    PleaseDisposeMe a = new PleaseDisposeMe();
    throw new Exception();
    a.Dispose();
}
catch (Exception ex)
{
    Log(ex);
}

// This next call will throw a time-out exception unless the GC
// runs a.Dispose in time.
PleaseDisposeMe b = new PleaseDisposeMe();
Run Code Online (Sandbox Code Playgroud)

使用FXCop是一个很好的建议,但如果这是我唯一的答案,我的问题必须成为C#人的请求,或使用C++.二十个嵌套使用语句的人吗?

Her*_*shi 6

在我工作的地方,我们使用以下准则:

  • 每个IDisposable类必须有一个终结器
  • 每当使用IDisposable对象时,它必须在"using"块中使用.唯一的例外是如果对象是另一个类的成员,在这种情况下,包含的类必须是IDisposable,并且必须在其自己的'Dispose'实现中调用成员的'Dispose'方法.这意味着开发人员永远不应该调用'Dispose',除了在另一个'Dispose'方法中,消除了问题中描述的错误.
  • 每个Finalizer中的代码必须以警告/错误日志开头,通知我们已经调用了终结器.这样你就有很好的机会在发布代码之前发现如上所述的这些错误,而且它可能暗示你的系统中出现了错误.

为了让我们的生活更轻松,我们在我们的基础结构中也有一个SafeDispose方法,它在try-catch块中调用其参数的Dispose方法(带有错误记录),以防万一(尽管Dispose方法不应该抛出异常) ).

另见:Chris Lyon关于IDisposable的建议

编辑:@Quarrelsome:你应该做的一件事是在'Dispose'中调用GC.SuppressFinalize,这样如果对象被处理掉,它就不会被"重新处理".

通常还建议保持一个标志,指示物体是否已被丢弃.以下模式通常非常好:

class MyDisposable: IDisposable {
    public void Dispose() {
        lock(this) {
            if (disposed) {
                return;
            }

            disposed = true;
        }

        GC.SuppressFinalize(this);

        // Do actual disposing here ...
    }

    private bool disposed = false;
}
Run Code Online (Sandbox Code Playgroud)

当然,锁定并不总是必要的,但如果你不确定你的类是否会在多线程环境中使用,建议保留它.

  • "我在哪里工作,我们使用以下指南:每个IDisposable类必须有一个终结器......"更改您的指南. (3认同)