次の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>
{
}
関連:
C#でのシングルトンパターンの実装 のJon Skeetによると、投稿したコードは、ECMA CLI標準と照合すると壊れているように見えるため、実際には不正なコードと見なされます。
また、注意してください。新しいタイプのTでオブジェクトをインスタンス化するたびに、それは別のインスタンスになります。元のシングルトンには反映されません。
このコードはコンパイルされません。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 のようにネストされたタイプを作成できます。
ジュディスビショップの礼儀、 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; }
}
}
これが.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; } }
}
別のスレッドでのこの回答の詳細: C#でシングルトンを実装する方法?
ただし、スレッドはgenericを使用しません。
2行のコードを節約できるように、本当に「基本クラスを焼き付ける」ことは望んでいないと思います。シングルトンを実装するために基本クラスは実際には必要ありません。
シングルトンが必要なときはいつでも、これを実行してください。
class MyConcreteClass
{
#region Singleton Implementation
public static readonly Instance = new MyConcreteClass();
private MyConcreteClass(){}
#endregion
/// ...
}
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private Singleton(){}
public static Singleton Instance
{
get
{
return instance;
}
}
}
初期化順序に関する.NETのあいまいさはありません ;しかし、これはスレッドの問題を引き起こします。
:/ judith bishopの一般的な「シングルトン」パターンはちょっと欠陥があるようです。この「パターン」で使用するにはコンストラクターがパブリックである必要があるため、タイプTのインスタンスをいくつか作成することは常に可能です。私の意見では、シングルトンとはまったく関係がありません。これは、常に同じオブジェクトを返す一種のファクトリですが、シングルトンにはなりません...クラスのインスタンスが複数存在する場合に限ります。シングルトンになることはできません。このパターンが一流である理由は何ですか?
public sealed class Singleton
{
private static readonly Singleton _instance = new Singleton();
private Singleton()
{
}
public static Singleton Instance
{
get
{
return _instance;
}
}
}
静的イニシャライザーはスレッドセーフと見なされます。わかりませんが、コードを3行以内でラップする場合は、シングルトンのイディオムを使用しないでください...シングルトンから継承しても、どちらの意味でも
スレッドセーフで怠惰な方法(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);
}
}
}
}
私はより良いシングルトンパターンを探していて、これが好きでした。そのため、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
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()
{
}
参照:
リクエストに応じて、元の回答から別の質問へのクロスポスト。
私のバージョンは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;
}
私は数年前にこれを拾いました、私のものがいくらかはわかりませんが、コードをグーグルで検索すると、それが私でなければ、テクニックの元のソースが見つかる可能性があります。
これは 私が見つけることができるコードの最も古いソース それは私がそれを投稿していませんでした。
これをグループに提出します。スレッドセーフで汎用的であり、パターンに従います。あなたはそれから継承することができます。これは他の人が言ったことから一緒に石畳になっています。
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();
私はあなたの元の答えがとても気に入りました-(blowdartによって投稿されたリンクによると)欠けているのは、_instance変数を揮発性にして、実際にロックに設定されていることを確認することだけです。シングルトンを使用する必要がある場合、実際にはブローダーツソリューションを使用しますが、インスタンス化を遅らせる必要はありません。
どの例を選択しても、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();
});
インスタンスデータのオンデマンド作成を確実にするための私の貢献:
/// <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;
}
}
}
ウィキペディア のように:
シングルトンパターンは、クラスのインスタンス化を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;
}
}
}
これは私のために働きます:
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. ...
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;
}
}
}
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();
}
リフレクションを使用してプライベート(またはパブリック)のデフォルトコンストラクターにアクセスするのを少し前に見ました。
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;
}
}
}