アクセス指定子

メンバへのアクセスの許可

6日目では、クラスおよびインスタンスの生成について説明しました。ここでは、さらにその内容を深めていこうとしていくことにします。クラスには、フィールドおよび、メソッドと呼ばれるものがあることはすでに説明しました。

このフィールドおよびメソッドには、アクセス許可を指定することにより、アクセスできる範囲を指定することができます。そのために必要なのが、アクセス指定子です。ここでは、アクセス指定子を利用して、メンバの可視性を指定する方法について説明します。

サンプルプログラム

以下のプログラムを実行してみてください。

プロジェクト:SampleEx701/ファイル名Person.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample701
{
    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; }
        }
    }
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample701
{
    class Program
    {
        static void Main(string[] args)
        {
            Person p1,p2;
            p1 = new Person();       //  一つ目のPersonクラスのメソッドのインスタンスを生成
            p2 = new Person();       //  二つ目のPersonクラスのメソッドのインスタンスを生成
            p1.Name = "山田太郎";    //  フィールドnameに値を代入
            p1.Age = 19;             //  フィールドageに値を代入
            p2.SetAgeAndName("佐藤花子", 23);   //  setAgeAndName()メソッドで、nameとageを設定
            p1.ShowAgeAndName();     //  メソッドから、名前と年齢を表示
            //  プロパティから名前と年齢を表示
            Console.WriteLine("名前:{0} 年齢:{1}", p2.Name, p2.Age);
        }
    }
}
実行結果
名前:山田太郎 年齢:19
名前:佐藤花子 年齢:23

アクセス修飾子の種類

このプログラムは、第6日目のSample601を若干変更したものです。実行結果は同じですが、細部に違いがあります。まずは、

Person.csの先頭部分を見てください。privateという修飾子がついていると思います。この他にも、今まで詳しく説明してきませんでしたが、メソッドにpublicという修飾子がついています。これらを、アクセス修飾子と言います。

アクセス修飾子とは、フィールドおよびメソッドへのアクセスの制限を指定するためのものです。C#のアクセス修飾子には、以下のようなものがあります。(表7-1)

表7-1.C#のアクセス指定子
アクセス指定子 呼び名 意味
public パブリック どこからでもアクセスできる。
protected プロテクティッド 同一クラスか、そのサブクラスからしか呼び出せない。
internal インターナル 同一のアセンブリ(DLL)内でアクセスが可能。
protected internal プロテクティッドインターナル protected かつ internal。
private プライベート 同じクラス内からしか呼び出せない。

サンプルの解説

以上を踏まえて、サンプルの解説をしてききます。Personクラスにおいて、メソッドは、publicなので、外部のクラスであるProgramからアクセス可能なので、Person.csの18行目および19行目でアクセスしています。しかし、フィールド、privateがついているので、クラスの外からアクセスできません。

しかし、Person.csのメソッド内では、これらメンバにアクセスしています。これは、privateはクラス内からアクセスできるからです。

プロパティ

C#言語に限らず、オブジェクト指向言語では、基本的にフィールドはprivateにして、外部から隠蔽することが常識とされています。しかし、それでは、外部からアクセスできません。そこで、C#言語では、プロパティと呼ばれるものが用意されており、外部からフィールドへアクセスできるようにしています。

プロパティ
アクセス修飾子 型名 プロパティ名
{     set
    {
        // setアクセサー(setter とも言う)
        // ここに値の変更時の処理を書く。
        // value という名前の変数に代入された値が格納される。
    }
    get
    {
        // getアクセサー (getter とも言う)
        // ここに値の取得時の処理を書く。
        // メソッドの場合と同様に、値はreturnキーワードを用いて返す。
    }
}

Person.csの27行目から31行目までがフィールドnameの、33行目から37行目までがフィールドAgeののプロパティになっています。ここでは、nameプロパティから見てみましょう。

PersonクラスのNameプロパティ
public string Name
{
    set { name = value; }
    get { return name; }
}

一般に、プロパティの名前は、変数名に合わせ、先頭を大文字にするのが一般的です。フィールドnameの場合、プロパティ名は、Nameとなります。また、nameはString型なので、型名もStringになります。

setアクセサーに出てくる、valueは、外部から与えられた値を指します。例えば、Program.csの16行目で、以下のようにしてプロパティの値を設定していますが、この場合、valueは、"山田太郎"になるわけです。

Nameプロパティでsetアクセサーへのアクセス
p1.Name = "山田太郎";

これにより、フィールドnameに、"山田太郎"という文字列が代入されます。

読み込み専門・書き込み専門のプロパティ

サンプルプログラム

