29

これが私のシングルトンクラスの書き方です。

public class MyClass
{
    /// <summary>
    /// Singleton
    /// </summary>
    private static MyClass instance;

    /// <summary>
    /// Singleton access.
    /// </summary>
    public static MyClass Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new MyClass();
            }
            return _instance;
        }
    }

    private MyClass() { .... }
}

再利用可能なシングルトン パターンを作成するには?

シングルトン パターンには、次の課題があります。

  • コンストラクタはprivateorprotectedです。
  • 基本クラスは、継承されたクラスをインスタンス化できません。したがって、共通の要約を再利用できますMyAbstractSingletonClass
  • インスタンスを取得するには、ローカルの読み取り専用プロパティが必要です。

問題

私は多くのクラスでこのパターンを使用しており、常に同じコードを書かなければなりません。シングルトンが必要なときに再利用されるものを作成するにはどうすればよいですか?

4

8 に答える 8

58

これは、自己参照ジェネリック型制約と「new() 」型制約の組み合わせを使用して実現できます。

「新しい」制約は、子クラスが常にパラメーターなしのコンストラクターを持つことを保証するため、_instance = new T();常に機能します。

自己参照型の制約により、「インスタンス」静的プロパティが常に正しい型を返すことが保証されます。「ベース」タイプではありません。シングルトン基本クラスは次のようになります。

public abstract class SingletonBase<T> 
    where T : SingletonBase<T>, new()
{
    private static T _instance = new T();
    public static T Instance
    {
        get
        {                
            return _instance;
        }   
    }
}

子クラスは次のようになります。

public class MyChildSingleton : SingletonBase<MyChildSingleton>
{
    //Done!
}

もちろん、シングルトンを汎用にしたい場合は、「シングルトン インスタンスの作成」コードを少し変更して、「ダブル チェック ロック」パターンまたはLazyクラスを使用して、スレッドセーフにする必要があります。

大きな注意点: このメソッドを使用する場合、"new()" 制約により、クラスが常に public のパラメーターなしのコンストラクターを持つことがほぼ保証されます。つまり、エンドユーザーはnew MyChildSingleton()、本当に必要な場合はいつでも呼び出すことができ、シングルトン インスタンスを完全にバイパスできます。シングルトンは、厳密に強制されるのではなく、「慣例による」ものになります。これを回避するには、もう少しエンジニアリングが必要です。上記のシナリオでは、" ."Defaultではなく静的インスタンスに " " という名前を付けるのが規則のようです。Instanceこれは、クラスが「推奨される」シングルトン インスタンスを提供するという事実を微妙に伝えますが、それを使用することは技術的にオプションです。

シングルトン パターンを厳密に適用する試みをいくつか行った結果、リフレクションを使用してプライベート コンストラクターを手動で呼び出すことになりました。ここで私の完全なコードの試みを見ることができます。

于 2013-05-25T02:06:10.887 に答える
5

BtownTKD の回答に加えて、実行時にコンストラクター呼び出しを制限するのは実際には非常に簡単です (コンパイルでは可能かどうかはわかりません)。_instance が null でない場合に例外をスローする保護されたコンストラクターを SingletonBase に追加するだけです。コンストラクターが外部から最初に呼び出される場合でも、例外がスローされます。

私はこのテクニックをシングルトンベースに適用し、ここで説明されているようにレイジーでスレッドセーフにすることもできました: http://csharpindepth.com/Articles/General/Singleton.aspx

結果(使い方の説明付き):

/// <summary>
/// Generic singleton class, providing the Instance property, and preventing manual construction.
/// Designed as a base for inheritance trees of lazy, thread-safe, singleton classes.
/// Usage:
/// 1. Sub-class must use itself, or its sub-class, as the type parameter S.
/// 2. Sub-class must have a public default constructor (or no constructors).
/// 3. Sub-class might be abstract, which requires it to be generic and demand the generic type
///    have a default constructor. Its sub-classes must answer all these requirements as well.
/// 4. The instance is accessed by the Instance getter. Using a constructor causes an exception.
/// 5. Accessing the Instance property in an inner initialization in a sub-class constructor
///    might cause an exception is some environments.
/// </summary>
/// <typeparam name="S">Lowest sub-class type.</typeparam>
public abstract class Singleton<S> where S : Singleton<S>, new()
{
    private static bool IsInstanceCreated = false;
    private static readonly Lazy<S> LazyInstance = new Lazy<S>(() =>
        {
            S instance = new S();
            IsInstanceCreated = true;
            return instance;
        });

    protected Singleton()
    {
        if (IsInstanceCreated)
        {
            throw new InvalidOperationException("Constructing a " + typeof(S).Name +
                " manually is not allowed, use the Instance property.");
        }
    }

    public static S Instance
    {
        get
        {
            return LazyInstance.Value;
        }
    }
}

私は集中的なマルチスレッド テストを行っていないと言わざるを得ませんが、既に述べたように、古いダブル チェック トリックをいつでも使用できます。

于 2016-04-21T16:07:49.640 に答える
4

基本クラスにシングルトン パターンを実装することは、疑わしい方法です。

ただし、達成しようとしていることに適した他の創造的な設計パターンを実装することはできます。たとえば、Abstract Factoryを見てください。

于 2013-05-25T02:02:52.717 に答える
2

あなたは正しいです - 現状では、これを達成することはできません。ただし、ジェネリックを使用してアプローチすることもできます。このアプローチでは、一意の派生型ごとに 1 つのシングルトン インスタンスを取得することに注意してください。

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var x = new MyDerivedClass();
            Console.WriteLine(x.ToString());
            Console.WriteLine(x.Instance.ToString());

            Console.ReadKey();
        }
    }


    public abstract class MyBaseClass<T> where T : class, new()
    {
        protected T GetInstance()
        {
            if (_instance == null)
            {
                lock (_lockObj)
                {
                    if (_instance == null)
                        _instance = new T();
                }
            }
            return _instance;
        }

        public T Instance
        {
            get { return GetInstance(); }
        }

        private volatile static T _instance;
        private object _lockObj = new object();
    }

    public class MyDerivedClass : MyBaseClass<MyDerivedClass>
    {
        public MyDerivedClass() { }
    }

}
于 2013-05-25T02:10:54.163 に答える
0

私は最近、関連する質問に対するこの回答を提案しました:

https://stackoverflow.com/a/20599467

このメソッドでは、すべての派生クラス コンストラクターが基本クラスのみが提供できるオブジェクトを必要とし、パラメーターのないコンストラクターの制限が必要ないため、基本クラスは派生クラスのすべてのインスタンスの作成を管理します。

于 2013-12-15T22:22:40.493 に答える