#はじめに
雰囲気で理解していたら業務中に痛い目にあったので、真面目に調べてみることにしました。
調べていると、どうやらオブジェクト指向も理解しておく必要があると気づいたので、それらも含めてまとめてみました。
#メンバーとフィールドについて
こちらの方は比較的簡単のため、先に解説しておきます。
public class Person
{
const int ADULT = 20; // 定数(フィールドではない)
private string Name; // フィールド
private int Age; // フィールド
// コンストラクター(フィールドではない)
public Person(string name, int age)
{
this.Name = name;
this.Age = age;
}
// メソッド(フィールドではない)
public void printPerson()
{
if(this.Age > ADULT)
{
Console.Write(Name + "は成人しています");
}
else
{
Console.Write(Name + "は未成年です");
}
}
}
##フィールド
クラスの中で定義してある変数部分のことです。
上のコードではNameとAgeです。
また、フィールドはメンバー変数とも呼ばれるようです。
##メンバー
メンバーは、上記のコードのコメントに書いてあるもの全てをさします。
フィールド, 定数, メソッド, コンストラクター全てがメンバーになります。
また、フィールドや定数はデータメンバー、メソッドやコンストラクターを関数メンバーといいます。
#プロパティについて
プロパティについて説明する前に、まずはオブジェクト思考の実装の隠蔽とカプセル化について説明しないといけません。
##実装の隠蔽とカプセル化
###実装の隠蔽とは
通常、内部の実装がどうなっているのかを隠蔽(要するに private にする)し、可能な操作のみを公開(public)することが望ましいとされています。 簡単に言うと、メンバー変数はクラス外部から直接アクセス出来ないようにして、オブジェクトの状態の変更はすべてメソッドを通して行うべきだということです。
実装の隠蔽 - C# によるプログラミング入門 | ++C++; // 未確認飛行 Cから引用
文章だけでもかなりわかりやすく説明されていますが、コードを使ってもう少しわかりやすく説明します。
上で作ったPersonクラスに性別とニックネームの設定をしたいと思います。
NameとAgeはコンストラクターで設定しますが、性別とニックネームはコンストラクターで設定はしないとします。
public class Person
{
public string Sex; // 性別
public string Nickname; // ニックネーム
}
public class Sample
{
person1 Taro = new person("Taro", 15);
// 性別とニックネームを設定する
Taro.Sex = "男";
Taro.Nickname ="tataroro";
// ニックネームを表示する
Console.Write(Taro.Nickname);
}
まずは隠蔽しない場合の例です。
SexとNickNameのアクセスレベルはPublicであるので、他のクラスから直接指定することが出来ます。
これの何が問題かというとTaro.Sex = "ああああ"という設定も可能であるということです。ニックネームが「ああああ」であろうと問題ありませんが、性別は「男」「女」のみに設定したいと思います。
次は隠蔽を行ったコードを書いてみます。
public class Person
{
private string Sex; // 性別
private string Nickname; // ニックネーム
public void SetSex(bool judge)
{
this.Sex = judge ? "男" : "女";
}
public void GetNickName() { return this.Nickname; } // フィールドのニックネームを返す
public void SetNickName(string nickname) { this.Nickname = nickname; } // フィールドにニックネームを設定する
}
public class Sample
{
person1 Taro = new person("Taro", 15);
// 性別とニックネームを設定する
Taro.SetSex(true);
Taro.SetNickName("tataroro");
// ニックネームを表示する
Console.Write(GetNickName());
}
SexとNickNameのアクセスレベルをprivateに変更したため、値を直接セットすることは出来なくなりました。
SexとNickNameに値を代入するには必ずメソッドを経由して値がセットされるため、Sexには男か女しか入りません。
(本来は男女を真偽値で設定するのはあまりよくありません。ここでは簡単な例として使用しています)
このように変数を保護(private)して、外部からのアクセスにはメソッドを提供することをカプセル化と言います。
##プロパティ
上記の内容を踏まえた上でプロパティの説明に入ります。
プロパティとは、クラス外部から見るとメンバー変数のように振る舞い、クラス内部から見るとメソッドのように振舞うものです。
いまいちイメージしづらいので、実際にコードを使って説明します。
public class Person
{
private string Sex; // 性別
private string _nickname; // ニックネーム
public void SetSex(bool judge)
{
this.Sex = judge ? "男" : "女";
}
// プロパティ
public string NickName
{
get { return _nickname; } // フィールドのニックネームを返す
set { _nickname = value; } // フィールドにニックネームを設定する
}
}
public class Sample
{
person1 Taro = new person("Taro", 15);
// 性別とニックネームを設定する
Taro.SetSex(true);
Taro.SetNickName = "tataroro"; // setの部分が呼び出される
// ニックネームを表示する
Console.Write(Taro.SetNickName); // getの部分が呼び出される
}
このコードの処理は一つ前のコードと同じ処理をしています。
一つ前のコードでは、SetNickName、GetNickNameのメソッドを用いて値の設定と表示を行っていましたが、今回はプロパティNickNameに変更して同じような処理を行っています。(プロパティ名とフィールド名を区別するために、フィールド名は_nicknameに変更しています)
プロパティを用いることによりPersonクラスではメソッドの役割をするNickNameが、Sampleクラスからはメンバ変数と同じ扱い方をしていることがわかりますね。
これが上記で説明した「クラス外部から見るとメンバー変数のように振る舞い、クラス内部から見るとメソッドのように振舞うものです。」ということです。
また上のプロパティの記述は{ get; set; }と短縮することも可能です。
public string SetNickName
{
get { return NickName; }
set { Sex = value; }
}
// 上のプロパティは以下のように短縮できる
public string SetNickName { get; set; }
#おわりに
点と点の知識が上手く繋がった気がしました。勉強する前は、カプセル化をの概念が曖昧だったため、プロパティのメリットなどを理解していませんでした。
間違った部分などがあればコメントにてお願いします。
#参考
【C#】「カプセル化」をもう一度学ぶ
プロパティ?フィールド?メンバー?C#のクラス構造のおさらい
C#でカプセル化を使ったオブジェクト指向設計の実装方法を解説!!
カプセル化するとインスタンス変数は賢くなる
[C# の型とメンバー - C# によるプログラミング入門 | ++C++; // 未確認飛行 C]
(https://ufcpp.net/study/csharp/list_type.html)
プロパティ - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
実装の隠蔽 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C