プロパティには副作用がありますか


19

C#のプロパティには、状態の変化を通知することに加えて副作用がありますか?

いくつかの異なる方法で使用されるプロパティを見てきました。初めてアクセスされたときに値を読み込むプロパティから、別のページへのリダイレクトを引き起こすなどの大きな副作用を持つプロパティにアクセスします。



1
@Jobの調査結果の中で、C#でプロパティを使用しないのなぜですか?別名ジェフリー・リヒターの意見は、この質問に非常に関連しています。
-rwong

@rwong関連するはい、しかし完全に無意味-それにもかかわらず、彼が強調する問題を認識するために読まなければなりません。少なくともこの問題に関して、私はスキートのキャンプにいます。
Apoorv Khurasia

これはコンストラクターに関係しますが、これも関連性があります:Programmers.stackexchange.com/a/164255/1996
Jim G.

回答:


40

プロパティセッターはほとんどすべての場合に副作用があるため、読み取り専用プロパティ、または少なくともプロパティgetterについて話していると思います。それ以外の場合はあまり役に立ちません。

一般的に、優れた設計は、最小限の驚きの原則に従います。発信者があなたに期待していないことは、特に将来の結果が変わる可能性がある場合はしないでください。

一般的に、これはプロパティゲッターに副作用がないことを意味します。

ただし、「副作用」とはどういう意味か注意しましょう。

副作用は、技術的には、ある任意の状態の変更。それは公的にアクセス可能な状態かもしれないし、あるいは...完全にプライベートな状態かもしれない。

遅延/遅延ローダーは、ほぼ排他的な状態の一例です。そのリソースを解放するのが呼び出し側の責任でない限り、実際には、遅延初期化を使用することにより、実際に驚きと複雑さを軽減しています。呼び出し元は、通常、内部構造の初期化を明示的に通知する必要はありません。したがって、遅延初期化は上記の原則に違反しませ

別の例は、同期されたプロパティです。メソッドをスレッドセーフにするには、多くの場合、クリティカルセクションまたはミューテックスによって保護する必要があります。クリティカルセクションに入るか、ミューテックスを取得すること副作用です。状態、通常はグローバル状態を変更しています。ただし、この副作用は、さらに悪い種類の驚き-別のスレッドによってデータが変更される(または、さらに悪いことに、部分的に変更される)驚きを防ぐために必要です。

そのため、次の制限を少し緩めます。プロパティの読み取りには、目に見える副作用や、セマンティクス変更する副作用があってはなりません。

発信者が副作用の影響を受ける可能性がなく、副作用に気付かない場合でも、その副作用は害を及ぼしません。特定の副作用の存在を確認するテストを作成することが不可能な場合、プライベート実装の詳細としてラベル付けするのに十分にローカライズされているため、外界に対する正当な懸念はありません。

しかし、注意してください。経験則として、あなたがすべきことが多いプライベート実装の詳細であることを考えるかもしれないもの、予想外に漏れると、公開になる可能性があるため、副作用を避けるようにしてください。


2
+
1-

ゲッターで許容される副作用の別の例:ロギング機能。
ローレンペクテル

5

あなたの質問に答えます:フォームのWidthプロパティを変更するとどうなりますか?

私の頭の上から、これは:

  • バッキングフィールドの値を変更します。
  • イベントを発生させます。
  • フォームの寸法を変更し、ウィンドウマネージャーに変更を報告し、再描画を要求します。
  • フォームのサイズが変更されたときにサイズまたは位置を変更する必要があるコントロールが変更できるように、変更のフォーム上のコントロールに通知します。
  • イベントを起動するように変更されたすべてのコントロールを発生させ、ウィンドウマネージャに再描画が必要であることを伝えます。
  • おそらく他のものも。

(免責事項:私はDelphi開発者であり、.NET開発者ではありません。これはC#で100%正確ではないかもしれませんが、元の.NETフレームワークのどれだけがDelphiに基づいているかを考えると、かなり近いと思います。)

これらの「副作用」はすべて、フォームのWidthプロパティを変更すると発生します。それらのいずれかが何らかの形で不適切または誤っているように見えますか?


それ自体を呼び出す無限ループに入っていない限り。これを回避するために、各「変更」は少なくとも3つのイベントにマップされます。1つは変更前に発生し、1つは変更中に発生し、1つは終了後に発生します。
-rwong

5

見てみましょうマイクロソフトのデザインルール、それらの特に1を:

CA1024:必要に応じてプロパティを使用します

...これらの条件のいずれかが存在する場合、メソッドはプロパティになるのに適した候補です。

  • 引数を取らず、オブジェクトの状態情報を返します。
  • 単一の引数を受け入れて、オブジェクトの状態の一部を設定します。

プロパティはフィールドのように振る舞う必要があります。メソッドができない場合は、プロパティに変更しないでください。以下の状況では、メソッドはプロパティよりも優れています。

  • このメソッドは、時間がかかる操作を実行します。このメソッドは、フィールドの値を設定または取得するのに必要な時間よりも明らかに遅いです。
  • メソッドは変換を実行します。フィールドにアクセスしても、保存されているデータの変換されたバージョンは返されません。
  • Getメソッドには、目に見える副作用があります。フィールドの値を取得しても、副作用は発生しません。
  • 実行の順序は重要です。フィールドの値の設定は、他の操作の発生に依存しません。
  • メソッドを連続して2回呼び出すと、異なる結果が作成されます。
  • このメソッドは静的ですが、呼び出し元が変更できるオブジェクトを返します。フィールドの値を取得しても、呼び出し側はフィールドに保存されているデータを変更できません。
  • メソッドは配列を返します...

2

プロパティには副作用はないはずです。ただし、このルールには例外が1つあります。遅延読み込みです。プロパティで何かを遅延ロードしたい場合、それは問題ありません。なぜなら、クライアントは遅延ロードが行われていることを伝えることができず、通常は気にしないからです。

編集:ああ、もう一つ-「PropertyXYZが変更されました!」というイベントを起動します (例INotifyPropertyChanged)-セッターでは問題ありません。



2

プログラミングには絶対的なものはほとんどありません。あなたの質問に答えるには、オブジェクトのいくつかの望ましいプロパティを調べ、それが私たちのケースに当てはまるかどうかを確認する必要があります

  1. クラスを簡単にテストできるようにします。
  2. クラスに並行性をサポートさせたい
  3. サードパーティのためにクラスを簡単に推論できるようにしたい

これらの質問のいくつかに「はい」と答えた場合、プロパティとその副作用について非常に慎重に検討することをお勧めします。値を更新すること自体が副作用であるため、副作用と言うときは二次的な副作用を意味すると思います。

一般的に副作用が多いほど、クラスのテストは難しくなります。二次的な副作用が多ければ多いほど、並行処理の処理は難しくなりますが、それはとにかくいくつかの主要な設計思想を必要とする難しい問題です。

大きな落とし穴は、おそらくあなたのクラスを「ブラックボックス」として扱う人向けです。プロパティを読んだからといって何らかの状態が変化したり、別の変更によって他のプロパティが変化したりする可能性があります。カスケード更新まで)。

そのため、一般的にいって、プロパティはできるだけ簡単に推論できるようにし、できるだけシンプルにする必要があります。それは、設計の決定に含まれていることを意味します。優れたプログラマーは常にルールを破ることができますが、本当に本当に正当な理由があることを確認してください:)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.