はじめに
こんにちは。
今回は、オブジェクト指向で重要であるカプセル化について説明をしていきたいと思います。
「え、そもそもC#とか初めてなんだけど。。。」
「プログラミングの基礎がわからない。」
という方向けの記事ではないので以下のサイトの基本編を学んでからこの記事を読んでください。
→一週間で身につくC#言語の基本
また、オブジェクト指向の基礎を理解していない方はこちらをご覧ください。
→【C#】オブジェクト指向(クラスとオブジェクト)
カプセル化とはなにか
カプセル化は一言でいうと簡単な操作で使える範囲を限定にするということです。
私たちは、日常生活で自動車、テレビ、携帯電話などさまざまな道具に囲まれています。それらの中身はとても複雑な構造をしていますが、日常で使用するときに中身を意識することはほとんどありません。私たちの身の回りの機械は、複雑な仕組みが隠蔽されたブラックボックスであるにもかかわらず、問題なく操作できるように設計されています。
実は、オブジェクト指向プログラミングにも同様の考え方が取り入られています。クラスの複雑な仕組みはなるべく隠蔽し、必要最低限の操作で外部から操作できるようにすることが推奨されています。このような考え方を、オブジェクトという容器の中に入れて保護するというイメージからカプセル化といいます。カプセル化のためにフィールドやメソッドを保護する仕組みとして、「アクセス修飾子」があります。
セッターとゲッター
すべてのフィールドで外部からの操作を禁止にするのは現実的ではありません。そこで、オブジェクト指向言語では、アクセスが必要なフィールドには、そのフィールドにアクセスするための専用メソッドを追加することでこの問題を解決します。
例えば、Personクラスにageというフィールドがあったとします。このとき、このフィールドに値を設定するためのメソッドをsetAge()などのように定め、このメソッドを通じてageの値を変更します。このように、フィールドの値を取得するメソッドのことを、オブジェクト指向プログラミングの世界ではセッター(setter) と呼んでいます。その逆に、フィールドageの値を取得するためには別途getAge()などというメソッドを用います。このようにフィールドの値を取得するためのメソッドのことを、ゲッター(getter) といいます。
このように読み書きの処理を分けることにより、外部からの不正なアクセスを防ぐことができます。C++やJavaでも、カプセル化の際にはセッターやゲッターを使いますが、C#では通常のメソッドとは別に、セッター、ゲッターを作成するための仕組みが用意されています。
アクセス修飾子の種類
フィールドやメソッドを保護するために使用するのが、アクセス修飾子です。ここでは、アクセス修飾子を利用して、メンバの可視性を指定する方法について説明します。
まずは以下のサンプルプログラムを入力してみてください。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test_CS
{
class Person
{
// 名前(フィールド)
private string name = "";
// 年齢(フィールド)
private int age = 0;
// 情報の設定
public void SetAgeAndName(string name, int age)
{
this.name = name;
this.age = age;
}
// 情報の表示(メソッド)
public void ShowAgeAndName()
{
Console.WriteLine("名前:{0} 年齢:{1}", name, age);
}
// 情報の設定
public string Name
{
set { name = value; }
get { return name; }
}
// 情報の設定
public int Age
{
set { age = value; }
get { return age; }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test_CS
{
class Program
{
static void Main(string[] args)
{
Person p1, p2;
// 1つ目のPersonクラスのメソッドのインスタンスを生成
p1 = new Person();
// 2つ目のPersonクラスのメソッドのインスタンスを生成
p2 = new Person();
p1.Name = "山田太郎"; // フィールドnameに値を代入
p1.Age = 19; // フィールドageに値を代入
// setAgeAndName()メソッドでnameとageを設定
p2.SetAgeAndName("佐藤花子", 23);
// それぞのインスタンスnameとageを表示
p1.ShowAgeAndName();
// プロパティから名前と年齢を表示
Console.WriteLine("名前:{0} 年齢:{1}",
p2.Name, p2.Age);
}
}
}
名前:山田太郎 年齢:19
名前:佐藤花子 年齢:23
続行するには何かキーを押してください . . .
まずは、Person.csの先頭部分を見てください。privateという修飾子が付いています。この他にも、メソッドにpublicという修飾子がついています。これらがアクセス修飾子です。
アクセス修飾子とは、フィールドおよびメソッドへのアクセスの制限を指定するためのものです。C#のアクセス修飾子には、以下のようなものがあります。
・C#のアクセス修飾子
名前 | 呼び名 | 意味 |
---|---|---|
public | パブリック | どこからでもアクセスできる |
protected | プロテクティッド | 同一クラスか、そのサブクラスからしかアクセスできない |
internal | インターナル | 同一のアセンブリ(DLL)内でアクセスできる |
protected internal | プロテクティッドインターナル | protected かつ internal |
private | プライベート | 同じクラス内からしかアクセスできない |
今回のPersonクラスでは、メソッドはpublicなので、外部のクラスであるProgramからアクセス可能です。
フィールドにはprivateが付いているので、クラスの外からアクセスできません。しかし、Person.csのメソッド内では、これらのフィールドにアクセスしています。privateはクラス内からアクセスは制限しないからです。
プロパティ
C#にはもともとカプセル化をするために、言語仕様としてセッターやゲッターを記述するための仕組みを用意しています。それがプロパティと呼ばれるものです。
プロパティは、セッター/ゲッターを一つのメソッドで記述できる大変便利なメソッドです。setやgetなどの名前を付ける代わりに、フィールド名の先頭を大文字にしたものをプロパティ名とします。例えば、nameという名前のフィールドのプロパティはName、ageというフィールドの名前はAgeといった具合です。
ただし、記述の仕方は一般のメソッドとは異なり、次のように記述します。set{}、get{}の部分をアクセサーといい、そこにフィールドにアクセスする処理を書きます。
アクセス修飾子 型名 プロパティ名
{
set
{
// setアクセサー(setter ともいう)
// ここに値の変更時の処理を書く。
// valueという名前の変数に代入された値が格納される。
}
get
{
// getアクセサー(getterともいう)
// ここに値の取得時の処理を書く。
// メソッドの場合と同様に、値はreturnキーワードを用いて返す。
Nameプロパティを見てみましょう。
public string Name
{
set { name = value; }
get { return name; }
}
フィールドnameに対するプロパティなので、名前はNameとなります。また、nameはstring型なので、型名もstringになります。
setアクセサーに出てくるvalueは、外部から与えられた値を返します。例えば、Program.csで以下のようにプロパティの値を設定していますが、この場合のvalueは"山田太郎"になるわけです。
p1.Name = "山田太郎";
これにより、フィールドnameに"山田太郎"という文字列が代入されます。
おわりに
いかがでしたでしょうか。大規模な開発となると分担してソースコードを書いていくわけですがアクセス制限をかけないと外部からの予期しない干渉がおき障害につながることがあります。カプセル化はそれを防ぐことができるのです。カプセル化は書き込み専門・読み込み専門にしたり、フィールドなしでも定義できる自動実装プロパティもあるので機会があったら説明します。
投稿者
エンジニアファーストの会社 株式会社CRE-CO 田渕浩之