この質問は、この質問 (のサブセット) に似ています
この場合、ランタイム タイプを使用して、返された結果の成功と失敗を区別しています。
次のパターンをよく見かけます。
public struct Result {
public boolean IsSuccess { get;set;}
public string ErrorMessage {get;set;}
public int Value {get;set;}
}
...
Result result = someObject.SomeMethod();
if (result.IsSuccess) DoSomething(result.Value);
else handleError(result.ErrorMessage);
次のほうがより自然で、意図をより明確に表現していると思います(私の意見では):
public abstract class Result { }
public sealed class Failure : Result {
public string ErrorMessage { get; set; }
}
public sealed class Success : Result {
public int Value { get; set; }
}
...
Result result = someObject.SomeMethod();
if (result is Success) DoSomething((result as Success).Value);
else if (result is Failure) handleError((result as Failure).ErrorMessage);
また、.Net (および他の多くの言語) は、複数の catch 句 (例外の種類によって catch ブロックが選択される) を含む try-catch ブロックでこのパターンを使用することに注意してください。
編集:このパターン (つまり、実行時の型に依存する) は、F# の識別された共用体と同じです。違いは、F# ではネイティブであり、C# では、異なる目的のための構成を使用してエミュレートされることです。
編集:最初のコードの主な問題は、「部分的に初期化されたオブジェクト」のコードの匂いだと思います。100% の場合、オブジェクトの半分だけが初期化されます。.IsSuccess が評価されると、それ以降はオブジェクトの一部のみが使用されるため、ISPにもほぼ違反しています (成功の場合は .Result のみが使用され、エラーの場合はエラー プロパティのみが使用されます)。実行型チェック ソリューションには、これらの問題はありません。
問題は、このパターンを使用する際の問題点は何ですか? 特に、保守性、可読性、テスト容易性、概念の純粋性、OOP/OOD の観点からの問題に関心があります。