C# 如何在异常情况下正确处理增强类?

C# 如何在异常情况下正确处理增强类?,c#,dispose,raii,using-statement,C#,Dispose,Raii,Using Statement,我最近刚开始在我们的几个项目中使用完整的代码分析规则集。真正让我更加仔细地思考IDisposable类的一个警告是。我有很多这样的例子,抱怨一些一次性对象没有在每个异常路径上被处理 因此,问题是:在返回之前,在方法的其余部分出现异常的情况下,处理对象处理的更正确方法是什么?例如,此方法: public MyDisposable GetMyDisposable() { var disposable = new MyDisposable(); disposable.MethodTha

我最近刚开始在我们的几个项目中使用完整的代码分析规则集。真正让我更加仔细地思考IDisposable类的一个警告是。我有很多这样的例子,抱怨一些一次性对象没有在每个异常路径上被处理

因此,问题是:在返回之前,在方法的其余部分出现异常的情况下,处理对象处理的更正确方法是什么?例如,此方法:

public MyDisposable GetMyDisposable()
{
    var disposable = new MyDisposable();
    disposable.MethodThatCanThrowExceptions();
    return disposable;
}
将引发这样的警告,因为如果canthrowexceptions的
方法确实抛出异常,调用方将不会收到一次性实例,因此在垃圾收集器启动之前,没有人能够处理它

我使用的模式大致如下:

public MyDisposable GetMyDisposable()
{
    var disposable = new MyDisposable();
    try
    {
        disposable.MethodThatCanThrowExceptions();
    }
    catch
    {
        disposable.Dispose();
        throw;
    }
    return disposable;
}
但我看到一些人提到使用
finally
块代替catch,使用布尔值表示代码的其余部分有问题,如下所示:

public MyDisposable GetMyDisposable()
{
    bool ok;
    var disposable = new MyDisposable();
    try
    {
        disposable.MethodThatCanThrowExceptions();
        ok = true;
        return disposable;
    }
    finally
    {
        if (!ok) disposable.Dispose();
    }
}
public MyDisposable GetMyDisposable()
{
    using (var disposable = new MyDisposable())
    {
        disposable.MethodThatCanThrowExceptions();
        return release disposable;
    }
}
在这两者之间,处理这种情况的最佳方法是什么?有没有其他更简单的方法来处理这种情况?我用C++编程了一点,用这种语言,通常使用一个类来处理这些可替换的情况,称为范围保护。据我所见,该模式支持
Release
方法,因此您可以使用处置行为包装代码,但在某些情况下取消实际处置。在C#中,我假设它看起来是这样的:

public MyDisposable GetMyDisposable()
{
    bool ok;
    var disposable = new MyDisposable();
    try
    {
        disposable.MethodThatCanThrowExceptions();
        ok = true;
        return disposable;
    }
    finally
    {
        if (!ok) disposable.Dispose();
    }
}
public MyDisposable GetMyDisposable()
{
    using (var disposable = new MyDisposable())
    {
        disposable.MethodThatCanThrowExceptions();
        return release disposable;
    }
}
我知道这样的东西在C#中不存在,但如果有类似的东西或干净的东西就好了。

在这种特定情况下(我可能会补充这一点,这是相当不寻常的),当您返回可支配资源时,您将处理对象的责任从这个方法传递给它的调用方


显然,返回一个已经处理过的对象对调用者来说是毫无意义的,因为他们永远无法使用它。这就排除了最后一个选项

您希望在无法正确初始化对象时处置该对象,这意味着在出现异常时处置该对象,这正是您的第一个提案以最直接的方式所做的。第二种方法的功能与第一种方法相同,只是在如何进行时并不那么简单


如果有一种方法负责处理资源,并且一次性资源的生命周期与存储资源的变量的范围相同,则使用
finally
块(为了简单起见,通常隐藏在
Using
的内部)是合适的。绝大多数可支配资源的使用都是如此,但这一种并不特别。

许多语言需要令人烦恼的笨拙结构来发现何时发生异常而不捕获异常。这通常会迫使人们在使用笨拙但语义精确的代码来绕过该限制的同时避免捕获无法处理的异常,或者容忍捕获不可能处理的异常然后重新调用该异常的语义不精确性之间进行权衡。我个人的偏好是使用语义正确但笨拙的代码,直到语言实现者允许清晰地编写语义正确的代码为止,但许多其他人更喜欢容忍捕获和无条件重新引用异常的语义松散性

请注意,始终避免此类捕获和重新捕获行为会带来一些好处,但如果代码的其余部分充斥着此类行为,那么在一些地方避免此类行为将不会带来太多好处。容忍语法上的笨拙来保持语义上的整洁是一件好事,但是如果事情已经在语义上变得肮脏,那么编写语法上笨拙但无法修复底层肮脏的代码可能就不值得了


当然,如果.NET语言的维护者能够让代码以语法清晰的方式做正确的事情,那么整个问题将变得毫无意义。

您的第二个代码片段就是正确的方法。第三个片段也可以工作,我将初始化局部变量:
boolok=false。你能详细解释一下为什么
catch
方法是“正确的”方法吗?也许是在回答中?另外,关于初始化,我认为这是有争议的,但是我倾向于遵循Resharper的警告,在这种情况下,这将是一个冗余的初始化,因为
bool
的默认值是
false
。我只是不喜欢在没有变量的情况下使用额外的变量。这也是我更喜欢
catch
方法的原因,但我希望看到某种官方或“经验证”的最佳方法。我有一些疑问,例如,两种方法是否具有相同的性能。我们都知道异常处理是“慢”的(当实际捕获异常时,抛出异常时就不那么慢了),所以我想知道
catch
版本是否会以某种方式变慢,即使它没有捕获变量的异常。“显然,返回一个已经处理过的对象对调用者来说毫无意义,因为他们永远无法使用它。这排除了最后一个选项。“我想你误读了我的代码。在上一个例子中,我比较了一个假定的新构造(new
release
关键字)使用C++范围保护类。我意识到使用块的普通<代码>根本不起作用。@ JuleAlgon在这种情况下是的,它不存在于C*中,因此不可能。而且,鉴于我们当前的代码和多少警告,我不同意您关于一次性所有权的所有权的声明。根据我的经验,这种情况经常发生。@julealgon然后是代码库