"使用"构造和异常处理

Tri*_*Gao 13 c# language-features design-patterns exception-handling exception

对于需要开始和分离结束部分的情况," 使用 "构造看起来非常方便.

快速举例说明:

using (new Tag("body")) {
    Trace.WriteLine("hello!");
}
// ...
class Tag : IDisposable {
    String name;
    public Tag(String name) {
        this.name = name;
        Trace.WriteLine("<" + this.name + ">");
        Trace.Indent();
    }
    public void Dispose() {
        Trace.Unindent();
        Trace.WriteLine("</" + this.name + ">")
    }
}
Run Code Online (Sandbox Code Playgroud)

开头部分定义为构造函数,结束部分是Dispose方法.

然而,尽管有吸引力,但这个结构有一个严重的警告,这个警告来自Dispose方法是从finally块中调用的.所以有两个问题:

  1. 您应该避免从finally块中抛出异常,因为它们将覆盖应该捕获的原始异常.

  2. 如果在"开始"和"结束"之间抛出异常,则无法知道Dispose方法的内部,因此无法相应地处理"结束"部分.

这两件事使得使用这种结构变得不切实际,这是一个非常可悲的事实.现在,我的问题是:

  1. 我对问题的理解是对的吗?这是"使用"实际上如何工作?

  2. 如果是这样,有没有办法克服这些问题,并实际使用"使用"结构,而不是它最初的设计(释放资源和清理)

  3. 如果没有实用的方法来"使用"这种方式.有哪些替代方法(使用开头和结尾部分强制执行某些代码的上下文)?

das*_*ght 5

您的规则#1适用于有或没有using,所以规则#2是真正的决胜局:选择一个try/ catch如果你一定要当一个异常被抛出的情况和正常的程序完成区分.

例如,如果持久层可能在使用数据库连接的过程中发现问题,则无论是否存在异常,您的连接都需要关闭.在这种情况下,using构造是一个完美的选择.

在某些情况下,您可以using专门设置以检测正常完成与异常完成.环境交易提供了一个完美的例子:

using(TransactionScope scope = new TransactionScope()) {
    // Do something that may throw an exception
    scope.Complete();
}
Run Code Online (Sandbox Code Playgroud)

如果在调用之前调用了scopes ,则知道已抛出异常,并中止事务.DisposeCompleteTransactionScope


Jor*_*dão 2

using语句和接口的目的IDisposable是让用户处置非托管资源。这些资源通常是昂贵且珍贵的,因此无论如何都必须处置它们(这就是为什么它出现在 上finally)。块中的代码finally甚至无法中止,并且可能会挂起整个应用程序域的关闭。

现在,为了你所描述的目的而滥用是非常诱人的using,我过去也这样做过。大多数时候那里没有危险。但是,如果发生意外异常,整个处理状态就会受到影响,您不一定要运行结束操作;所以一般来说,不要这样做。

另一种方法是使用 lambda,如下所示:

public interface IScopable { 
  void EndScope();
}

public class Tag : IScopable {
  private string name;
  public Tag(string name) {
    this.name = name;
    Trace.WriteLine("<" + this.name + ">");
    Trace.Indent();
  }
  public void EndScope() {
    Trace.Unindent();
    Trace.WriteLine("</" + this.name + ">");
  }
}

public static class Scoping {
  public static void Scope<T>(this T scopable, Action<T> action) 
    where T : IScopable {
    action(scopable);
    scopable.EndScope();
  }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

new Tag("body").Scope(_ => 
  Trace.WriteLine("hello!")
);
Run Code Online (Sandbox Code Playgroud)

您还可以创建其他实现,根据是否引发异常来运行某些操作。

在 Nemerle 中,可以使用新语法来扩展该语言以支持这一点。