“ TryParse / Parse like”パターン:それを実装する最良の方法は何ですか
-
06-07-2019 - |
質問
この質問は、のフォローアップです。メソッドが失敗しました。 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番目の例で例外をキャッチする方法はありますか?
解決
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");
}
}
コミットする前に時間を計ってください!
他のヒント
通常、このパターンを使用します。これが意味をなすかどうかに関して、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 */
}
例外をキャッチし、何もせずに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;
}
TrySomething with throwExceptionを公開しない場合は、プライベートメンバーにすることができます。
例外は高コストになる可能性があり、文字列を正規表現でチェックして、文字列がスローされないようにすることができます。何をしようとしているかによって異なります。
これが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)
を模倣しており、私の意見では、言語/環境との一貫性を保つことが最善です。