質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

6回答

9857閲覧

単純なgetter setter は必要なのか

animeing

総合スコア136

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

3クリップ

投稿2021/07/28 03:32

Getter Setterについて調べると、
メンバ変数に別classからアクセスする際は Getter Setter を用いるように書かれてたりするのですが、
Getter / Setter 共にpublicでSetの際のチェックが不要な場合、Getter / Setter を使うことにどのような利点があるのか教えていただきたいです。

分からない

C#

1class ObjectPosition 2{ 3 //このGetter Setter はなぜ必要なのか public変数と何が違ってどのような利点があるのかわからない 4 public Position CreatePosition 5 { 6 get; 7 set; 8 } 9} 10

分かる
外部から値を見られても外部から値を変えられたくない

C#

1class ObjectPosition 2{ 3 public Position CurrentPosition 4 { 5 get; 6 private set; 7 } 8} 9

値を変える前にその値に問題ないかチェックしたい わかる

C#

1class ObjectPosition 2{ 3 private string objectName; 4 public stringObjectName 5 { 6 get 7 { 8 return objectName; 9 } 10 set 11 { 12 if (value == objectName || value.Length ==0) 13 return; 14 objectName = value; 15 } 16 } 17} 18

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

ベストアンサー

一番の理由は、公式のプログラミングガイドでそうするよう言われているからです。

フィールド (C# プログラミング ガイド)

通常、フィールドは、プライベートまたは保護されたアクセシビリティを持つ変数に対してのみ使用します。 クラスからクライアント コードに公開するデータは、メソッド、プロパティ、およびインデクサーを使用して提供する必要があります。

更にその理由の一つが次に書かれています。

これらの構成要素を使用して、内部フィールドに間接的にアクセスすることで、無効な値が入力されることを防止できます。

また、公式情報ではありませんが、多くの有識者から信頼されている次のサイトではこう書かれています。

実装の隠蔽

通常、内部の実装がどうなっているのかを隠蔽(要するに private にする)し、可能な操作のみを公開(public)することが望ましいとされています。 簡単に言うと、メンバー変数はクラス外部から直接アクセス出来ないようにして、オブジェクトの状態の変更はすべてメソッドを通して行うべきだということです。

つまり、プロパティはインターフェースであり、フィールドは実装であり、実装は隠蔽してインターフェースを公開すべきというオブジェクト指向の基本的な考えに基づくものです。
(この場合のインターフェースは Interface キーワードで宣言されるもののことではなく、オブジェクト同士の通信に使うものという意味合いで使用しています)

実装を隠蔽することにより、インターフェースはそのままにバージョンアップによって実装を取り替えることができます。
例えば、あるクラスでフィールドを公開した場合、派生したクラスでもそのフィールドは公開されます。
これは実装を公開しているため、派生先のクラスでも同じようにしか使うことができません。

しかし例えば、フィールドの代わりにプロパティを公開した場合、その実装を取り替えることができます。
例えばプロパティにセットしたデータをフィールドの代わりに別のオブジェクトやコレクションに保存したり、セットされるごとにイベントを励起したり、あるいは不正な値で例外を起こしたりできます。

これらの変更を行っても使用する側の変更は不要で、同じ手続きで扱うことができ、オブジェクトを部品化できます。

オブジェクト指向では、詳細を隠蔽してオブジェクト同士がメッセージを送りあうというのが肝なので、メッセージ送出にあたるメソッドやプロパティのみ公開されるわけです。

まとめ

この回答をまとめるとこうなります。

  • ゲッター・セッターのみのプロパティを作るのはフィールドを公開しないため
  • フィールドを公開しないのは公式情報でそのようなガイドラインが公開されている(つまりそのようなプログラミングを理想として C# が設計されている)ため
  • そのガイドラインがあるのは、オブジェクト指向言語である(基本的な考えとして実装の隠蔽がある)ため
  • 実装の隠蔽(カプセル化)の利点としては、主に次の二つ。不正なデータの設定を防ぐため、またインターフェースの更新をせず別の実装と交換できるようにするため(オブジェクトの部品化とポリモーフィズム)

以上です。

投稿2021/07/28 04:29

編集2021/07/28 05:06
Zuishin

総合スコア28669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

animeing

2021/07/28 13:01

回答ありがとうござます。 [内部フィールドに間接的にアクセスすることで、無効な値が入力されることを防止できます。] というのはSetしようとしている値をチェックする必要がある場合には確かに有効かと思います。 この部分に関して恐らく今の理解で違いはないような気がしています。 (違ったら教えてください。すみません。。) [プロパティはインターフェースであり、フィールドは実装であり、実装は隠蔽してインターフェースを公開すべきというオブジェクト指向の基本的な考えに基づくものです。] 実装は隠蔽して、インターフェースを公開するという部分に関しては理解できました。 ただ、そうなると下にあるようなコードが許されて ① ```C# class ObjectPosition { //確かに実装は見えてないけどこのインターフェースを使うにあたってgetもsetもpublicなので実装に間接的ではあれアクセスできるので厳密には隠れてないのではないでしょうか? //また、そのsetについて無効な値が入力されることを防止してない点についてもpublicな変数と大差ないように思えます。 public Position CreatePosition { get; set; } } ``` これが許されない理由が分かりません。 ② ```C# class ObjectPosition { public Position createPosition; } ``` ①は確かにインターフェースが公開されており、②は実装が公開されているというのは分かるのですが、 ①も②も双方ともに値の取得変更ができるので、扱おうとしている値が変数なのかインターフェースなのか という点において意識することは特にないように感じました。 そうなると①の実装が隠れていて②の実装が隠れていないとするその差が良く分からなかったです。 ただ、確かに後々無効な値が入力されることを防止する必要が出た際のことを考えるとGetter/Setterにしたほうが良さそうな気はしました。
Zuishin

2021/07/28 13:34 編集

インターフェースを表に出す場合、隠蔽された実装は交換がきくと回答に書いていますが、その詳細を聞きたいということでしょうか? たとえば基本クラスでは CreatePosition をフィールドをバックストアとして宣言します。 派生クラス 1 では CreatePosition にセットされた値は 2 倍になるとします。 派生クラス 2 では CreatePosition にセットされた値は 3 倍になるとします。 CreatePosition はどのクラスでも同じように扱えますが、その振る舞いは違います。 この「同じように扱える」のがインターフェースの役割で、「振る舞い」を決めるのが実装の役割です。 派生クラスで振る舞いを変えることができるのは、実装が隠蔽されていてインターフェースを通してアクセスしているからです。 ①は確かに一つのクラスでは②と区別がつきにくいかもしれませんが、派生したクラスでは違います。 たとえば、データバインディングにはフィールドは使えません。 変更通知できないからです。 基本クラスでは変更通知が必要なくても、派生クラスで必要になる場合、フィールドでは邪魔になります。 オブジェクト指向プログラミングではカプセル化やポリモーフィズムを多用しますが、フィールドを公開したのではそれがやりにくくなります。 つまり、ただ単にデータストアとして見た場合、プロパティとフィールドに差は感じられないでしょうが、オブジェクト指向プログラミングをしようとすればそこには歴然とした差があるということです。
animeing

2021/07/28 13:56 編集

詳細を教えていただき、ありがとうございます。 実際に書いてみたところ利点をすごい実感できました。 [「同じように扱える」のがインターフェースの役割で、「振る舞い」を決めるのが実装の役割です。] の意味が本当の意味で分かった気がします。 数年前からもやもやと疑問に思ってたことがすっきりしました。
guest

0

こんにちは。
回答は既に出揃っていると思うので、補足的に概念の説明をしてみます。


まず最初に表題に回答すると、単純な getter/setter は必要です。

プロパティとフィールドの最も大きな違いは、値の検証を記述できるかどうかなどではなく、「それがオブジェクト指向の文脈に乗るかどうか」です。
オブジェクトが備えるべき「性質」のうち、オブジェクトの状態を覗き見れるレンズの存在を表すものがプロパティです。
対して、フィールドはオブジェクト指向のレイヤには現れない「実装」の領域の概念です。
これらを隠すことで、オブジェクト指向のレイヤ上でのみプログラムを考えることができるようになるのです。
これが、フィールドを公開してはならないとガイドラインに定められている理由です。
単純な getter/setter とは、「状態を読むことができ、また自由に書き換えられる」という性質をオブジェクト指向の文脈上で規定したものであるため、単にフィールドを公開することとは意味が違います。

とはいえ、C# ではどちらでも同じような記述で同じように利用できてしまうため、カプセル化の観点からもプロパティの存在意義を示します。
例によって、getter/setter ともに自動実装のプロパティの存在意義について、以前の回答から文章を貼り付けておきます。

オブジェクトの変数を public にすると、他の誰もが値を自由に変更することが出来る。言い換えると、状態を変更する「責任」がその値を変更する側に分散する。getter/setter を介して private 変数を操作する設計にすると、状態を変更する「責任」は、そのオブジェクト自身が持つことになる。こうやって、オブジェクトの状態を管理する「責任」をそれぞれのオブジェクトに閉じ込めること、これが getter と setter の存在意義です。

投稿2021/07/28 08:37

tamoto

総合スコア4252

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

「分からない」のケースでは、パブリックプロパティなど使わなくてもパブリックフィールドでいいんじゃないかと思っているのですよね。

以前別のスレッドで書いたことですが、再度まとめて以下に書いておきます。「分かる」の例も混じっています。

(1) オブジェクト指向の概念の一つ「カプセル化」を実現するため、通常クラス内の各フィールドへの直接アクセスは禁止するようにしておき、外部からはパブリックプロパティで各フィールドの値を取得したり設定したりするということがもともとのプロパティの目的。

(2) プロパティを使う目的には、開発者が意図した規則に基づいてフィールドを正しく使用できるよう保証するということもあります。例えば、以下の記事ように、ユーザーが Visual Studio でプロパティを設定する際、範囲外であると例外をスローするようなこともできます。

Web Custom Control の例外処置
http://surferonwww.info/BlogEngine/post/2010/08/06/Exception-handling-by-web-custom-control.aspx

(3) Entity Framework Code First でのモデルを定義を行う場合はフィールドではダメで、プロパティの定義が必要です。

新しいデータベースの Code First
https://docs.microsoft.com/ja-jp/ef/ef6/modeling/code-first/workflows/new-database

(4) ASP.NET Web Forms アプリのデータバインド式はプロパティでないとダメです。

(5) JSON にシリアライズするのもフィールドではダメでプロパティにする必要があります。(使うシリアライザにもよりますが、例えば System.Text.Json 名前空間の JsonSerializer ではプロパティでないとダメです)

投稿2021/07/28 04:47

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

単純なgetter setter は必要なのか

OOPの文脈で、昔からよく出てくる話題ですね。
「ゲッター/セッターは、カプセル化のためにある」と言われます。

でも、小さなサンプルコードだけ見ると、「なぜ」そうするのか、イマイチ分かりにくい。
全体が100行くらいだったら、ゲッター/セッターがない方が、分かりやすく感じるでしょう。

そこで、プログラム全体で1万行以上、すでに書いてある、と想像してみてください。
そして、全体が大きくなると、「どこから呼ばれてるか、分からなくなってくる」のです。
100箇所以上から呼ばれると、どこでどう処理されて、とイチイチ覚えていられなくなります。

呼び出し元の処理がバラバラだとバグの元だから、一元的に処理する仕組みが必要だろうと。
そこで、処理とデータを一体化させて、データを保護するのが、カプセル化の考え方です。


プログラムが大きくなってくると、全体を見通せないので、
破綻しないよう整合性を管理する仕組みが必要になります。

このことを分かりやすく日常でたとえると、一戸建てで個室に鍵がなくても、
ホテルのフロントでは、全個室の鍵を管理する必要があるようなものです。

あるいは、大企業には入口に受付が用意されているようなものです。
マンションの一室にある零細企業だと、受付なんか要らないでしょう。
来る人はみんな顔見知りだろうし。でも、大企業だと来客は不特定多数です。

そういう言わば「組織化」の方法だから、組織が小さいうちは不要だと感じる。
実際、なくても回るし、ない方がスッキリする。でも、大きくなると必要だと。


さて、質問者の方の意図を汲み、ゲッター/セッターの必要性を説明しました。

……が! 一周回って、OO原理主義的な「ゲッター/セッター」不使用論
というのがあります。発展的話題として、こちらも紹介しておきます。

ゲッターで何かの変数を取ってきて → 値を処理して → セッターで戻す、
というのは、カプセル化としては中途半端ではないか、
結局中身を外部から変更可能ではないか、というわけです。

では、どうするか? 「求めるな、命じよ」というOOの格言のように、
なるべくクラスのデータ処理はクラスで完結させ、ゲッター/セッターを介せず、
クラスに付いているメソッドで処理するようにしよう、というわけです。


たとえば、自動車のオブジェクトには、外からゲッター/セッターを介して、
速度や座標を計算して与えるのではなくて、オブジェクト自身にさせようと。
外部に公開するメソッドは、アクセルやブレーキのような単純な操作にすると。

あるいは、経理課の人(クラス)から帳簿のデータを見せてもらって、
他の部署(営業とか)の人が計算したデータを返すのではなくて、
会計のことは経理課の人たちで完結させるようにしようと。

これは現実的には、テストでゲッター/セッターがある方が便利だとか、
貫徹するのが難しい部分もありますが、考え方としてはアリでしょう。
というか、じつは私自身は、むしろこちらに近い立場です。

さて、もしかしたら、後半は混ぜ返す印象があったかもしれません。
ただ、ひとくちにOOといっても、考え方に幅があるので、
広く紹介しました。読者の方で取捨選択してください。

投稿2021/07/28 11:41

LLman

総合スコア5592

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

animeing

2021/07/28 13:36

意図をくみ取って回答していただき、ありがとうござます。 「ゲッター/セッター」不要論について色々調べてるときに 下のサイトを見つけてなかなか面白い考えだなと思いました。 [https://www.kaitoy.xyz/2015/07/22/getters-setters-evil/] この考えについてまだ良いとも悪いとも言えないですが、嫌いな考えではないです。 さて、意図をくみ取って回答していただいた点について 疑問に思った点を先読みされたかのように [ゲッターで何かの変数を取ってきて → 値を処理して → セッターで戻す、 というのは、カプセル化としては中途半端ではないか、]と感じました。 元々100箇所以上から呼ばれてる物を ゲッター・セッターにすることで確かに ゲッターの呼出し元・セッターの呼出し元というように分かれるので多少呼出し元を探しやすくはなると思いますが、 結局多いことに違いがなく、そうなってるということは 多くのプログラマさんの怒りを恐れずに言えば 「設計がまずってる気がする」なと感じました。
Zuishin

2021/07/28 13:49

これはオブジェクト指向をよくわかっていない人が多かった黎明期に書かれたもので、Java のゲッター・セッターについての言及です。 要するに「古い」です。 クラスと構造体を勘違いした人がとにかく必要なデータをすべて詰め込んだ巨大なクラスを作り、そこに山ほどあるフィールドのすべてにただアクセスするだけのゲッター・セッターを作っていた頃の話です。 昔は確かにそういう人がいました。 しかし今からそのやり方に学ぶ必要はないと思います。 このような古臭い方法を宣伝したいための質問であったのなら、そう書いていてくれれば回答しませんでした。 思想的に凝り固まった人に新しい概念を教えるのは面倒な作業である上に誰も得をしないからです。
animeing

2021/07/28 14:10 編集

古い考えだったのですね。 危うく古い考えを真剣に「良いのではないか」と考えるところでした。 宣伝の意図はなかったです。申し訳ありません。 すごい助かりました。
LLman

2021/07/28 14:20

>animeingさん >「ゲッター/セッター」不要論 じつは質問者の方も、もともと不要論(不使用論)の立場だったのですか? それなら、そういう質問にしておけば、「ゲッター/セッターの要/不要」 についての回答がもっと集まったでしょうに。 さて、「ドメインモデリング」や「DDD」を追求していくと、 ゲッター/セッターは、むしろ不純な仕組みにも思えてきます。 ゲッター/セッターだけあるデータの入れ物クラスを量産すると、 アンチパターンの「ドメインモデル貧血症」になりかねません。 ただ、たとえば現実的には、一定規模以上のアプリでは、DBを使う必要があり、 DBとはゲッター/セッター(クエリ)だけある、巨大なデータの入れ物じゃないか、 ということもあって、プログラム全体で貫徹するのは難しいですが。 ただじゃあ、DBがあるインフラ層とドメイン層のレイヤーを区別しようとか、 さらにいろいろな手法によって、DDDでは整合性を取ろうとしています。
LLman

2021/07/28 14:27

>Zuishinさん >要するに「古い」 リンク先のゲッター/セッター不要論に対してですか。 お考えは分かりましたが、でも、どうでしょうね? たとえば、「不変」「イミュータブル」に できる変数があればなるべくそうしよう、という 関数型の影響を受けた流れは最近でもありますし。 セッターを使った時点で不変ではなくなるので、 禁欲することに今でも有効性はあると思います。 ただこれは、GoToやグローバル変数とかと同じで、 「絶対使うな」ではなく、なるべく節約する感じですが。
animeing

2021/07/28 14:40

>じつは質問者の方も、もともと不要論(不使用論)の立場だったのですか? 質問する前に単純なゲッター・セッターを書いててふと疑問に思い調べていて、 どうしても分からなかったので、ゲッター・セッターを逆に要らないという人の意見は あるのかないのか、あるならどんな理由なのかなと 知的好奇心で調べたところ不要論の記事を見つけた次第です。 そして、この内容にうまく反論できなかったのですが、自分の知識の中に ゲッターセッターは使うべきというのがあったので、 正しくゲッター・セッターの有用性を自分が理解できてないのだろうなと思い 質問したという感じです。 もし不快に思われたのであれば申し訳ありません。
LLman

2021/07/28 14:47

>animeingさん >もし不快に思われたのであれば いえぜんぜん。お気になさらず。 どのような技術を取捨選択するかは、本人の自由であり、 あまり価値観を押しつけると、宗教論争的になりますから。 ただ、少数派の意見を紹介するようなことは、あえてやります。 それは天の邪鬼や逆張りをしたいからではなくて、 たんにいろいろな可能性に触れて欲しいからです。
Zuishin

2021/07/28 15:03

> どのような技術を取捨選択するかは、本人の自由であり、 > あまり価値観を押しつけると、宗教論争的になりますから。 プログラミングには可読性を上げるためにコーディング規約というものがあります。 それぞれが自分の宗教に従って自由なコードを書いていたのでは非常に読みにくくなります。 私が回答に真っ先に書いたように、C# のコーディング規約ではフィールドを公開するのは非推奨です。 イミュータブルの話もどこで出たのかよくわかりませんでした。 今回の質問とは関係ないように思います。 C# ではイミュータブルなオブジェクトを作るのにもプロパティを使います。 いろいろな可能性に触れてほしいと言えば聞こえはいいですが、それは正しい方法を会得してからにすべきでしょうね。 六代目中村勘九郎は「型があるから型破り。型がなければそれは型なし」と言い、利休は「守破離」と言いました。 順番を間違えてはいけません。 型を知らない崩し字はただの下手な字です。
LLman

2021/07/28 15:52

>Zuishinさん >コーディング規約 集団で開発する、もしくは不特定多数に公開するなら、 なるべく規約を守るべきですね。それには同意しますよ。 べつに「フィールドを公開する」ことを推奨してもいませんし。 >イミュータブルの話 >今回の質問とは関係ない そうですかね? 不変の時点で、セッターを使わないことと同等なので、関係はあるでしょう? >イミュータブルなオブジェクトを作るのにもプロパティを使います だから、不要論と区別する、「不使用論」っていう言葉を使ったんです。 ゲッター/セッター(プロパティ)という仕組み自体はあってもいいが、 クラスの外部からゲッター/セッター/プロパティで、 変数を取得して、処理して変更する、というのがカプセル化に反すると。 カプセル化と「責務」を考えたら、なるべく、 クラス自身が自分のデータを処理するのが、 本来のOOだろうというのが、不使用論の考え方ですね。 >「守破離」 「型」「順番」というなら、そもそも使わない方が型だったんですよ。 それが、C++やJavaの普及で、従来の手続き型の手法が混じって、 ゲッター/セッターを大量に作り、外から操作するのが一般化したと。 だからむしろ、「純粋OO」とかOO原理主義的な文脈だと、 ゲッター/セッターをなるべく使わない方が、型であり「守」なんです。 ただ、「純粋関数型」を実現しようとすると大変なように、 純粋OOを実現するのも大変なので、「破」「離」というか、 妥協して手続き的発想を混ぜることも、現実にはよくあります。
Zuishin

2021/07/28 21:59 編集

今回の質問を読み直してください。 ただのデータストアならプロパティではなくフィールドでいいんじゃないか、なぜそれがいけないのかという質問です。 あなたの回答はそれにこたえていません。 > そうですかね? 不変の時点で、セッターを使わないことと同等なので、関係はあるでしょう? いいえ。質問を読み直してください。 > だから、不要論と区別する、「不使用論」っていう言葉を使ったんです。 不要ではなく使わないというあなたの宗教の勧誘でしかありません。 質問とは無関係です。 > 「型」「順番」というなら、そもそも使わない方が型だったんですよ。 いいえ。私は使うのが型である根拠を示しました。 あなたのそれには何の根拠もありません。 質問にも関係ありません。 > 妥協して手続き的発想を混ぜることも、現実にはよくあります。 それは今回の質問には無関係です。 この回答は原理主義ですらない「あなたが教祖の独自宗教」の布教でしかなく、質問の回答になっていないということが自覚できていますか? 自覚できていないのなら読み直して自覚してください。 自覚できて行なっているのなら、迷惑行為はやめてください。
ko_yu

2022/06/12 17:10

私にはLLmanさんの回答のほうが分かりやすかったです。回答ありがとうございます。
guest

0

同じような話があったので,参考になるかも?

投稿2021/07/28 03:48

fana

総合スコア11996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ブレークポイントを設定できます。

投稿2021/07/28 03:41

ozwk

総合スコア13553

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問