C#praivateなコンストラクタ

C# コンピュータ
C#
引数なしのコンストラクタをpraivateで宣言すると、外部から引数なしでインスタンスを生成することは出来ません。
通常クラスのインスタンスは外部で生成(new)され利用するケースが多く、一見インスタンスが生成できないと使い道が無いようにも思えます。
使い道として、引数なしのコンストラクタを禁止し、引数有りのコンストラクタを強制することが出来ます。
using System;

namespace class1
{
    class BaseClass
    {
        string _path;
        private BaseClass()
        {
            Console.WriteLine("BaseClass()");
        }
        public BaseClass(string path)
        {
            _path = path;
            Console.WriteLine("BaseClass(string path)");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            // var obj = new BaseClass(); // NG

            var obj = new BaseClass(@".\dummy.txt"); // OK
        }
    }
}
また、外部からの呼び出しが禁止ですが、内部からならOKですので、staticなメソッドでインスタンスを生成することが出来ます。
インスタンスの生成をコントロールすることが出来るので、インスタンスを一つだけ作るシングルトンパターンに使われる事例を見かけます。
using System;

namespace class2
{
    class Foo
    {
        private static Foo _instance; // 唯一のインスタンス

        private Foo()
        {
            // 外部からのFooクラスのインスタンス生成を禁じる
        }

        public static Foo GetInstance()
        {
            if (_instance == null)
            {
                // 必要になったら1回だけインタンスを生成
                _instance = new Foo(); // 内部からの呼び出し
            }

            return _instance;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            // var foo = new Foo(); // NG

            var foo = Foo.GetInstance(); // OK
            var foo2 = Foo.GetInstance();

            Console.WriteLine("{0} == {1}", foo.GetHashCode(), foo2.GetHashCode());
        }
    }
}
インスタンスの生成機能を隠蔽するpraivateなコンストラクタですが、都合が悪いケースもあります。
クラスを継承する場合コンストラクタは、オーバーライドではなく親クラスのコンストラクタ→派生先のコンストラクタの順番で呼び出されます。privateなコンストラクタを持つクラスを継承すると派生先から見て外部に当たる親クラスのコンストラクタを呼び出すことが出来ないためインスタンスを生成できなくなります。
using System;

namespace class3
{
    class BaseClass
    {
        // private BaseClass(){ Console.WriteLine("private BaseClass()"); } // NG

        protected BaseClass(){ Console.WriteLine("protected BaseClass()"); } // OK
        public BaseClass(string str) { Console.WriteLine("public BaseClass({0})", str); } // OK
    }
    class SubClass : BaseClass
    {
        // private SubClass(string str) { Console.WriteLine("private SubClass({0})", str); } // NG
        public SubClass(string str) { Console.WriteLine("public SubClass({0})", str); } // OK

        public SubClass(string str, int i) : base(str)
        {
            Console.WriteLine("public SubClass(string str={0}, int i={1})", str, i);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var a = new BaseClass("A");
            // 結果
            // public BaseClass(A)

            var b = new SubClass("B");
            // 結果
            // protected BaseClass()
            // public SubClass(B)

            var c = new SubClass("C", 5);
            // 結果
            // public BaseClass(C)
            // public SubClass(string str=C, int i=5)
        }
    }
}
この振る舞いを利用した使い道もありそうですが、継承をする可能性がある場合、デフォルトとなる引数なしのコンストラクタはprivateではなく派生先でも呼び出し可能なprotectedにしておく方が無難そうす。

コメント