如何做C++;C#中的样式析构函数?

如何做C++;C#中的样式析构函数?,c#,dispose,idisposable,using,C#,Dispose,Idisposable,Using,我有一个C#类,通过IDisposable具有Dispose函数。它旨在使用块在内部使用,因此可以立即释放它处理的昂贵资源 问题是,在调用Dispose之前抛出异常时,出现了一个bug,程序员忽略了使用using或finally 在C++中,我从来不用担心这个。对类的析构函数的调用将自动插入到对象作用域的末尾。避免这种情况发生的唯一方法是使用新操作符并将对象保持在指针后面,但程序员需要做的额外工作并不是偶然的,比如忘记使用using 有没有办法让using块在C#中自动使用 非常感谢 更新: 我

我有一个C#类,通过
IDisposable
具有
Dispose
函数。它旨在使用块在
内部使用,因此可以立即释放它处理的昂贵资源

问题是,在调用
Dispose
之前抛出异常时,出现了一个bug,程序员忽略了使用
using
finally

在C++中,我从来不用担心这个。对类的析构函数的调用将自动插入到对象作用域的末尾。避免这种情况发生的唯一方法是使用新操作符并将对象保持在指针后面,但程序员需要做的额外工作并不是偶然的,比如忘记使用
using

有没有办法让
using
块在C#中自动使用

非常感谢

更新:

我想解释一下为什么我不接受定稿人的答案。这些答案本身在技术上是正确的,但它们不是C++风格析构函数。

这是我发现的bug,简化为基本的

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();

使用<代码> FXCop <代码>是一个很好的建议,但是如果这是我唯一的答案,我的问题就必须成为对C人的恳求,或者使用C++。有人吗

~ClassName()
{
}
编辑(粗体):

当对象移出作用域并被垃圾收集器清理时,将调用If,但是这不是确定性的,并且不保证在任何特定时间发生。 这称为终结器。垃圾收集器会将所有具有finalizer的对象放在一个特殊的finalize队列中,并在其中调用finalize方法(因此,从技术上讲,声明空finalizer会影响性能)

对于非托管资源,框架指南中的“已接受”处置模式如下所示:

    public class DisposableFinalisableClass : IDisposable
    {
        ~DisposableFinalisableClass()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // tidy managed resources
            }

            // tidy unmanaged resources
        }
    }

因此,上面的意思是,如果有人调用Dispose,非托管资源将被整理。但是,如果有人忘记调用Dispose或出现异常阻止调用Dispose,则非托管资源仍将被清理掉,只是在稍晚的时候,GC会收到它的脏手套(包括应用程序关闭或意外结束).

不幸的是,在代码中没有直接执行此操作的方法。如果这是内部的问题,那么有各种代码分析解决方案可以解决这类问题。你调查过警察了吗?我认为这将抓住这些情况,在所有情况下,IDisposable对象可能会挂起。如果它是一个人们在您的组织之外使用的组件,而您不需要FxCop,那么文档确实是您唯一的资源:)


编辑:对于终结器,这并不能真正保证何时会发生终结。这可能是一个解决方案,但这取决于情况。

< P>这与在C++中忘记使用Debug的程序员没有什么不同,至少在这里垃圾收集器最终还是会赶上它。
如果您担心的唯一资源是内存,那么您永远不需要使用IDisposable。框架将自行处理这一问题。IDisposable仅适用于非托管资源,如数据库连接、文件流、套接字等。

@Quarrelsome

当对象移出范围并被垃圾收集器清理时,将调用If

这句话有误导性,我的理解是错误的:绝对不能保证何时调用终结器。你绝对正确,billpg应该实现一个终结器;但是,当对象超出其想要的范围时,不会自动调用它,Finalize operations下的第一个要点有以下限制


事实上,微软给了Chris Sells一笔资金来创建一个.NET实现,它使用引用计数而不是垃圾收集。事实证明,性能受到了相当大的影响。

一个更好的设计是让这个类在被释放之前自己释放昂贵的资源


例如,如果是数据库连接,则仅在需要时连接,并在实际类被释放之前立即释放。

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

  • 每个IDisposable类必须有一个终结器
  • 无论何时使用IDisposable对象,都必须在“使用”块中使用它。唯一的例外是,如果对象是另一个类的成员,则在这种情况下,包含的类必须是IDisposable的,并且必须在其自己的“Dispose”实现中调用该成员的“Dispose”方法。这意味着开发人员永远不应该调用“Dispose”,除非在另一个“Dispose”方法中调用,从而消除问题中描述的错误
  • 每个终结器中的代码必须以警告/错误日志开头,通知我们终结器已被调用。通过这种方式,在发布代码之前,您有极好的机会发现上述错误,另外,这可能是系统中出现错误的提示
为了让我们的生活更轻松,我们的基础设施中还有一个SafeDispose方法,它在try-catch块中调用参数的Dispose方法(带有错误日志记录),以防万一(尽管Dispose方法不应该抛出异常)

另请参见:关于IDisposable的建议

编辑: @争吵:您应该做的一件事是在“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;
}
当然,锁定并不总是必要的,但是如果您不确定是否会使用您的类
using (SqlConnection con = new SqlConnection("DB con str") )
using (SqlCommand com = new SqlCommand( con, "sql query") )
{
    //now code is indented one level
    //technically we're nested twice
}
PleaseDisposeMe a;
try
{
    a = new PleaseDisposeMe();
    throw new Exception();
}
catch (Exception ex) { Log(ex); }  
finally {    
    //this always executes, even with the exception
    a.Dispose(); 
}