web-dev-qa-db-ja.com

C#のスレッドセーフなシングルトンジェネリックテンプレートパターンとは何ですか

次のC#シングルトンパターンがありますが、それを改善する方法はありますか?

    public class Singleton<T> where T : class, new()
    {

        private static object _syncobj = new object();
        private static volatile T _instance = null;
        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncobj)
                    {
                        if (_instance == null)
                        {
                            _instance = new T();
                        }
                    }
                }
                return _instance;
            }
        }

        public Singleton()
        { }

    }

推奨される使用例:

class Foo : Singleton<Foo> 
{
} 

関連

。NETの明らかなシングルトン実装?

31
Sam Saffron

C#でのシングルトンパターンの実装 のJon Skeetによると、投稿したコードは、ECMA CLI標準と照合すると壊れているように見えるため、実際には不正なコードと見なされます。

また、注意してください。新しいタイプのTでオブジェクトをインスタンス化するたびに、それは別のインスタンスになります。元のシングルトンには反映されません。

18
Jon Limjap

このコードはコンパイルされません。Tに「クラス」制約が必要です。

また、このコードでは、ターゲットクラスにパブリックコンストラクターが必要です。これは、コンパイル時に、インスタンスプロパティ(またはフィールド)を介してのみ(単一の)インスタンスを取得するように制御できないため、シングルトンには適していません。インスタンス以外に静的メンバーがない場合は、次の方法で問題ありません。

class Foo
{
  public static readonly Instance = new Foo();
  private Foo() {}
  static Foo() {}
}

スレッドセーフ(CLRによって保証)でレイジー(インスタンスはタイプへの最初のアクセスで作成されます)です。 BeforeFieldInitの詳細と、ここで静的コンストラクターが必要な理由については、 https://csharpindepth.com/articles/BeforeFieldInit を参照してください。

タイプに他のパブリック静的メンバーが必要で、インスタンスへのアクセス時にのみオブジェクトを作成する場合は、 https://csharpindepth.com/articles/Singleton のようにネストされたタイプを作成できます。

9
Ilya Ryzhenkov

ジュディスビショップの礼儀、 http://patterns.cs.up.ac.za/

このシングルトンパターンの実装により、遅延初期化が保証されます。

//  Singleton PatternJudith Bishop Nov 2007
//  Generic version

public class Singleton<T> where T : class, new()
{
    Singleton() { }

    class SingletonCreator
    {
        static SingletonCreator() { }
        // Private object instantiated with private constructor
        internal static readonly T instance = new T();
    }

    public static T UniqueInstance
    {
        get { return SingletonCreator.instance; }
    }
}
9
Darksider

これが.NET4を使用する私のポイントです

public class Singleton<T> where T : class, new()
    {
        Singleton (){}

        private static readonly Lazy<T> instance = new Lazy<T>(()=> new T());

        public static T Instance { get { return instance.Value; } } 
    }
6
Alexandr

別のスレッドでのこの回答の詳細: C#でシングルトンを実装する方法?

ただし、スレッドはgenericを使用しません。

3
Binoj Antony

2行のコードを節約できるように、本当に「基本クラスを焼き付ける」ことは望んでいないと思います。シングルトンを実装するために基本クラスは実際には必要ありません。

シングルトンが必要なときはいつでも、これを実行してください。

class MyConcreteClass
{
  #region Singleton Implementation

    public static readonly Instance = new MyConcreteClass();

    private MyConcreteClass(){}

  #endregion

    /// ...
}
2
Wayne Bloss
public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();

   private Singleton(){}

   public static Singleton Instance
   {
      get 
      {
         return instance; 
      }
   }
}

初期化順序に関する.NETのあいまいさはありません ;しかし、これはスレッドの問題を引き起こします。

1
blowdart

:/ judith bishopの一般的な「シングルトン」パターンはちょっと欠陥があるようです。この「パターン」で使用するにはコンストラクターがパブリックである必要があるため、タイプTのインスタンスをいくつか作成することは常に可能です。私の意見では、シングルトンとはまったく関係がありません。これは、常に同じオブジェクトを返す一種のファクトリですが、シングルトンにはなりません...クラスのインスタンスが複数存在する場合に限ります。シングルトンになることはできません。このパターンが一流である理由は何ですか?

