web-dev-qa-db-ja.com

C#でプロパティの使用を避ける必要があるのはなぜですか?

Jeffrey Richterは、優れた著書であるCLR Via C#で、プロパティは好きではないので、使用しないことを推奨しています。彼は何らかの理由を挙げましたが、私にはよくわかりません。プロパティを使用する理由または使用しない理由を誰かに説明できますか?自動プロパティを備えたC#3.0では、これは変更されますか?

参考として、ジェフリー・リヒターの意見を追加しました:

•プロパティは読み取り専用または書き込み専用の場合があります。フィールドアクセスは常に読み取りと書き込みが可能です。プロパティを定義する場合は、getとsetの両方のアクセサーメソッドを提供するのが最善です。

•プロパティメソッドは例外をスローする場合があります。フィールドアクセスは例外をスローしません。

•プロパティをoutまたはrefパラメーターとしてメソッドに渡すことはできません。フィールドはできます。たとえば、次のコードはコンパイルされません。

using System;
public sealed class SomeType
{
   private static String Name 
   {
     get { return null; }
     set {}
   }
   static void MethodWithOutParam(out String n) { n = null; }
   public static void Main()
   {
      // For the line of code below, the C# compiler emits the following:
      // error CS0206: A property or indexer may not
      // be passed as an out or ref parameter
      MethodWithOutParam(out Name);
   }
}

•プロパティメソッドの実行には時間がかかる場合があります。フィールドへのアクセスは常に即座に完了します。プロパティを使用する一般的な理由は、スレッド同期を実行することです。これは、スレッドを永久に停止する可能性があるため、スレッド同期が必要な場合はプロパティを使用しないでください。そのような状況では、方法が推奨されます。また、クラスにリモートアクセスできる場合(たとえば、クラスがSystem.MashalByRefObjectから派生している場合)、プロパティメソッドの呼び出しは非常に遅くなるため、プロパティよりもメソッドの方が適しています。私の意見では、MarshalByRefObjectから派生したクラスはプロパティを使用するべきではありません。

•連続して複数回呼び出された場合、プロパティメソッドは毎回異なる値を返すことがあります。フィールドは毎回同じ値を返します。 System.DateTimeクラスには、現在の日付と時刻を返す読み取り専用のNowプロパティがあります。このプロパティをクエリするたびに、異なる値が返されます。これは誤りであり、Microsoftは、Nowをプロパティではなくメソッドにすることでクラスを修正できることを願っています。

•プロパティメソッドは、観察可能な副作用を引き起こす可能性があります。フィールドアクセスは行いません。つまり、型のユーザーは、型で定義されたさまざまなプロパティを、型の異なる動作に気付かずに、自分が選択した順序で設定できる必要があります。

•プロパティメソッドは追加のメモリを必要とする場合や、実際にはオブジェクトの状態の一部ではない何かへの参照を返す場合があるため、返されたオブジェクトを変更しても元のオブジェクトには影響しません。フィールドを照会すると、常に元のオブジェクトの状態の一部であることが保証されているオブジェクトへの参照が返されます。コピーを返すプロパティでの作業は、開発者にとって非常に混乱する可能性があり、この特性は文書化されていないことがよくあります。

102
Quan Mai

プロパティを嫌うジェフの理由は、プロパティがフィールドのように見えるためです。そのため、違いを理解していない開発者は、安価に実行できると想定して、フィールドのように扱います。

