7日目:アクセス指定子とプロパティ

クラスのメンバへのアクセス制御と、プロパティの使い方を学びましょう

日数: 0 1 2 3 4 5 6 7

アクセス指定子

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

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

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

サンプルプログラム

以下のプログラムを実行してみてください。プロジェクト Sample701 を作成し、ファイル Person.cs を追加して以下のプログラムを入力してください。

プロジェクト: Sample701 / ファイル名: 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 SetNameAndAge(string name, int age)
        {
            this.name = name;
            this.age  = age;
        }
        //  情報の表示(メソッド)
        public void ShowNameAndAge()
        {
            Console.WriteLine("名前:{0} 年齢:{1}", name, age);
        }
        //  Name プロパティ
        public string Name
        {
            set { name = value; }
            get { return name;  }
        }
        //  Age プロパティ
        public int Age
        {
            set { age = value; }
            get { return age;  }
        }
    }
}
プロジェクト: Sample701 / ファイル名: 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();  //  1つ目の Person クラスのインスタンスを生成
            p2 = new Person();  //  2つ目の Person クラスのインスタンスを生成
            p1.Name = "山田太郎";  //  Name プロパティで name に値を代入
            p1.Age  = 19;          //  Age プロパティで age に値を代入
            p2.SetNameAndAge("佐藤花子", 23);  //  SetNameAndAge() メソッドで name と age を設定
            p1.ShowNameAndAge();  //  メソッドから名前と年齢を表示
            //  プロパティから名前と年齢を表示
            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 からアクセスできます。一方、フィールドには private が付いているため、クラスの外からは直接アクセスできません。

ただし、Person.cs のメソッド内ではこれらのフィールドにアクセスしています。これは、private はクラス内からならアクセスできるからです。

💡 オブジェクト指向では、フィールドを 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、型名も string となります。

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

Name プロパティの set アクセサーへのアクセス
p1.Name = "山田太郎"; // value = "山田太郎" として set アクセサーが呼ばれる

これにより、フィールド 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; }
        }
    }
}
プロジェクト: Sample702 / ファイル名: 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;  // Data1 は読み込み専用なのでコメントを外すとエラー
            a.Data2 = 2;
            a.ShowDatas();
            Console.WriteLine("a.Data1 = {0}", a.Data1);
            //Console.WriteLine("a.Data2 = {0}", a.Data2);  // Data2 は書き込み専用なのでエラー
        }
    }
}
実行結果
data1=5 data2=2
a.Data1 = 5

get のみ・set のみのプロパティ

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

同様に、Access.cs の26〜29行目のフィールド data2 のプロパティには set しか定義されていません。そのため、このプロパティは書き込み専用です。Program.cs の18行目のコメントを外すとビルドエラーになります。

⚠️ プロパティは set のみ、get のみの定義が可能です。用途に応じて使い分けましょう。

自動実装プロパティ

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

次に、プロパティのさらに高度な使い方について説明します。自動実装プロパティとは、対応するフィールドを定義しなくても利用できるプロパティです。以下のサンプルを入力・実行してみてください。

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

namespace Sample703
{
    class Person2
    {
        //  情報の設定(メソッド)
        public void SetNameAndAge(string name, int age)
        {
            Name = name;
            Age  = age;
        }
        //  情報の表示(メソッド)
        public void ShowNameAndAge()
        {
            Console.WriteLine("名前:{0} 年齢:{1}", Name, Age);
        }
        //  Name プロパティ(クラス外からは読み込み専用)
        public string Name
        {
            private set; get;
        }
        //  Age プロパティ(読み書き両方対応)
        public int Age
        {
            set; get;
        }
    }
}
プロジェクト: Sample703 / ファイル名: 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.SetNameAndAge("山田太郎", 26);
            //  年齢の変更
            p.Age = 32;
            //  名前の変更(クラス外からは不可)
            //  p.Name = "鈴木一郎";  // コメントを外すとエラー
            //  名前と年齢の表示
            Console.WriteLine("名前:{0} 年齢:{1}", p.Name, p.Age);
        }
    }
}
実行結果
名前:山田太郎 年齢:32

自動実装プロパティの書式

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

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

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

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

set の前に private を付けることで、クラス内からはこのプロパティへの書き込みが可能ですが、クラスの外部からは読み込みのみとなります。これにより、クラス外からは読み取り専用のプロパティを定義できます。

サンプルの解説

このサンプルでは NameAge という2種類の自動実装プロパティが定義されています。Nameprivate set となっているため、クラス外から書き込むことができません。そのため、Program.cs の19行目のコメントを外すとエラーになります。

Ageset; get; の両方が定義されているため、クラス外からも読み書き可能です。17行目で p.Age = 32 として年齢を変更した後に表示しているため、実行結果の年齢は 32 となっています。

また、Person2 クラスには対応するフィールドがないため、クラス内のメソッドでは NameAge として直接プロパティにアクセスしています(14・15・20行目)。

練習問題 7 へ →