半人前プログラマーの技術談議

開発したもの紹介していくブログ

C# オブジェクト指向編 アクセス修飾子 part2-3

引き続きオブジェクト指向について解説していきます!

 

アクセス修飾子

 

スコープ

アクセス修飾子を説明する前に,先にスコープ(変数の有効範囲)について整理して置きます.

次のプログラムを例にしてみましょう.

 

ソースコード

using System;

namespace Training
{
    class Program
    {
        static int n = 10;

        static void Main(string[] args)
        {
            string sMain = "クッキー";

            Console.WriteLine(n);
            Console.WriteLine(sMain);
            Console.WriteLine(sDisplay); // エラー

            Console.WriteLine("--------------");

            Display();
        }

        static void Display()
        {
            string sDisplay = "チョコ";

            Console.WriteLine(n);
            Console.WriteLine(sMain); // エラー
            Console.WriteLine(sDisplay);
        }
    }
}

 

このプログラムはビルド時にエラーを吐くので,実行結果はありません.

 

有効範囲を図にすると次のようになります.

f:id:pokoshirou:20181012174808p:plain

 

メソッド内で宣言した変数は,メソッド内でしか使う事はできません.

また,メソッドの処理が終わると,メソッド内で宣言した変数の情報は無くなります.

 

では,エラーになる箇所の命令文をコメントアウト(または削除)して実行してみましょう.

 

実行結果

f:id:pokoshirou:20181012175852p:plain

 

 

 

アクセス修飾子

いよいよアクセス修飾子です.

今まで散々放置してきたpublicとかですね.

アクセシビリティと呼んだりもします.

(他にもアクセスレベルやアクセス指定子という呼び方もあります.) 

 

※ここからの内容はJavaなどの他の言語と若干仕様が異なります.

 

アクセス修飾子の種類と対応を表にします.

public どこからでもアクセスできる.
protected internal 同一アセンブリ内(exe,dllなどのプログラム単位またはライブラリ単位)と子クラス(サブクラス)からアクセスできる.
internal 同一アセンブリ内のみアクセスできる.
protected 自クラスと子クラスからのみアクセスできる.
private 自クラスのみからアクセスできる.
private protected 同一アセンブリ内の,自クラスと子クラスからのみアクセスできる.

 

今すぐ全部覚える必要はありません.

この中で特に重要なのがpublicprivateです.

今回はこの2つだけ解説します.

 

アクセス修飾子を付ける事ができるのは,

(クラス)

フィールド

メソッド

この3つだけです.(クラスは少し特殊です)

 

メソッド内の変数には付けることができません

スコープの項で説明したように,有効範囲がメソッド内限定なので,そもそも付ける必要はありません.(この為に先に説明しました.)

 

次のプログラムを例にしてみます.

using System;

namespace Training
{
    class Program
    {
        static void Main(string[] args)
        {
            Test test = new Test();

            int n = test.isN(); // public

            Console.WriteLine(n);
        }
    }

    public class Test
    {
        private int N;

        private void Substitution()
        {
            N = 100;    // private
        }

        public int isN()
        {
            Substitution(); // private

            return N; // private
        }
    }
}

 

実行結果

f:id:pokoshirou:20181012204413p:plain

 

ProgramクラスからTestクラス,フィールド,メソッドを呼び出す(アクセスする)には対象のクラス,フィールド,メソッドがpublicである必要があります.

 

Testクラスにpublicが付いているので,Programクラスからtestインスタンスを作成する事ができます.

 

TestクラスのisNメソッドはpublicなので,Programクラスからアクセスできています.

また,TestクラスのSubstitutionメソッドでは,変数Nはprivateで宣言されていますが,自クラス内である為,アクセスする事ができます.

isNメソッドも同様です.

 

最後にTestクラスにpublicが付いていますが,省略することもできます.

クラスのアクセス修飾子は省略した場合,publicと同じ扱いになります.

何も付けない状態デフォルト(default)と言います.

 

 

privateを使う理由

他のクラスからフィールドやメソッドにアクセスして,予定していない処理の順番でプログラムが実行されたりすると,不具合の原因になってしまいます.

 

また,アクセスを制限しているフィールドの変更を,自クラス内のメソッドのみで変更ができるなどといった制限を掛ける事により,誤作動なども防ぐ事ができます.

 

他にもまだまだ事例はありますが,大体このような理由でprivateを使っています.

 

さらに,privateを使う事でインテリセンス(IntelliSense)から,アクセスを制限したフィールド,メソッドが表示されなくなるので,入出力の部分のみわかるように設計できるというメリットもあります.

f:id:pokoshirou:20181012205645p:plain

 

また,アクセスを制限して,情報を隠す事をカプセル化情報隠蔽)と言います.

 

 

以上を踏まえて,前回のソースコードを確認してみよう.

 

ソースコード

using System;

namespace Training
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] name = { "自動車", "タクシー", "バス", "消防車" };

            Car[] car = new Car[4];      // Car型の配列を宣言する

            for (int i = 0; i < 4; i++)
            {
                car[i] = new Car(i + 1, name[i]);
                car[i].Display();
                Car.Counter();
            }
        }
    }

    public class Car
    {
        public static int count = 0;    // クラス変数

        public int number;      // 番号
        public string name;     // 名前

        public Car (int number, string name)
        {
            this.number = number;
            this.name = name;
            Car.count++;    // コンストラクタが呼び出された回数
        }

        public void Display()   // インスタンスメソッド
        {
            Console.WriteLine("ID:" + this.number + " 名前:" + this.name);
        }

        public static void Counter()    // クラスメソッド
        {
            Console.WriteLine("コンストラクタが呼び出された回数:" + Car.count);
        }
    }
}

 

では,このソースコードからprivateにできる箇所を探してみましょう.

 

ソースコード

using System;

namespace Training
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] name = { "自動車", "タクシー", "バス", "消防車" };

            Car[] car = new Car[4];      // Car型の配列を宣言する

            for (int i = 0; i < 4; i++)
            {
                car[i] = new Car(i + 1, name[i]);
                car[i].Display();
                Car.Counter();
            }
        }
    }

    public class Car
    {
        private static int count = 0;    // クラス変数

        private int number;      // 番号
        private string name;     // 名前

        public Car (int number, string name)
        {
            this.number = number;
            this.name = name;
            Car.count++;    // コンストラクタが呼び出された回数
        }

        public void Display()   // インスタンスメソッド
        {
            Console.WriteLine("ID:" + this.number + " 名前:" + this.name);
        }

        public static void Counter()    // クラスメソッド
        {
            Console.WriteLine("コンストラクタが呼び出された回数:" + Car.count);
        }
    }
}

 

フィールドが全てprivateになります.

フィールドの変更は全てメソッドが処理しているので,他クラスからアクセスされる必要はありません.

 

 

今回の内容でクラスに関しては一括りになります.

次回は参照型と補足をしようと思ってます.(継承はその次ぐらいになるかな