10

この質問は、メソッドが失敗したことを示す方法からのフォローアップです。xxx()Tryxxx()パターンは、多くのライブラリで非常に役立つものです。コードを複製せずに両方の実装を提供するための最良の方法は何でしょうか。

最高のもの:

public int DoSomething(string a)
{
     // might throw an exception
}
public bool TrySomething(string a, out result)
{
    try
    {
        result = DoSomething(a)
        return true;
    }
    catch (Exception)
    {
        return false;
    }

また

public int DoSomething(string a)
{
     int result;
     if (TrySomething(a, out result))
     {
         return result;
     }
     else
     {
         throw Exception(); // which exception?
     }
}
public bool TrySomething(string a, out result)
{
    //...
}

私は本能的に最初の例がより正しいと思います(どの例外が発生したかを正確に知っています)が、try / catchは高すぎるのではないでしょうか?2番目の例で例外をキャッチする方法はありますか?

4

4 に答える 4

16

TrySomething で例外をキャッチして飲み込むだけにするのは、本当に悪い考えです。TryXXX パターンの要点の半分は、例外によるパフォーマンスへの影響を回避することです。

例外に多くの情報が必要ない場合は、DoSomething メソッドで TrySomething を呼び出すだけで、失敗した場合に例外をスローすることができます。例外の詳細が必要な場合は、さらに精巧なものが必要になる場合があります。例外のパフォーマンス ヒットの大部分がどこにあるのかを計っていません。それが作成ではなくスローである場合は、TrySomething と同様のシグネチャを持つプライベート メソッドを記述できますが、例外または null が返されます。

public int DoSomething(string input)
{
    int ret;
    Exception exception = DoSomethingImpl(input, out ret);
    if (exception != null)
    {
        // Note that you'll lose stack trace accuracy here
        throw exception;
    }
    return ret;
}

public bool TrySomething(string input, out int ret)
{
    Exception exception = DoSomethingImpl(input, out ret);
    return exception == null;
}

private Exception DoSomethingImpl(string input, out int ret)
{
    ret = 0;
    if (input != "bad")
    {
        ret = 5;
        return null;
    }
    else
    {
        return new ArgumentException("Some details");
    }
}

ただし、コミットする前にこの時間を計ってください。

于 2008-10-08T12:37:05.770 に答える
6

私は通常、このパターンを使用します。これが意味を成すかどうかは、Internal メソッドがどのように実装されているかによって異なります。条件付きの catch ブロックを使用する必要がある場合は、少し厄介になる可能性があります...

public object DoSomething(object input){
  return DoSomethingInternal(input, true);
}

public bool TryDoSomething(object input, out object result){
  result = DoSomethingInternal(input, false);
  return result != null;
}

private object DoSomethingInternal(object input, bool throwOnError){
  /* do your work here; only throw if you cannot proceed and throwOnError is true */
}
于 2008-10-08T12:34:46.727 に答える
2

最初の例は、例外をキャッチするだけで、何もせずに false を返す場合に適しています。

TrySomething を次のように変更できます。

public bool TrySomething(string a, out result, bool throwException)
{
  try
  {
    // Whatever
  }
  catch
  {
    if(throwException)
    {
      throw;
    }
    else
    {
      return false;
    }
  }

}

public bool TrySomething(string a, out result)
{
  return TrySomething(a, out result, false);
}

DoSomething は次のようになります

public int DoSomething(string a)
{
  int result;

  // This will throw the execption or 
  // change to false to not, or don't use the overloaded one.
  TrySomething(a, out result, true) 

  return result;      
}

throwException を含む TrySomething をパブリックに公開したくない場合は、それをプライベート メンバーにすることができます。

例外は高価になる可能性があり、文字列に対していくつかの RegEx チェックを実行して、例外がスローされないようにすることができます。それはあなたがしようとしていることに依存します。

于 2008-10-08T12:27:54.773 に答える
2

これが C# であると仮定すると、2 番目の例と言えます。

public bool TrySomething(string a, out result)
{
    try
    {
        result = DoSomething(a)
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

組み込みの を模倣してint.TryParse(string s, out int result)おり、私の意見では、言語/環境との一貫性を保つことが最善です。

于 2008-10-08T12:30:51.227 に答える