2009-12-30 121 views
11

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

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

我读过,调用ThrowHelper方法(与抛出异常的唯一方法purpouse),而不是直接扔应该产生较小的字节码。

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

无论如何,国际海事组织的弊端也不是非实质性的。

  • 的(例外)控制流程被隐藏
  • 异常的一部分最终具有更隐蔽栈跟踪
  • 编译器(2.0)将无法识别ThrowHelper调用是出口点从一种方法,因此一些代码是必要的。

我有限的经验是,总体设计往往变得更糟。

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

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

btw我试图不要混淆这个问题与验证问题,例如,一种方法,如:

ThrowIfNameIsNullOrEmpty(name); 
+3

哇。我以前没有看到过这种模式,但是和我一样,我的直接反应非常消极。对绝大多数情况来说,字节码大小必定是不成熟的优化。 – bobince 2009-12-30 13:20:24

+1

微软在设计Stack时使用ThrowHelper :http://pastebin.com/p2k4URtU – 2012-05-18 19:54:11

回答

13

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

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

然而,而不是抛出的方法,这是更好的编写创建所需的异常工厂方法,然后从原来的地方把它:

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

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

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

这会保留堆栈跟踪。

1

我总是试图抛出我自己的例外,仅仅是为了定制的目的。我可以发出一个消息,告诉我在短期内问题是什么(可能来自哪里)。就性能问题而言,我尽可能地尽量限制在我的软件发行版本上抛出的异常数量,所以我从来没有真正想过太多。我有兴趣看看其他人说什么。

这是我的2美分。请这样拿。

7

这对我来说似乎是一个不必要的抽象。你有一种方法可以完成一件事,就像它所做的事情一样,被称为冗长的。

有关较少字节码的争论实际上毫无意义,因为代码的大小很少重要(除非您从您的程序的一个荒谬数目的地方抛出异常,否则不会节省超过每个程序实例的千字节源代码)。同时你所说的缺点都是真正的问题,特别是在涉及异常处理的清晰度方面。

基本上抽象这样的小事通常会回来咬你。 (一些有趣的阅读主题:http://www.joelonsoftware.com/articles/LeakyAbstractions.html

1

从我所了解的,较小的字节码几乎是唯一的优势(See this question),所以我不指望你会看到很多参数在这里的答案。

4

我想说这样做的唯一合理时间就在BCL这样的地方,那里的课程使用量足够广泛,节省可能是值得的。然而,这将是有道理的。就你而言,我认为你实际上并没有节省任何空间。 BCL中ThrowHelper的实例通过将枚举替换为字符串和方法调用(以获取字符串消息)来减小大小。你的情况只是通过相同的论点,因此没有实现任何节约。调用您的方法的成本与抛出异常的成本一样多。

+0

明白你的观点。如果权重不是使用'throw'关键字本身,而是参数传递,这种帮助器不会提高性能。 – smv 2009-12-30 13:43:53

0

使用throw-helper或exception-factory方法还没有提到的一个优点是可以为此目的传递一个委托。当使用投掷辅助代表时,可以获得不投掷的可能性(在某些情况下,这可能是一件好事;在其他情况下,这是一件坏事)。例如:

 
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'参数的委托,可以消除对闭包的需求。这个例子很简单,只是使用闭包。

+0

这很聪明,但我更喜欢更简单的方法......只是不要担心错误处理程序中的错误... :-) – smv 2012-04-24 21:02:37

相关问题