プロパティのゲッターまたはセッター内から例外をスローするのが適切なのはいつですか?それはいつ適切ではありませんか?どうして?この件に関する外部ドキュメントへのリンクは役に立ちます... Googleは驚くほどほとんど姿を見せませんでした。
プロパティのゲッターまたはセッター内から例外をスローするのが適切なのはいつですか?それはいつ適切ではありませんか?どうして?この件に関する外部ドキュメントへのリンクは役に立ちます... Googleは驚くほどほとんど姿を見せませんでした。
回答:
Microsoftは、http://msdn.microsoft.com/en-us/library/ms229006.aspxでプロパティの設計方法に関する推奨事項を公開しています。
基本的に、プロパティゲッターは、常に安全に呼び出せる軽量のアクセサーであることを推奨しています。例外をスローする必要がある場合は、メソッドとしてゲッターを再設計することをお勧めします。セッターの場合、例外は適切で許容可能なエラー処理戦略であることを示しています。
インデクサーの場合、Microsoftはゲッターとセッターの両方が例外をスローすることは許容できることを示しています。そして実際、.NETライブラリの多くのインデクサーがこれを行っています。最も一般的な例外はArgumentOutOfRangeException
です。
プロパティゲッターで例外をスローしたくない理由はいくつかあります。
obj.PropA.AnotherProp.YetAnother
この種の構文では、例外のcatchステートメントを挿入する場所を決定することが問題になります。補足として、プロパティが例外をスローするように設計されていないからといって、例外がスローされないわけではないことに注意してください。コードを呼び出すのは簡単なことです。新しいオブジェクト(文字列など)を割り当てるという単純な行為でさえ、例外が発生する可能性があります。常に防御的にコードを記述し、呼び出すものからの例外を予期する必要があります。
セッターからの例外のスローに問題はありません。結局のところ、値が特定のプロパティに対して無効であることを示すより良い方法は何ですか?
ゲッターの場合、一般的には眉をひそめられますが、それは簡単に説明できます。プロパティゲッターは、通常、オブジェクトの現在の状態を報告します。したがって、ゲッターがスローすることが妥当な唯一のケースは、状態が無効な場合です。しかし、一般に、最初に無効なオブジェクトを取得したり、通常の方法で無効な状態にしたりできないようにクラスを設計することもお勧めです(つまり、常にコンストラクターで完全な初期化を確保し、メソッドを状態の有効性とクラスの不変量に関して例外セーフにするようにしてください)。そのルールを守っている限り、プロパティゲッターは無効な状態を報告しなければならない状況に陥ることはなく、スローすることもありません。
私が知っている例外が1つありますが、それは実際にはかなり大きな例外IDisposable
です。Dispose
は、オブジェクトを無効な状態にする方法として特別に意図ObjectDisposedException
されており、その場合に使用される特別な例外クラスもあります。オブジェクトが破棄された後、ObjectDisposedException
プロパティゲッターを含む(そしてDispose
それ自体を除外する)クラスメンバーからスローすることは完全に正常です。
IDisposable
無意味にする必要があるという考えが嫌いDispose
です。メンバーを呼び出すときに使用Dispose
不可になったリソースを使用する必要がある場合(たとえば、メンバーは閉じられたストリームからデータを読み取る場合)、メンバーはObjectDisposedException
リークするのではなくスローする必要ArgumentException
がありますが、特定のフィールドの値については、必要な場合よりも、廃棄後にこのようなプロパティを読み取れるようにする(最後に入力された値を生成する)方が
Dispose
ようなプロパティがすべて読み取られるまで延期されます。あるスレッドがオブジェクトのブロック読み取りを使用し、別のスレッドがそれを閉じる場合、およびの前にいつでもデータが到着する場合は、受信データDispose
をDispose
遮断して、以前に受信したデータを読み取ることができると便利な場合があります。他の方法では存在する必要のない状況Close
とDispose
状況を人為的に区別してはなりません。
Get...
代わりにメソッドとしてより良いことかもしれません。ここでの例外は、プロパティを提供する必要がある既存のインターフェイスを実装する必要がある場合です。
ゲッターにはほとんど適切ではなく、セッターには適切な場合もあります。
これらの種類の質問に最適なリソースは、CwalinaとAbramsによる「フレームワーク設計ガイドライン」です。製本された本として入手でき、その大部分はオンラインでも入手できます。
セクション5.2から:プロパティの設計
プロパティゲッターから例外をスローすることは避けてください。プロパティゲッターは単純な操作であり、前提条件はありません。ゲッターが例外をスローできる場合は、おそらくメソッドとして再設計する必要があります。この規則はインデクサーには適用されないことに注意してください。引数を検証した結果として例外が発生することが予想されます。
このガイドラインはプロパティゲッターにのみ適用されることに注意してください。プロパティセッターで例外をスローしても問題ありません。
ObjectDisposedException
、オブジェクトがDispose()
呼び出されてプロパティ値を要求されたときにスローすることを検討する必要があるガイダンスとどのように関連していますか?ガイダンスは、「オブジェクトが破棄されていない限り、プロパティゲッターからの例外のスローを避けてください。その場合、ObjectDisposedExcpetionのスローを検討する必要があります」のようです。
例外に対する1つの優れたアプローチは、例外を使用して、次のように自分や他の開発者のためにコードを文書化することです。
例外は、例外的なプログラムの状態に対するものです。つまり、好きな場所に書いても問題ありません。
それらをゲッターに配置したい理由の1つは、クラスのAPIを文書化することです。プログラマーが間違って使用しようとするとすぐにソフトウェアが例外をスローすれば、間違って使用することはありません!たとえば、データの読み取りプロセス中に検証を行っている場合、データに致命的なエラーがあった場合、プロセスを続行して結果にアクセスできるようにしても意味がない場合があります。この場合、エラーが発生した場合に出力をスローさせて、別のプログラマーがこの状態をチェックするようにすることができます。
それらは、サブシステム/メソッド/何でもの仮定と境界を文書化する方法です。一般的なケースでは、捕まってはいけません!これは、システムが期待どおりに動作している場合はスローされないためでもあります。例外が発生した場合は、コードの前提が満たされていないことを示します。それはもともと意図されていました。この目的で記述された例外をキャッチした場合、おそらくシステムが予測できない/一貫性のない状態になったことを意味します。これにより、最終的にデータのクラッシュや破損などが発生し、検出/デバッグがはるかに困難になる可能性があります。
例外メッセージは、エラーを報告する非常に大まかな方法です。それらをまとめて収集することはできず、実際には文字列のみが含まれます。このため、入力データの問題を報告するのに適していません。通常の実行では、システム自体がエラー状態になることはありません。この結果、メッセージはユーザー向けではなく、プログラマ向けに設計する必要があります。入力データに誤りがある場合は、より適切な(カスタム)形式で検出してユーザーに中継できます。
このルールの例外(ハハ!)はIOのようなもので、例外は制御できず、事前にチェックできません。
これはすべて(他の回答でリンクされているように)MSDNに記載されていますが、ここでは一般的な経験則を示します...
セッターで、プロパティを型の上および上で検証する必要がある場合。たとえば、PhoneNumberと呼ばれるプロパティはおそらく正規表現の検証が必要であり、形式が無効な場合はエラーをスローする必要があります。
ゲッターの場合、値がnullの可能性がありますが、呼び出しコードで処理する必要がある可能性が最も高いです(設計ガイドラインによる)。
MSDN:標準の例外タイプのキャッチとスロー
これは非常に複雑な質問であり、答えはオブジェクトの使用方法によって異なります。経験則として、「遅延バインディング」であるプロパティのゲッターとセッターは例外をスローしませんが、「早期バインディング」のみのプロパティは必要に応じて例外をスローします。ちなみに、マイクロソフトのコード分析ツールは、私の意見では狭すぎるプロパティの使用を定義しています。
「遅延バインディング」とは、反射によってプロパティが見つかることを意味します。たとえば、Serializeable属性は、そのプロパティを介してオブジェクトをシリアル化/逆シリアル化するために使用されます。この種の状況で例外をスローすると、物事が壊滅的な方法で中断され、例外を使用してより堅牢なコードを作成する良い方法ではありません。
「早期バインディング」とは、プロパティの使用がコンパイラによってコードでバインドされることを意味します。たとえば、記述するコードの一部がプロパティゲッターを参照する場合です。この場合、意味のある例外をスローしても問題ありません。
内部属性を持つオブジェクトには、それらの属性の値によって決定される状態があります。オブジェクトの内部状態を認識して敏感な属性を表すプロパティは、遅延バインディングには使用しないでください。たとえば、開いてアクセスし、閉じる必要があるオブジェクトがあるとします。この場合、最初にopenを呼び出さずにプロパティにアクセスすると、例外が発生します。この場合、例外をスローせず、コードに例外をスローせずに値へのアクセスを許可するとしますか?意味のないゲッターから値を受け取ったとしても、コードは満足しているように見えます。これで、値をチェックしてナンセンスかどうかを確認する方法を知る必要があるため、ゲッターを呼び出すコードを悪い状況に置きました。これは、コードがそれを検証するために、プロパティゲッターから取得した値を想定する必要があることを意味します。これが悪いコードが書かれる方法です。
スローする例外がわからない場所にこのコードがありました。
public Person
{
public string Name { get; set; }
public boolean HasPets { get; set; }
}
public void Foo(Person person)
{
if (person.Name == null) {
throw new Exception("Name of person is null.");
// I was unsure of which exception to throw here.
}
Console.WriteLine("Name is: " + person.Name);
}
コンストラクターで引数として強制することで、モデルが最初からプロパティがnullになるのを防ぎました。
public Person
{
public Person(string name)
{
if (name == null) {
throw new ArgumentNullException(nameof(name));
}
Name = name;
}
public string Name { get; private set; }
public boolean HasPets { get; set; }
}
public void Foo(Person person)
{
Console.WriteLine("Name is: " + person.Name);
}