我什么时候应该使用ThrowHelper方法而不是直接投掷?

时间:2009-12-30 12:48:16

标签: c# exception throw

何时使用 ThrowHelper 方法而不是直接投掷?

void MyMethod() {
    ...
    //throw new ArgumentNullException("paramName");
    ThrowArgumentNullException("paramName");
    ...
}
void ThrowArgumentNullException(string paramName) {
    throw new ArgumentNullException(paramName);
}

我已经读过调用 ThrowHelper 方法(一个抛出异常的唯一方法)而不是直接抛出应该产生更小的字节码。

这和明显的封装(另一层间接)可能是不直接抛出的好理由,至少在某些情况下是这样。

无论如何,IMO的缺点也不是没有实质性的。

  • 隐藏(例外)控制流的一部分
  • 异常最终会产生更加神秘的堆栈跟踪
  • 编译器(2.0)无法识别 ThrowHelper 调用是方法的退出点,因此需要一些代码。

我有限的经验是整体设计经常变得更糟。

int MyMethod(int i) {
    switch (i) {
        case 1:
            return 1;
        default:
            ThrowMyException();
    }
    return 0; // Unreachable (but needed) code
 }

这可能部分是个人品味的问题。无论如何,你对这个问题有什么个人指导?您是否发现将 ThrowHelpers 用于所有常见任务(如方法参数验证( ThrowArgumentNullException(paramName)等)是一个好主意? 我在这个问题上遗漏了一些明显的东西吗?

顺便说一下,我试图不将此问题与验证问题混合,例如像这样的方法:

ThrowIfNameIsNullOrEmpty(name);

6 个答案:

答案 0 :(得分:15)

我的默认方法是直接从特殊代码分支抛出。

DRY原则和3指南当我将其包装在一个方法中时:如果我发现自己写了相同的'throw'代码3次或更多次,我会考虑将它包装在辅助方法中。

但是,不是抛出一个方法,而是编写一个创建所需Exception然后从原始位置抛出它的Factory方法更好:

public void DoStuff(string stuff)
{
    // Do something

    throw this.CreateException("Boo hiss!");
}

private MyException CreateException(string message)
{
    return new MyException(message);
}

这样可以保留堆栈跟踪。

答案 1 :(得分:8)

这对我来说似乎是一种不必要的抽象。你有一个做一件事的方法,就像它所做的那样详细命名。

关于较少字节码的论点实际上没有意义,因为代码的大小很少重要(除非你从源代码中的荒谬数量的地方抛出异常,否则每个程序实例的保存量不会超过一千字节)码)。与此同时,您所陈述的缺点都是真正的问题,特别是在涉及异常处理的清晰度的情况下。

基本上抽象掉这样的小事通常会让你感到厌烦。 (关于这个主题的一些有趣的阅读:http://www.joelonsoftware.com/articles/LeakyAbstractions.html

答案 2 :(得分:4)

我想说,这样做的唯一合理时间是像BCL这样的课程,其中课程的使用量足够广泛,节省可能是值得的。然而,它必须有意义。在你的情况下,我认为你实际上并没有节省任何空间。 BCL中ThrowHelper的实例通过将枚举替换为字符串和方法调用(以获取字符串消息)来减小大小。您的案例只是传递相同的参数,因此无法实现任何节省。调用方法的成本和抛出异常的成本一样多。

答案 3 :(得分:1)

我总是试图将自己的例外用于定制的目的。我可以抛出一条消息,告诉我短暂的问题是什么(以及它可能来自哪里)。就性能问题而言,我尽可能地限制在我的软件的发行版本上抛出的异常数量,所以我从来没有真正考虑过多。我很想看到其他人说的话。

那是我的2美分。请把它当作这样。

答案 4 :(得分:1)

从我的理解,较小的字节码几乎是唯一的优势(See this question),所以我不认为你会在这里的答案中看到很多关于它的论据。

答案 5 :(得分:0)

使用throw-helper或exception-factory方法尚未提及的一个优点是可以为此目的传递委托。当使用throw-helper委托时,可以获得不抛出的可能性(在某些情况下这将是一件好事;在其他情况下,这是一件坏事)。例如:

string TryGetEntry(string key, Funct<problemCause, string> errorHandler)
{
  if (disposed)
    return errorHandler(problemCause.ObjectDisposed);
  else if (...entry_doesnt_exist...)
    return errorHandler(problemCause.ObjectNotFound);
  else
    return ...entry...;
}
string GetEntry(string key)
{
  return TryGetEntry(key, ThrowExceptionOnError);
}
bool TryGetEntry(string key, ref result)
{
  bool ok;
  string result;
  result = TryGetEntry(key, (problemCause theProblem) => {ok=false; return (string)null;});
  return ok;
}

使用这种方法,可以轻松地使用一种具有各种错误处理策略的例程。通过让TryGetEntry方法接受泛型类型参数以及该类型的'ref'参数和接受这样的'ref'参数的委托,可以消除对闭包的需要;但是,这个例子更简单,只需使用闭包。