個人的に私はこの特定の点で彼に同意しません-私はプロパティがクライアントコードを同等のメソッド呼び出しよりもはるかに読みやすくすることに気づきます。プロパティは基本的に変装したメソッドであることを開発者が知る必要があることに私は同意します。しかし、それについて開発者を教育することは、メソッドを使用してコードを読みにくくすることよりも優れていると思います。 (特に、いくつかのゲッターとセッターが同じステートメントで呼び出されているJavaコードを見てきたので、同等のC#コードを読む方がはるかに簡単であることを知っています。デメテルの法則はすべて理論的には非常に良いですが、時々foo.Name.Lengthは本当に正しいものです...)

(そして、いいえ、自動的に実装されたプロパティは実際にはこれを変更しません。)

これは、拡張メソッドの使用に対する反対意見に少し似ています-推論は理解できますが、実用的な利点(控えめに使用した場合)は、私の見解のマイナス面よりも重要です。

173
Jon Skeet

さて、彼の議論を一つずつ見てみましょう:

プロパティは読み取り専用または書き込み専用です。フィールドアクセスは常に読み取りと書き込みが可能です。

アクセスをよりきめ細かく制御できるため、これはプロパティにとって有利です。

プロパティメソッドは例外をスローする場合があります。フィールドアクセスは例外をスローしません。

これはほとんど当てはまりますが、初期化されていないオブジェクトフィールドでメソッドを呼び出すと、例外がスローされることがあります。

•プロパティをoutまたはrefパラメーターとしてメソッドに渡すことはできません。フィールドはできます。

フェア。

•プロパティメソッドの実行には時間がかかる場合があります。フィールドへのアクセスは常に即座に完了します。

また、非常に時間がかかりません。

•連続して複数回呼び出された場合、プロパティメソッドは毎回異なる値を返すことがあります。フィールドは毎回同じ値を返します。

違います。 (おそらく別のスレッドによって)フィールドの値が変更されていないことをどのようにして知ることができますか?

System.DateTimeクラスには、現在の日付と時刻を返す読み取り専用のNowプロパティがあります。このプロパティをクエリするたびに、異なる値が返されます。これは誤りであり、Microsoftは、Nowをプロパティではなくメソッドにすることでクラスを修正できることを願っています。

それが間違いであるなら、それはマイナーなものです。

•プロパティメソッドは、観察可能な副作用を引き起こす可能性があります。フィールドアクセスは行いません。つまり、型のユーザーは、型で定義されたさまざまなプロパティを、型の異なる動作に気付かずに、自分が選択した順序で設定できる必要があります。

フェア。

•プロパティメソッドは追加のメモリを必要とする場合や、実際にはオブジェクトの状態の一部ではない何かへの参照を返す場合があるため、返されたオブジェクトを変更しても元のオブジェクトには影響しません。フィールドを照会すると、常に元のオブジェクトの状態の一部であることが保証されているオブジェクトへの参照が返されます。コピーを返すプロパティでの作業は、開発者にとって非常に混乱する可能性があり、この特性は文書化されていないことがよくあります。

抗議のほとんどは、Javaのゲッターとセッターにも言えるでしょう。実際には、このような問題が発生することなく、かなりの間抗議行動がありました。

問題のほとんどは、構文の強調表示(つまり、フィールドとプロパティを区別する)を改善することで解決できると思うので、プログラマーは何を期待すべきかを理解できます。

34
Hejazzman

私はその本を読んだことがなく、理解できない部分を引用していないので、推測する必要があります。

一部の人々は、あなたのコードに意外なことをさせることができるので、プロパティを嫌います。

Foo.Bar、それを読んでいる人は通常、これが単にFooクラスのメンバーフィールドにアクセスしていることを期待します。これは安価でほぼ無料の操作であり、確定的です。私はそれを何度も呼び出すことができ、毎回同じ結果を得ることができます。

代わりに、プロパティを使用すると、実際には関数呼び出しになる場合があります。無限ループの可能性があります。データベース接続を開く場合があります。アクセスするたびに異なる値を返す可能性があります。

これは、LinusがC++を嫌う理由と似ています。あなたのコードは読者にとって意外に振る舞うことができます。彼は演算子のオーバーロードを嫌っています:a + bは、必ずしも単純な加算を意味するものではありません。これは、C#プロパティと同様に、非常に複雑な操作を意味する場合があります。副作用があるかもしれません。それは何でもします。

正直なところ、これは弱い議論だと思います。どちらの言語もこのようなものでいっぱいです。 (C#でも演算子のオーバーロードを避ける必要がありますか?結局、同じ引数をそこで使用できます)

プロパティは抽象化を可能にします。 pretend何かが通常のフィールドであり、それが1つのフィールドであるかのように使用でき、舞台裏で何が行われているのかを気にする必要はありません。

これは通常良いことだと考えられていますが、明らかにプログラマが意味のある抽象化を書くことに依存しています。プロパティすべきはフィールドのように動作します。副作用が発生したり、高価な操作や安全でない操作を実行したりしないでください。 それらを考えるをフィールドとして使用できるようにしたい。

しかし、それらを完璧とは言えない理由がもう1つあります。他の関数への参照によって渡すことはできません。

フィールドはrefとして渡すことができ、呼び出された関数がフィールドに直接アクセスできるようにします。関数はデリゲートとして渡すことができ、呼び出された関数が直接それにアクセスできるようにします。

プロパティ...できません。

それは最悪だ。

しかし、それはプロパティが悪である、または使用されるべきではないという意味ではありません。多くの目的で、それらは素晴らしいです。

18
jalf

一般的にプロパティを使用しない理由はわかりません。

C#3+の自動プロパティは、構文を少しだけ単純化します(合成糖)。

11

それはただ一人の意見です。私はかなりの数のC#の本を読みましたが、「プロパティを使用しないでください」と言っている人はまだ見たことがありません。

個人的には、プロパティはC#の最も優れた点の1つだと思います。彼らはあなたが好きなメカニズムを介して状態を公開することができます。何かが最初に使用されたときに遅延インスタンス化を行うことができ、値の設定などの検証を行うことができます。それらを使用して書き込む場合、プロパティは、より優れた構文であるセッターおよびゲッターと考えるだけです。

プロパティに関する警告については、いくつかあります。 1つはおそらくプロパティの誤用であり、もう1つは微妙な場合があります。

まず、プロパティはメソッドのタイプです。複雑なロジックをプロパティに配置すると、クラスのほとんどのユーザーはプロパティがかなり軽量になることを期待するため、驚くかもしれません。

例えば。

public class WorkerUsingMethod
{
   // Explicitly obvious that calculation is being done here
   public int CalculateResult()
   { 
      return ExpensiveLongRunningCalculation();
   }
}

public class WorkerUsingProperty
{
   // Not at all obvious.  Looks like it may just be returning a cached result.
   public int Result
   {
       get { return ExpensiveLongRunningCalculation(); }
   }
}

これらのケースにメソッドを使用すると、区別するのに役立ちます。

次に、さらに重要なことに、デバッグ中にプロパティを評価すると、プロパティに副作用が生じる可能性があります。

次のようなプロパティがあるとします。

public int Result 
{ 
   get 
   { 
       m_numberQueries++; 
       return m_result; 
   } 
}

ここで、クエリが多すぎる場合に発生する例外があるとします。デバッグを開始し、デバッガーでプロパティをロールオーバーするとどうなるでしょうか。悪いこと。これを行わないでください!プロパティを見ると、プログラムの状態が変化します。

これらは私が持っている唯一の警告です。プロパティの利点は問題をはるかに上回っていると思います。

9
Mark Simpson

ジェフリー・リヒターの意見の詳細を選ぶのは仕方がない:

プロパティは読み取り専用または書き込み専用です。フィールドアクセスは常に読み取りと書き込みが可能です。

誤り:フィールドは読み取り専用としてマークできるため、オブジェクトのコンストラクターのみがフィールドに書き込むことができます。

プロパティメソッドは例外をスローする場合があります。フィールドアクセスは例外をスローしません。

誤り:クラスの実装により、フィールドのアクセス修飾子がパブリックからプライベートに変更される可能性があります。実行時にプライベートフィールドを読み取ろうとすると、常に例外が発生します。

6

その理由は、非常に具体的な文脈の中で与えられたに違いありません。それは通常逆です-プロパティを使用することをお勧めします。プロパティを使用すると、クライアントに影響を与えずにクラスの動作を変更できるようになります...

6
Rashack

私はジェフリー・リヒターに同意しませんが、なぜ彼が不動産を好きではないのか推測できます(私は彼の本を読んだことがありません)。

プロパティはメソッドと同じように(実装面で)、クラスのユーザーとして、そのプロパティは「多かれ少なかれ」パブリックフィールドのように動作することを期待しています。

  • プロパティのゲッター/セッター内で時間のかかる操作はありません
  • プロパティゲッターには副作用がありません(複数回呼び出しても結果は変わりません)。

残念ながら、そのように動作しないプロパティを見てきました。しかし、問題はプロパティ自体ではなく、それらを実装した人々です。ですから、教育が必要です。

5
M4N

それらの使用は避けてはなりませんが、他の寄稿者からの理由により、資格と注意を払って使用する必要があります。

データベースのアウトプロセスコールを内部で開いて顧客リストを読み取る、Customersのようなプロパティを以前に見ました。クライアントコードには 'for(int i to Customers.Count)'があり、反復ごとに、および選択した顧客のアクセスのために、データベースを個別に呼び出していました。これは、プロパティを非常に軽量に保つという原則を示す悪質な例です。内部フィールドへのアクセス以上のものはめったにありません。

プロパティを使用するための1つの引数は、設定されている値を検証できることです。もう1つは、プロパティの値が、TotalValue =金額*数量のように、単一のフィールドではなく、派生値になる場合があることです。

4
Rob Kent

プロパティを使用しないことを検討する時期があります。それは.Net Compact Frameworkコードの記述です。 CF JITコンパイラーは、デスクトップJITコンパイラーと同じ最適化を実行せず、単純なプロパティアクセサーを最適化しません。そのため、この場合、単純なプロパティを追加すると、パブリックフィールドの使用によるコードの膨張が発生します。通常、これは問題にはなりませんが、ほとんどの場合、Compact Frameworkの世界では、厳しいメモリ制約に直面しているため、このようなわずかな節約でもカウントされます。

4
Steve

個人的には、単純なget/setメソッドを作成するときにのみプロパティを使用します。複雑なデータ構造になると、それから離れます。

3
Steve

引数は、プロパティはフィールドのように見えるために悪いと想定しますが、驚くべきアクションを実行できます。この仮定は、.NETプログラマの期待するアクションによって無効になります。

プロパティはフィールドのようには見えません。フィールドはプロパティのように見えます。

•プロパティメソッドは例外をスローする場合があります。フィールドアクセスは例外をスローしません。

したがって、フィールドは、例外をスローしないことが保証されているプロパティのようなものです。

•プロパティをoutまたはrefパラメーターとしてメソッドに渡すことはできません。フィールドはできます。

したがって、フィールドはプロパティのようなものですが、ref/out受け入れメソッドに渡すという追加機能があります。

•プロパティメソッドの実行には時間がかかる場合があります。フィールドへのアクセスは常に即座に完了します。 [...]

つまり、フィールドは高速なプロパティのようなものです。

•連続して複数回呼び出された場合、プロパティメソッドは毎回異なる値を返すことがあります。フィールドは毎回同じ値を返します。 System.DateTimeクラスには、現在の日付と時刻を返す読み取り専用のNowプロパティがあります。

したがって、フィールドは、フィールドが異なる値に設定されていない限り、同じ値を返すことが保証されているプロパティのようなものです。

•プロパティメソッドは、観察可能な副作用を引き起こす可能性があります。フィールドアクセスは行いません。

繰り返しますが、フィールドはそうしないことが保証されているプロパティです。

•プロパティメソッドは追加のメモリを必要とする場合や、実際にはオブジェクトの状態の一部ではない何かへの参照を返す場合があるため、返されたオブジェクトを変更しても元のオブジェクトには影響しません。フィールドを照会すると、常に元のオブジェクトの状態の一部であることが保証されているオブジェクトへの参照が返されます。コピーを返すプロパティでの作業は、開発者にとって非常に混乱する可能性があり、この特性は文書化されていないことがよくあります。

これは驚くべきことかもしれませんが、これを行うプロパティだからではありません。むしろ、プロパティで変更可能なコピーを返す人はほとんどいないため、0.1%の場合は驚くべきことです。

3
milleniumbug

プロパティの代わりにメソッドを呼び出すと、呼び出すコードの可読性が大幅に低下します。たとえばJ#では、Javaはプロパティとインデクサー(基本的には引数を持つプロパティ)をサポートしていないため、ADO.NETを使用するのは悪夢でした。)結果のコードは、括弧が空で非常に醜くなりましたメソッドがいたるところに呼び出されます。

プロパティとインデクサーのサポートは、Javaに対するC#の基本的な利点の1つです。

0
Marty Solomon