public sealed class Singleton
{
  private static readonly Singleton _instance = new Singleton();

  private Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      return _instance;
    }
  }
}

静的イニシャライザーはスレッドセーフと見なされます。わかりませんが、コードを3行以内でラップする場合は、シングルトンのイディオムを使用しないでください...シングルトンから継承しても、どちらの意味でも

1
haze4real

スレッドセーフで怠惰な方法(thxからwcell)でシングルトンデザインパターンを実装するこの汎用シングルトンクラスを試してください。

public abstract class Singleton<T> where T : class
{
    /// <summary>
    /// Returns the singleton instance.
    /// </summary>
    public static T Instance
    {
        get
        {
            return SingletonAllocator.instance;
        }
    }

    internal static class SingletonAllocator
    {
        internal static T instance;

        static SingletonAllocator()
        {
            CreateInstance(typeof(T));
        }

        public static T CreateInstance(Type type)
        {
            ConstructorInfo[] ctorsPublic = type.GetConstructors(
                BindingFlags.Instance | BindingFlags.Public);

            if (ctorsPublic.Length > 0)
                throw new Exception(
                    type.FullName + " has one or more public constructors so the property cannot be enforced.");

            ConstructorInfo ctorNonPublic = type.GetConstructor(
                BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[0], new ParameterModifier[0]);

            if (ctorNonPublic == null)
            {
                throw new Exception(
                    type.FullName + " doesn't have a private/protected constructor so the property cannot be enforced.");
            }

            try
            {
                return instance = (T)ctorNonPublic.Invoke(new object[0]);
            }
            catch (Exception e)
            {
                throw new Exception(
                    "The Singleton couldnt be constructed, check if " + type.FullName + " has a default constructor", e);
            }
        }
    }
}
1
w0land

私はより良いシングルトンパターンを探していて、これが好きでした。そのため、VB.NETに移植すると、他の人にも役立ちます。

Public MustInherit Class Singleton(Of T As {Class, New})
    Public Sub New()
    End Sub

    Private Class SingletonCreator
        Shared Sub New()
        End Sub
        Friend Shared ReadOnly Instance As New T
    End Class

    Public Shared ReadOnly Property Instance() As T
        Get
            Return SingletonCreator.Instance
        End Get
    End Property
End Class
1
dr. evil

Microsoftが提供するDouble-CheckLocking [Lea99]イディオム here は、提供されたコードと驚くほど似ていますが、残念ながら、これはスレッドセーフコードのピューリタンビューに関するECMA CLI標準に失敗し、正しく機能しない可能性があります。すべての状況で。

マルチスレッドプログラムでは、異なるスレッドがクラスのインスタンス化を同時に試みる可能性があります。このため、インスタンスがnullであるかどうかをチェックするためにifステートメントに依存するシングルトン実装はスレッドセーフではありません。そのようなコードを書かないでください!

スレッドセーフなシングルトンを作成する簡単で効果的な方法は、ネストされたクラスを使用してインスタンス化することです。以下は、レイジーインスタンス化シングルトンの例です。

public sealed class Singleton
{ 
   private Singleton() { }

   public static Singleton Instance
   {
      get
      {
         return SingletonCreator.instance;
      }
   }

   private class SingletonCreator
   {
      static SingletonCreator() { }
      internal static readonly Singleton instance = new Singleton();
   }
}

使用法:

Singleton s1 = Singleton.Instance;
Singleton s2 = Singleton.Instance;
if (s1.Equals(s2))
{
    Console.WriteLine("Thread-Safe Singleton objects are the same");
}

一般的な解決策:

public class Singleton<T>
    where T : class, new()
{
    private Singleton() { }

    public static T Instance 
    { 
        get 
        { 
            return SingletonCreator.instance; 
        } 
    } 

    private class SingletonCreator 
    {
        static SingletonCreator() { }

        internal static readonly T instance = new T();
    }
}

使用法:

class TestClass { }

