継承とカプセル化

目次

継承とカプセル化

継承

理解編

継承

継承 とは、あるクラスを土台として新しいクラスを作ることである。新しいクラスは継承元のメンバーを引き継ぐことができる。また、新しいメンバーを追加することもできる。 継承元のクラスを基本クラス 、新しいクラスを 派生クラス と呼ぶ。1つの基本クラスから派生クラスを作ることとを単一継承 と呼ぶ。一方、複数の基本クラスから派生クラスを作ることを 多重継承と呼ぶが、C# では使用できない。 Javaとかなら使用できるのだろうか?

継承は派生クラスの後ろに:を記述し、その後ろに継承元の基本クラスを指定する。

// 基本クラスの定義
class 基本クラス  { 処理内容 }
// 派生クラスの定義
class 派生クラス : 基本クラス { 処理内容 }

継承クラスのコンストラク

基本クラスと派生クラスにコンストラクタが存在する場合、基本クラスのコンストラクタ、派生クラスのコンストラクタの順番で呼び出される。

引数なしのコンストラクタはインスタンス生成時に自動的に呼び出される。一方、引数ありのコンストラクタでは、コンストラクタ初期化子と呼ばれるbaseキーワードを使用し、基本クラスに渡す引数を指定する必要がある。

隠蔽

隠蔽とは、派生クラスのメンバーを基本クラスのメンバーと同じ名前に定義することで、基本クラスのメンバーを強制的に上書きすることである。隠蔽する場合は、メンバー宣言の前にnewをつければよい。

基本クラスを参照していることを示すbaseを使用することで、派生クラスでメンバーを隠蔽しつつ基本クラスのメンバーにアクセスできる。 そうなると隠蔽するメリットは何なのだろうか?

隠蔽に似た機能で オーバーライド というものがある。オーバーライドは基本クラスのメソッドを派生クラスで書き換えるものであるが、隠蔽とは違うらしい。

実践編

継承

    // 基本クラスの生成
    class Music
    {
        public int Type = 3;
        public string Name = "Music";

        public void BaseInfo()
        {
            Console.WriteLine("BaseInfo is OK");
        }
    }

    // 派生クラスの生成
    class Song : Music
    {
        public string Key = "Song";

        // 新しいメソッドの追加
        public void DrvInfo()
        {
            Console.WriteLine("DrvInfo is OK");
        }
    }

    class MainClass
    {
        static void Main(string[] args)
        {
            // 派生クラスのインタスタン生成
            Song test = new Song();
            // 基本クラスのインタスタン生成
            Music baseInst = new Music();

            Console.WriteLine("----Access to derived class----");
            // 派生クラスのメンバーアクセス
            Console.WriteLine("Key is " + test.Key);
            Console.WriteLine("Type is " + test.Type);
            test.DrvInfo();
            test.BaseInfo();

            Console.WriteLine("----Access to base class----");
            // 基本クラスのメンバーアクセス
            Console.WriteLine("Type is " + baseInst.Type);
            baseInst.BaseInfo();

            Console.ReadKey();
        }
    }

結果を次に示す。 派生クラスでは、基本クラスのメンバーに加えて派生クラスにて追加したメンバーも使用できることが分かる。

----Access to derived class----
Key is Song
Type is 3
DrvInfo is OK
BaseInfo is OK
----Access to base class----
Type is 3
BaseInfo is OK

継承クラスのコンストラク

引数なしのコンストラク
    class Music
    {
        public Music()
        {
            Console.WriteLine("Base class Constracter");
        }
    }

    class Pop : Music
    {
        public Pop()
        {
            Console.WriteLine("Derived class Constracter");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Pop instPop = new Pop();
        }
    }

結果を次に示す。

Base class Constracter
Derived class Constracter
引数ありのコンストラク
    class Music
    {
        public Music(string str)
        {
            Console.WriteLine(str);
        }
    }
    class Pop : Music
    {
        public Pop(string str1, string str2) : base(str1)
        {
            Console.WriteLine(str2);
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            Pop instPop = new Pop("Music", "Pop");
        }
    }

結果を次に示す。

Music
Pop

隠蔽

