17

直接スローする代わりに、 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
 }

これは部分的に個人的な好みの問題かもしれません。とにかく、この問題に関するあなたの個人的なガイドラインは何ですか? メソッド パラメーターの検証 ( ThrowArgumentNullException(paramName)など) などの一般的なタスクすべてに、 ThrowHelpersを使用することをお勧めしますか? この問題について明らかな何かが欠けていますか?

ところで、この問題と検証の問題を混同しないようにしています。たとえば、次のような方法です。

ThrowIfNameIsNullOrEmpty(name);
4

7 に答える 7

19

私のデフォルトのアプローチは、例外的なコード ブランチから直接スローすることです。

DRY の原則と 3 のルールは、それをメソッドにラップする際のガイドです。同じ「スロー」コードを 3 回以上書いていることに気付いた場合は、ヘルパー メソッドでラップすることを検討します。

ただし、スローするメソッドの代わりに、目的の例外を作成し、元の場所からスローするファクトリ メソッドを作成する方がはるかに優れています。

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

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

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

これにより、スタック トレースが保持されます。

于 2009-12-30T12:59:18.997 に答える
9

これは私には不必要な抽象化のように思えます。1 つのことを実行するメソッドがあり、それが実行することと同じくらい詳細に名前が付けられています。

コードのサイズはめったに問題にならないため、バイトコードが少ないという議論は実質的に無意味です (ソース コードの途方もない数の場所からその例外をスローしない限り、プログラムのインスタンスごとに 1 キロバイト以上節約することはできません)。一方、あなたが述べた欠点は、特に例外処理の明確さが懸念される場合、すべて本当の懸念事項です。

このような些細なことを基本的に抽象化すると、通常は戻ってきて噛み付きます。(このテーマに関する面白い読み物: http://www.joelonsoftware.com/articles/LeakyAbstractions.html )

于 2009-12-30T12:59:41.093 に答える
5

これを行うための唯一の合理的な時間は、クラスの使用量が十分に広がっているBCLのようなものであり、節約する価値があるかもしれません。ただし、それは理にかなっている必要があります。あなたの場合、実際にスペースを節約しているとは思いません。BCLのThrowHelperのインスタンスは、文字列とメソッド呼び出し(文字列メッセージを取得するため)を列挙型に置き換えることでサイズを縮小します。あなたのケースは単に同じ引数を渡すだけなので、節約にはなりません。メソッドを呼び出すのは、例外を適切にスローするのと同じくらいの費用がかかります。

于 2009-12-30T13:09:34.733 に答える
3

私が理解していることから、バイトコードが小さいことが唯一の利点です (この質問を参照してください)。

于 2009-12-30T13:05:53.347 に答える
1

私は常に、単なるカスタマイズの目的で独自の例外をスローしようとしています。問題が何であるか (そしておそらくどこから発生したか) を簡単に教えてくれるメッセージを投げることができます。パフォーマンスの問題に関しては、ソフトウェアのリリース バージョンでスローされる例外の数をできるだけ制限しようとしているので、実際にあまり考えたことはありません。私は他の人が何を言っているのか興味があります。

それが私の2セントです。そのまま受け取ってください。

于 2009-12-30T12:58:19.647 に答える
0

throw-helper メソッドまたは exception-factory メソッドを使用することのまだ言及されていない利点の 1 つは、そのような目的でデリゲートを渡す可能性です。スロー ヘルパー デリゲートを使用すると、スローされない可能性が生じます (これは、場合によっては良いことであり、他の場合には悪いことです)。例えば:

string TryGetEntry(string key, Funct<problemCause, string> errorHandler)
{
  if (処分)
    return errorHandler(problemCause.ObjectDisposed);
  そうでなければ (...entry_doesnt_exist...)
    return errorHandler(problemCause.ObjectNotFound);
  そうしないと
    戻ります...エントリ...;
}
文字列 GetEntry(文字列キー)
{
  Return TryGetEntry(key, ThrowExceptionOnError);
}
bool TryGetEntry(文字列キー、参照結果)
{
  大丈夫です。
  文字列の結果;
  result = TryGetEntry(key, (problemCause theProblem) => {ok=false; return (string)null;});
  大丈夫です。
}

このアプローチを使用すると、さまざまなエラー処理戦略で 1 つのルーチンを簡単に使用できます。TryGetEntry メソッドがその型の 'ref' パラメーターとそのような 'ref' パラメーターを受け入れるデリゲートと共にジェネリック型パラメーターを受け入れるようにすることで、クロージャーの必要性をなくすことができます。ただし、クロージャーを使用するだけで、この例はより単純になります。

于 2012-01-30T18:44:45.030 に答える
0

バイトコードを小さくしても数キロバイトしか節約できないように見えますが、メソッドを実行するためにフェッチ/ロードする必要があるコードが増えるため、明らかにパフォーマンスに「影響します」。すべてのマイクロ最適化が重要な高パフォーマンスのコードを書く場合、それは重要です。

しかし、より重要なことは、ローカライズできること、またはいつか例外メッセージをローカライズすることです。

于 2022-01-01T18:55:24.927 に答える