Sample701では、読み込み・書き込みの両方ができるプロパティがありましたが、そのどちらかしかないというようなものもあります。まずは、以下のサンプルを実行してみてください。

プロジェクト:Sample702/ファイル名Access.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample702
{
    class Access
    {
        //  読み込みオンリーのデータ
        private int data1 = 5;
        //  書き込みオンリーのデータ
        private int data2 = 0;
        //  値の表示
        public void ShowDatas()
        {
            Console.WriteLine("data1={0} data2={1}", data1, data2);
        }
        //  data1のプロパティ(読み込みオンリー)
        public int Data1
        {
            get { return data1; }
        }
        //  data2のプロパティ(書き込みオンリー)
        public int Data2
        {
            set { data2 = value; }
        }
    }
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample702
{
    class Program
    {
        static void Main(string[] args)
        {
            Access a = new Access();
            //a.Data1 = 1;
            a.Data2 = 2;
            a.ShowDatas();
            Console.WriteLine("a.data1 = {0}", a.Data1);
            //Console.WriteLine("a.data2 = {0}", a.Data2);
        }
    }
}
実行結果
data1=5 data2=2
a.data1 = 5

Access.csの21行目から24行目を見てください。フィールドdata1のプロパティが定義されていますが、getプロパティしか定義されていません。そのため、このプロパティは、読み込みしかできません。試しにProgram.csの14行目のコメントをとってみてください。ビルドエラーが出ます。

同様に、Access.csの26行目から29行目を見てください。こちらの場合は、data2のプロティですが、setしか定義されていません。そのため、書き込みしかできません。なので、やはりProgram.csの18行目のコメントを取ると、ビルドエラーが出ます。

このように、プロパティは、セッターのみ、ゲッターのみの定義が可能です。

自動実装プロパティ

フィールドを必要としないプロパティ

次に、プロパティの更に高度な使い方について説明します。表題の自動実装プロパティとは、フィールドを必要としないプロパティです。それが具体的にどのようなものなのか、以下のサンプルを入力・実行してみてください。

プロジェクト:SampleEx703/ファイル名Person2.cs
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;
	using System.Threading.Tasks;

	namespace Sample703
	{
		   class Person2
		   {
		       //  情報の設定
		       public void SetAgeAndName(string name, int age)
		       {
		           Name = name;
		           Age = age;
		       }
		       //  情報の表示(メソッド)
		       public void ShowAgeAndName()
		       {
		           Console.WriteLine("名前:{0} 年齢:{1}", Name, Age);
		       }
		       //  情報の設定
		       public string Name
		       {
		           private set; get;
		       }
		       //  情報の設定
		       public int Age
		       {
		           set; get;
		       }
		   }
	}
	
Program.cs
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;
	using System.Threading.Tasks;

	namespace Sample703
	{
		   class Program
		   {
		       static void Main(string[] args)
		       {
		           Person2 p = new Person2();
		           //  名前と年齢を設定
		           p.SetAgeAndName("山田太郎", 26);
		           //  年齢の変更
		           p.Age = 32;
		           //  名前の変更(できない)
		           //  p.Name = 36;
		           //  名前と年齢の表示
		           Console.WriteLine("名前:{0} 年齢:{1}", p.Name, p.Age);
		       }
		   }
	}
	
実行結果
名前:山田太郎 年齢:32

このプログラムのPerson2クラスを見ると、フィールドが全く定義されていないのにもかかわらず、プロパティNameAgeが定義されて利用されています。このように、対応するフィールドを用意しなくても利用できるプロパティのことを、自動実装プロパティと言います。書式は以下の通りになります。

自動実装プロパティ①(読み込み・書き込み両方対応)
アクセス修飾子 型名 プロパティ名
{
    set; get;
}

自動実装プロパティは大変便利ですが、対応するフィールドがなくなるため、メソッド内では直接プロパティに対して書き込み・読み込み処理を行います。

更に、自動実装プロパティは以下のような方法で書き込み専用にすることが出来ます。

自動実装プロパティ②(読み込み専用)
アクセス修飾子 型名 プロパティ名
{
    private set; get;
}

setの前にprivateが付くことで、クラス内ならばこのプロパティにアクセスすることはできますが、外部からは呼び出すことが出来なくなります。これにより読み取り専用のプロパティを定義できます。

このサンプルでは、NameおよびAgeという二種類のプロパティが定義されていますが、このうちNameは読み取り専用になっています。そのため、19行目のコメントをとると、エラーになります。

また、このプロパティは対応するフィールドがないため、Person2クラス内のメソッドでは、Name、Ageとしてアクセスをしています。


練習問題 : 問題7.