C#のプロパティを使いこなそう!さまざまな実装方法を紹介

C#のプロパティを使いこなそう!さまざまな実装方法を紹介のアイキャッチイメージ

C#のプロパティとは?

C#のプロパティとは、クラスのメンバ変数を手軽に使えるようにするための便利な機能です。ただしプロパティについて学ぶ前に、まずオブジェクト指向の概念の一つ「カプセル化」について理解しておく必要があります。以下のプログラムを見てください。

class Human
{
 public string name;
 public int age;
}

Human hu = new Human();
hu.name = "太郎";
hu.age = 20;

Console.WriteLine(hu.name + " " + hu.age);

カプセル化の具体例

クラスHumanのメンバ変数に値を代入し、その後で取り出しています。上のようなプログラムはとても手軽で簡単です。個人の趣味でプログラミングをする際にはこのようなスタイルでも全く問題は無いでしょう。

しかし、業務で大規模なシステムのプログラミングをする場合、このような作りにはしません。以下のようにします。

class Human
{
 private string m_name;
 private int m_age;

 public string getName()
 {
  return m_name;
 }
 public int getAge()
 {
  return m_age;
 }
 public void setName(string name)
 {
  m_name = name;
 }
 public void setAge(int age)
 {
  m_age = age;
 }
}

Human hu = new Human();
hu.setName("太郎");
hu.setAge(20);

Console.WriteLine(hu.getName() + " " + hu.getAge());

getterとsetterはカプセル化には必須

Humanクラスのメンバ変数をprivateにして直接アクセスせず、getメソッドとsetメソッドでアクセスします。このメソッドはgetterとsetterと呼ばれます。なぜこのようにする必要があるのでしょうか。

大勢で1つのシステムの開発を行う場合、自分の担当していないクラスの中身まで知る必要はなく、ブラックボックスとして扱えるようにしなければなりません。クラスのインターフェイスの仕様を知るだけで使えるようにするべきです。この設計手法がカプセル化です。

具体的には上の例のようにクラスのメンバ変数はprivateにして、アクセスするメソッドをpublicとして公開する作り方を、カプセル化と言います。

getterとsetterのメリット

getterとsetterを使うことでカプセル化ができるだけでなく、範囲チェックやログ出力も可能になります。例えば上の例のsetAgeメソッド内で、以下のようなことができます。

 public void setAge(int age)
 {
  if (age < 0)
 {
  ※「ageの値が範囲外」といったエラーログ出力処理をする
  return;
 }
 m_age = age;
 }

クラスのメンバ変数をpublicにして直接アクセスしていると、このような前処理はできません。

getterとsetterのデメリット

しかしgetterとsetterは良い事ばかりではありません。クラスのメンバ変数が多くなるとgetterとsetterの数がとても多くなり、非常に見づらいソースコードになります。C++やJavaなどの開発環境ではgetterとsetterを自動生成してくれる機能もありますが、見づらいことには変わりありません。

その問題を解決してくれるのが、C#のプロパティなのです。

プロパティでgetterとsetterを簡略化

C#のプロパティを使えばgetterとsetterを以下のように簡略化できます。

class Human
{
 private string m_name;
 private int m_age;

 public string Name // m_nameのプロパティ
 {
  get { return m_name; }
  set { m_name = value; }
 }
 public int Age // m_ageのプロパティ
 {
  get { return m_age; }
  set { if (value >= 0) m_age = value; }
 }
}

Human hu = new Human();
hu.Name = "太郎";
hu.Age = 20;

Console.WriteLine(hu.Name + " " + hu.Age);

プロパティならメンバ変数に簡単にアクセスできる

上の例ではNameがm_nameのプロパティ、Ageがm_ageのプロパティとなります。プロパティの中ではsetとgetでアクセス処理を記述しています。Ageのsetにあるように、範囲チェックなどの処理も加えることができます。