隠蔽のコードを次に示す。

    // 基本クラスの定義
    class Music
    {
        public int value = 5;
    }

    // 派生クラスの定義
    class Pop : Music
    {
        // valueを隠蔽
        new public int value = 10;

        public void ViewValue()
        {
            // 基本クラスにアクセス
            Console.WriteLine(base.value);
            // 自分のメンバーにアクセス
            Console.WriteLine(value);
        }

    }
    class Program
    {
        static void Main(string[] args)
        {

            Pop instPop = new Pop();
            instPop.ViewValue();
        }
    }

結果を次に示す。

5
10

カプセル化

理解編

カプセル化

カプセル化とは、クラスへの外部アクセスを制限し、安全に使いやすくするためのものである。

  • アクセス制限はアクセス修飾子を使用する。
  • アクセス修飾子によりアクセス制限のレベルが設定できる。
アクセス修飾子 レベル
private クラスの内部のみアクセス可能
public アクセス制限なし
protected クラスの内部のみアクセス可能、または派生クラスに制限される

フィールドのアクセス制限

クラス内のフィールド値を安全に変更するには、外部から直接アクセスしない方がよい。その方法として、アクセサーメソッド を使用する方法と プロパティ を使用する方法がある。

とは書いたが、なぜフィールド値を直接書き換えしてはいけないのか理解できていない。リード/ライドの方向指定ができること、値のエラーチェックがことがメリットなのだろうか。

アクセサーメソッド

アクセサーメソッドとはフィールド値の変更や取得を行うためだけのメソッドのことである。メソッド名がSetxxGetxxとなっているものが、アクセサーメソッドに該当するらしい。Setxxメソッドで入力値のエラーチェックが行えるので、有効な値を他のメソッドに渡すことができる。

ただし、フィールド変数の数だけアクセサーメソッドの数が増えることから、可読性が悪くなるというデメリットがあるらしい。そこで C# では、デメリットを解消する方法として、プロパティ と呼ばれる機能がある。

プロパティ

プロパティは『クラスの内部ではメソッドのように振る舞うが、クラス外からはフィールドのようにアクセスできる』ものである。プロパティは、setset を使用し、次のように定義すること使用できる。

アクセス修飾子 データ型 プロパティ
{
    set
    {
        // 値代入時の処理を記述
        フィールド名 = value;
        // 代入値の妥当性チェックの記述も可能
    }
    get
    {
        // 値参照時の処理を記述
        // 返却値の妥当性チェックの記述も可能
        return フィールド名;
    }
}

set メソッドは値を設定するものなので戻り値を指定できない。また、これらメソッドは値の妥当性チェックや演算も行うことができる。 確かに使ってみると変数と同じ扱いで済むので便利である!

自動プロパティ

値の設定・取得をするだけならプロパティの中身を省略して記述できる。このプロパティを 自動プロパティ と呼ぶ。自動プロパティに初期値を与えることもできる。初期値が不要な場合は、次のうち= 初期値を削除すればよい。

データ型 プロパティ名 {get; set;} = 初期値;

実践編

カプセル化

    class Music
    {

        private int type = 0;

        // protected :クラス内部と派生クラスの内部からのみアクセス可能
        protected string name = "Music";

        public void SetType(int setType)
        {
            // "this"により、Musicクラスのメンバーを指定する。
            // "this"は、他メンバーにパラメータを渡す場合に用いる。
            // 同じクラス内なので、Typeにアクセス可能
            this.type = setType;
        }

        // フィールドの値を出力するメソッド
        public void Printname()
        {
            Console.WriteLine(name);
            Console.WriteLine(type);
        }
    }

    class Pop : Music
    {
        new public void Printname()
        {
            // nameのアクセス修飾子はprivate、派生クラスなのでアクセスできない
            // base.type = 100;

            // nameのアクセス修飾子はprotected、派生クラスなのでアクセス可能
            this.name = "Pop";
            // "base"により、基本クラスのメソッドを呼び出している。
            // Printnameのアクセス修飾しはpublic なのでアクセス可能
            base.Printname();
        }
    }

    class MainClass
    {
        static void Main(string[] args)
        {
            Pop pop = new Pop();
            pop.SetType(10);

            // protected にアクセスしようとするとエラーになる
            // アクセスできない保護レベルとなっている
            // pop.name = "test";

            pop.Printname();

            Console.ReadKey();
        }
    }

次に結果を示す。

Pop
10

フィールドのアクセス制限

