C# で同一構造のシングルトンについて考える

パラメータなど同一構造で複数定義したいものって割とよくあると思います。

各パラメータ定義はプログラム動作中に変化しないので static class としたいところですが、interfaceや継承が使えないので取り回しがイマイチです。

そこでベース定義クラスを作成してパラメータ定義本体をシングルトンとしてしまえば取り扱いやすくなると考えました。

```ParameterDefine.cs
namespace test
{
    public abstract class ParameterDefine
    {
        public int Min { get; init; }
        public int Max { get; init; }
        public int DefaultValue { get; init; }

        public ParameterDefine(int min, int max, int defaultValue)
        {
            Min = min;
            Max = max;
            DefaultValue = defaultValue;
        }
    }

    public class Parameter1 : ParameterDefine
    {
        private Parameter1():base(0 ,10, 0)
        {
        }
 
        private static Parameter1 _instance;

        public static Parameter1 GetInstance()
        {
            if(_instance is null)
            {
                _instance = new();
            }
            return _instance;
        }
    }

    public class Parameter2 : ParameterDefine
    {
        private Parameter2():base(0 ,20, 0)
        {
        }
 
        private static Parameter2 _instance;

        public static Parameter2 GetInstance()
        {
            if(_instance is null)
            {
                _instance = new();
            }
            return _instance;
        }
    }
}
```

しかしこれだとパラメータ定義を追加する度にシングルトンに関する記述を行わなければならず、全く同じコードがたくさん現れるという点で精神衛生上よくありません。

ということで最終的に下記に落ち着きました。

```ParameterDefine.cs
using System;

namespace test_lib
{
    public abstract class ParameterDefine
    {
        public int Min { get; init; }
        public int Max { get; init; }
        public int DefaultValue { get; init; }

        private protected ParameterDefine(int min, int max, int defaultValue)
        {
            Min = min;
            Max = max;
            DefaultValue = defaultValue;
        }
    }


    public abstract class ParameterDefineSingleton<T> : ParameterDefine
        where T : class
    {
        internal protected ParameterDefineSingleton(int min, int max, int defaultValue)
                                            : base(min, max, defaultValue)
        { 
        }

        private static T _instance;

        public static T GetInstance()
        {
            if(_instance is null)
            {
                _instance = (T)Activator.CreateInstance(typeof(T)
                                 , System.Reflection.BindingFlags.Instance
                                   | System.Reflection.BindingFlags.CreateInstance
                                   | System.Reflection.BindingFlags.NonPublic
                                 , null
                                 , null
                                 , System.Globalization.CultureInfo.CurrentCulture);
            }
            return _instance;
        }
    }
}

```

```Parameter.cs
using test_lib;

namespace app_test
{
    public sealed class Parameter1 : ParameterDefineSingleton<Parameter1>
    {
        private Parameter1() : base(0, 10, 0)
        {
        }
    }

    public sealed class Parameter2 : ParameterDefineSingleton<Parameter2>
    {
        private Parameter2() : base(5, 10, 5)
        {
        }
    }
}

```

ポイントはシングルトンクラスにて Activator を使用して無理やりプライベートコンストラクタを呼び出しているところです。
これにより、末端のパラメータ定義の直接的な生成を不可とし GetInstatnce() でしかインスタンスの取得をできなくします。