プロパティのメンバ変数への値の代入と取得は「クラス変数.プロパティ」で行えます。メンバ変数に直接アクセスする記述と似ていますが、ちゃんとプロパティ内に記述したgetとsetの処理は呼び出されています。

プロパティのアクセス制御

プロパティのgetとsetはそのままでpublicな状態で自由にアクセスできますが、宣言でアクセシビリティを設定すれば個別にアクセスを制御できます。

protected set { m_age = value; }

このようにすれば同じクラスと派生したクラスでのみプロパティに値を代入できるようになります。

private set { m_age = value; }

こうすれば、同じクラス内だけしかm_ageに値を入れることはできません。クラスの外からは読み取りだけにしたい場合に使えますね。

プロパティにはメンバ変数は必須ではない

実はC#のプロパティにはクラスのメンバ変数は必ずしも必須ではありません。以下のようにメンバ変数と無関係なプロパティを作ることもできます。

class Human
{
 public string Yamada
 {
  get { return "山田"; }
  set { Console.WriteLine("value is " + value); }
 }
}

Human hu = new Human();

Console.WriteLine(hu.Yamada);
hu.Yamada = "太郎";

これを実行すると以下のようになります。普通のメソッドでやれば良いようにも感じますが、役に立つ場合もあるかもしれません。

山田
value is 太郎

自動プロパティで記述を更に簡略化できる

C#には「自動プロパティ」という機能があり、これを使うとプロパティの記述を更に簡略化できます。以下のようにします。

class Human
{
 // メンバ変数は不要

 public string Name
 {
  get; set; // これだけでOK
 }
 public int Age
 {
  get; set; // これだけでOK
 }
}

メンバ変数を記述せずに、プロパティへの値の代入と取得ができてしまいます。なお、プロパティ内部で使用されている変数については隠蔽されていて、見ることはできません。

また自動プロパティは単純な代入・取得だけしかできないので、例えば値の範囲チェックなどを行いたい場合、自動プロパティは使えません。

読み取り専用のプロパティ

C#では読み取り専用のプロパティを作ることも可能です。以下のようにします。

class Human
{
 public string Name
 {
  get; // getのみを記述
 }
 public int Age
 {
  get; // getのみを記述
 }

 //コンストラクタ
 public Human(string name, int age)
 {
  Name = name; // プロパティに代入
  Age = age; // プロパティに代入
 }
}

Human hu = new Human("太郎", 20);
Console.WriteLine(hu.Name + " " + hu.Age);

クラスのインスタンス生成時のコンストラクタのみで代入可能になるので、途中で値を変更されたくない場合に便利です。

式形式のプロパティ

C#は読み取り専用のプロパティを使って、式形式のプロパティを作ることができます。例としては、上のHumanクラスに以下の1行のプロパティを追加します。

public string NameAndAge => Name + ” ” + Age;

これを以下のように使用することができます。

Human hu = new Human(“太郎”, 20);
Console.WriteLine(hu.NameAndAge);

結果は以下になります。

太郎 20

自動プロパティの初期化

C#の自動プロパティには初期値を持たせることができます。以下のようにすれば初期化できます。

public string Name { get; set; } = “花子”;
public int Age { get; set; } = 30;

普通の自動プロパティでもgetだけの読み取り専用のプロパティでも、同様に初期化出来ます。

プロパティの配列

C#のプロパティで配列変数を使いたい場合はどうすればいいのでしょうか。以下のようにすれば可能です。

class Human
{
 public string[] NameAry { get; set; }
}

Human hu = new Human();
string[] names = new string[] { "太郎", "花子", "幸雄" };
hu.NameAry = names;

for (int i = 0; i < hu.NameAry.Length; i++)
{
 Console.WriteLine(hu.NameAry[i]);
}

これを実行すると以下のようになります。

太郎
花子
幸雄

おわりに

C#のプロパティについて理解できましたでしょうか。プロパティはC++やJavaといった他のオブジェクト指向言語にはない優れた機能なので、是非活用してみてください。