アクセサーメソッド

アクセサーメソッドを用いたコードを次に示す。

    class Bmi
    {
        // --フィールド値
        // アクセス修飾子をprivateにして、クラス外からのアクセスを禁止する
        private double height = 0;
        private double weight = 0;

        // -- heightを取得するためのアクセサーメソッド
        public double GetHeight()
        {
            return this.height;
        }

        // -- weightを取得するためのアクセサーメソッド
        public double GetWeight()
        {
            return this.weight;
        }

        // -- 取得したheightをセットするためのアクセサーメソッド
        public void SetHeight(double setHeight)
        {
            if (setHeight <= 0)
            {
                Console.WriteLine("Error : invalid value");
            }
            else
            {
                this.height = setHeight;
                Console.WriteLine($"set height is {height} cm");
            }

        }

        // -- 取得したweightをセットするためのアクセサーメソッド
        public void SetWeight(double setWeight)
        {
            if (setWeight <= 0)
            {
                Console.WriteLine("Error : invalid value");
            }
            else
            {
                this.weight = setWeight;
                Console.WriteLine($"set weight is {weight} kg");
            }
        }


        // -- BMIを計算するメソッド
        public double CalcBmi()
        {
            return GetWeight() / ((GetHeight() / 100) * (GetHeight() / 100));
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            Bmi bmiInst = new Bmi();


            Console.Write("enter height (cm) :");
            double height = double.Parse(Console.ReadLine());
            Console.Write("enter weight (kg) :");
            double weight = double.Parse(Console.ReadLine());


            // 下記のようにメンバーを直接アクセスすることができない。
            // bmiInst.weght = 64;

            // アクセサーメソッドを用いてメンバー値の変更を行う。
            bmiInst.SetHeight(height);
            bmiInst.SetWeight(weight);

            Console.WriteLine($"BMI is {bmiInst.CalcBmi()}");

        }
    }

heiht:174、weight:65 を入力したときの結果を次に示す。

enter height (cm) :174
enter weight (kg) :65
set height is 174 cm
set weight is 65 kg
BMI is 21.4691504822301
プロパティ
    class Bmi
    {
        // --フィールド値
        // アクセス修飾子をprivateにして、クラス外からのアクセスを禁止する
        private double height = 0;
        private double weight = 0;


        // -- Weightプロパティ
        public double Weight
        {
            set
            {
                if (value <= 0)
                {
                    Console.WriteLine("Error : invalid value");
                }
                else
                {
                    this.height = value;
                    Console.WriteLine($"set height is {height} cm");
                }

            }
            get
            {
                return this.height;
            }
        }

        public double Height
        {
            set
            {
                if (value <= 0)
                {
                    Console.WriteLine("Error : invalid value");
                }
                else
                {
                    this.weight = value;
                    Console.WriteLine($"set weight is {weight} kg");
                }
            }

            get
            {
                return this.weight;
            }
        }



        // -- BMIを計算するメソッド
        public double CalcBmi()
        {
            return Weight / ((Height / 100) * (Height / 100));
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            Bmi bmiInst = new Bmi();


            Console.Write("enter height (cm) :");
            double height = double.Parse(Console.ReadLine());
            Console.Write("enter weight (kg) :");
            double weight = double.Parse(Console.ReadLine());


            // 下記のようにメンバーを直接アクセスすることができない。
            // bmiInst.weght = 64;

            // アクセサーメソッドを用いてメンバー値の変更を行う。
            bmiInst.Height = height;
            bmiInst.Weight = weight;

            Console.WriteLine($"BMI is {bmiInst.CalcBmi()}");

        }
    }

heiht:174、weight:65 を入力したときの結果を次に示す。 結果を次に示す。

enter height (cm) :174
enter weight (kg) :65
set weight is 174 kg
set height is 65 cm
BMI is 21.4691504822301
自動プロパティ

自動プロパティのコードを次に示す。

    class CallProperty
    {
        // 自動プロパティ
        public int Type { set; get; } = 10;
    }

    class Program
    {
        static void Main(string[] args)
        {
            CallProperty call = new CallProperty();

            Console.WriteLine(call.Type);

            call.Type = 100;

            Console.WriteLine(call.Type);

        }
    }

結果を次に示す。

10
100

参考

1.基礎からしっかり学ぶC#の教科書