Singleton s1 = Singleton<TestClass>.Instance;
Singleton s2 = Singleton<TestClass>.Instance;
if (s1.Equals(s2))
{
    Console.WriteLine("Thread-Safe Generic Singleton objects are the same");
}

最後に、やや関連性のある便利な提案を示します。lockキーワードを使用することで発生する可能性のあるデッドロックを回避するには、次の属性を追加して、パブリック静的メソッドでのみコードを保護することを検討してください。

using System.Runtime.CompilerServices;
[MethodImpl (MethodImplOptions.Synchronized)]
public static void MySynchronizedMethod()
{
}

参照:

  1. C#クックブック(オライリー)、ジェイ・ヒルヤード&スティーブン・テイル
  2. C#3.0デザインパターン(オライリー)、ジュディスビショップ
  3. CSharp-Online.Net -シングルトンデザインパターン:スレッドセーフなシングルトン
1
Rick

リクエストに応じて、元の回答から別の質問へのクロスポスト。

私のバージョンはReflectionを使用し、派生クラスの非公開コンストラクターで動作し、(明らかに)怠惰なインスタンス化でスレッドセーフです(以下にリンクされている記事によると):

public class SingletonBase<T> where T : class
{
    static SingletonBase()
    {
    }

    public static readonly T Instance = 
        typeof(T).InvokeMember(typeof(T).Name, 
                                BindingFlags.CreateInstance | 
                                BindingFlags.Instance |
                                BindingFlags.Public |
                                BindingFlags.NonPublic, 
                                null, null, null) as T;
}

私は数年前にこれを拾いました、私のものがいくらかはわかりませんが、コードをグーグルで検索すると、それが私でなければ、テクニックの元のソースが見つかる可能性があります。

これは 私が見つけることができるコードの最も古いソース それは私がそれを投稿していませんでした。

1
Cade Roux

これをグループに提出します。スレッドセーフで汎用的であり、パターンに従います。あなたはそれから継承することができます。これは他の人が言ったことから一緒に石畳になっています。

public class Singleton<T> where T : class
{
    class SingletonCreator
    {
        static SingletonCreator() { }

        internal static readonly T Instance =
            typeof(T).InvokeMember(typeof(T).Name,
                                    BindingFlags.CreateInstance |
                                    BindingFlags.Instance |
                                    BindingFlags.Public |
                                    BindingFlags.NonPublic,
                                    null, null, null) as T;
    }

    public static T Instance
    {
        get { return SingletonCreator.Instance; }
    }
}

意図された実装:

public class Foo: Singleton<Foo>
{
     private Foo() { }
}

次に:

Foo.Instance.SomeMethod();
0
010110110101

私はあなたの元の答えがとても気に入りました-(blowdartによって投稿されたリンクによると)欠けているのは、_instance変数を揮発性にして、実際にロックに設定されていることを確認することだけです。シングルトンを使用する必要がある場合、実際にはブローダーツソリューションを使用しますが、インスタンス化を遅らせる必要はありません。

0
Wyzfen

どの例を選択しても、Parallel.For!を使用して並行性を常に確認してください。 (反復が並行して実行されるループ)

シングルトンC'torに入れる:

 private Singleton ()
        {
            Console.WriteLine("usage of the Singleton for the first time");
        }

メインに入れる:

Parallel.For(0, 10,
              index => {
                  Thread tt = new Thread(new ThreadStart(Singleton.Instance.SomePrintMethod));
                  tt.Start();
              });
0
Eran Peled

インスタンスデータのオンデマンド作成を確実にするための私の貢献:


/// <summary>Abstract base class for thread-safe singleton objects</summary>
/// <typeparam name="T">Instance type</typeparam>
public abstract class SingletonOnDemand<T> {
    private static object __SYNC = new object();
    private static volatile bool _IsInstanceCreated = false;
    private static T _Instance = default(T);

/// <summary>Instance data</summary> public static T Instance { get { if (!_IsInstanceCreated) lock (__SYNC) if (!_IsInstanceCreated) _Instance = Activator.CreateInstance<T>(); return _Instance; } } }
0
TeaR

ウィキペディア のように:

シングルトンパターンは、クラスのインスタンス化を1つのオブジェクトに制限するデザインパターンです。

ジェネリックスを使用してそれを行う保証された方法はありません。シングルトン自体のインスタンス化を制限している場合、メインクラスのインスタンス化を制限する方法は不可能だと思います。この単純なパターンを実装します。それほど難しいことではありません。静的コンストラクターとプライベートセットを使用して、次のようにします。

public class MyClass
{
        private MyClass()
        {

        }

        static MyClass()
        {
            Instance = new MyClass();
        }

        public static MyClass Instance { get; private set; }
}

または:

public class MyClass
    {
            private MyClass()
            {

            }

            static MyClass()
            {
                Instance = new MyClass();
            }

            private static MyClass instance;



            public static MyClass Instance
            {
                get
                {
                    return instance;
                }
                private set
                {
                    instance = value;
                }
            }
    }
0
Sawan

これは私のために働きます:

public static class Singleton<T> 
{
    private static readonly object Sync = new object();

    public static T GetSingleton(ref T singletonMember, Func<T> initializer)
    {
        if (singletonMember == null)
        {
            lock (Sync)
            {
                if (singletonMember == null)
                    singletonMember = initializer();
            }
        }
        return singletonMember;
    }
}

使用法:

private static MyType _current;
public static MyType Current = Singleton<MyType>.GetSingleton(ref _current, () => new MyType());

シングルトンを消費する:

MyType.Current. ...
0
MaurGi

pff ...再び... :)
インスタンスデータのオンデマンド作成を確実にするための私の貢献:


/// <summary>Abstract base class for thread-safe singleton objects</summary>
/// <typeparam name="T">Instance type</typeparam>
public abstract class SingletonOnDemand<T> {
  private static object __SYNC = new object();
  private static volatile bool _IsInstanceCreated = false;
  private static T _Instance = default(T);

  /// <summary>Instance data</summary>
  public static T Instance {
    get {
      if (!_IsInstanceCreated)
        lock (__SYNC)
          if (!_IsInstanceCreated) {
            _Instance = Activator.CreateInstance<T>();
            _IsInstanceCreated = true;
          }
          return _Instance;
    }
  }
}
0
TeaR
public static class LazyGlobal<T> where T : new()
{
    public static T Instance
    {
        get { return TType.Instance; }
    }

    private static class TType
    {
        public static readonly T Instance = new T();
    }
}

// user code:
{
    LazyGlobal<Foo>.Instance.Bar();
}

または:

public delegate T Func<T>();

public static class CustomGlobalActivator<T>
{
    public static Func<T> CreateInstance { get; set; }
}

public static class LazyGlobal<T>
{
    public static T Instance
    {
        get { return TType.Instance; }
    }

    private static class TType
    {
        public static readonly T Instance = CustomGlobalActivator<T>.CreateInstance();
    }
}

{
    // setup code:
    // CustomGlobalActivator<Foo>.CreateInstance = () => new Foo(instanceOf_SL_or_IoC.DoSomeMagicReturning<FooDependencies>());
    CustomGlobalActivator<Foo>.CreateInstance = () => instanceOf_SL_or_IoC.PleaseResolve<Foo>();
    // ...
    // user code:
    LazyGlobal<Foo>.Instance.Bar();
}
0
uvw

リフレクションを使用してプライベート(またはパブリック)のデフォルトコンストラクターにアクセスするのを少し前に見ました。

public static class Singleton<T>
{
    private static object lockVar = new object();
    private static bool made;
    private static T _singleton = default(T);

    /// <summary>
    /// Get The Singleton
    /// </summary>
    public static T Get
    {
        get
        {
            if (!made)
            {
                lock (lockVar)
                {
                    if (!made)
                    {
                        ConstructorInfo cInfo = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null);
                        if (cInfo != null)
                            _singleton = (T)cInfo.Invoke(new object[0]);
                        else
                            throw new ArgumentException("Type Does Not Have A Default Constructor.");
                        made = true;
                    }
                }
            }

            return _singleton;
        }
    }
}
0
JDunkerley