web-dev-qa-db-ja.com

パラメータ付きシングルトン

いくつかの引数でインスタンス化されるシングルトンクラスが必要です。私が今やっている方法は:

class SingletonExample
{
     private SingletonExample mInstance;
     //other members... 
     private SingletonExample()
     {

     } 
     public SingletonExample Instance
     {
         get
         {
              if (mInstance == null)
              {
                  throw new Exception("Object not created");
              }
              return mInstance;
         }
     }

     public void Create(string arg1, string arg2)
     {
         mInstance = new SingletonExample();
         mInstance.Arg1 = arg1;
         mInstance.ObjectCaller = new ObjectCaller(arg2);
         //etc... basically, create object...
     } 
}

インスタンスは「後期」に作成されます。つまり、アプリの起動時に必要な引数がすべて揃っていません。

一般的に、メソッド呼び出しの順序付けを強制するのは好きではありませんが、ここでは別の方法は見ていません。 IoCも解決しません。コンテナに登録できる場所で、Create()を呼び出すこともできるためです...

これは良いシナリオだと思いますか?他にアイデアはありますか?

編集:I知っている例として書いたものはスレッドセーフではない、スレッドセーフは問題の一部ではない

32
veljkoz

パラメータを持つシングルトンは、私にとって怪しい匂いがします。

Whatevaの答えと次のコードを検討してください。

Singleton x = Singleton.getInstance("hello", "world");
Singleton y = Singleton.getInstance("foo", "bar");

明らかに、x == yおよびyはxの作成パラメーターで機能しますが、yの作成パラメーターは単に無視されます。結果はおそらく...少なくとも混乱させるでしょう。

あなたが本当にあなたがそれをしなければならないように本当に落ちたなら、次のようにしてください:

class SingletonExample
{
     private static SingletonExample mInstance;
     //other members... 
     private SingletonExample()
     {  // never used
        throw new Exception("WTF, who called this constructor?!?");
     }
     private SingletonExample(string arg1, string arg2)
     {
         mInstance.Arg1 = arg1;
         mInstance.ObjectCaller = new ObjectCaller(arg2);
         //etc... basically, create object...    
     } 
     public static SingletonExample Instance
     {
         get
         {
              if (mInstance == null)
              {
                  throw new Exception("Object not created");
              }
              return mInstance;
         }
     }

     public static void Create(string arg1, string arg2)
     {
         if (mInstance != null)
         {
             throw new Exception("Object already created");
         }
         mInstance = new SingletonExample(arg1, arg2);             
     } 
}

マルチスレッド環境では、競合状態を回避するために同期を追加します。

22

シングルトンはいですが、ユーザーwhatevaが自分のコードを修正することはできません...

public class Singleton 
{ 
    private static Singleton _instance = null; 

    private static Object _mutex = new Object();

    private Singleton(object arg1, object arg2) 
    { 
        // whatever
    } 

    public static Singleton GetInstance(object arg1, object arg2)
    { 
        if (_instance == null) 
        { 
          lock (_mutex) // now I can claim some form of thread safety...
          {
              if (_instance == null) 
              { 
                  _instance = new Singleton(arg1, arg2);
              }
          } 
        }

        return _instance;
    }
}  

Skeetはこの数年前にブログを書いたが、かなり信頼できると思う。例外は必要ありません。どのオブジェクトがシングルトンであると想定されているかを覚えておらず、間違った場合にフォールアウトを処理することはできません。

編集:型はあなたが望むものを適切に使用していません。objectはここでは便宜上使用されています。

33
annakata

より良い答え:

  1. インターフェースを作成します:ISingleton(実行したいアクションをすべて含みます)

  2. そしてあなたのタイプ:_Singleton : ISingleton_

  3. UnityContainerにアクセスできると仮定します:

IUnityContainer _singletonContainer = new UnityContainer(); // or whatever code to initialize the container

  1. タイプの使用を作成する準備ができたら(Unity for DIを使用している場合):

_singletonContainer.RegisterType(typeof(ISingleton), new Singleton(params));

  1. シングルトンを取得する場合は、次を使用します。

var localSingletonVar = _singletonContainer.Resolve<ISingleton>();

注:コンテナにISingletonインターフェイス用に登録された型がない場合、例外をスローするか、nullを返す必要があります。

古い答え:

_public class Singleton
{

    private static Singleton instance = null;

    private Singleton(String arg1, String arg2)
    {
    }

    public static Singleton getInstance(String arg1, String arg2)
    {
        if (instance != null)
        {
            throw new InvalidOperationException("Singleton already created - use getinstance()");
        }
        instance = new Singleton(arg1, arg2);
        return instance;
    }

    public static Singleton getInstance()
    {
        if (instance == null)
            throw new InvalidOperationException("Singleton not created - use GetInstance(arg1, arg2)");
        return instance;
    }
}
_

同様のことを行います(インスタンスも作成されたかどうかを確認する必要があります)、または、DIコンテナーが登録されていない型で例外をスローすることをサポートしている場合は、それを使用します。

ATTN:スレッドセーフでないコード:)

6
Bogdan Maxim

アンナカタが提供する二重ロックシングルトンソリューションは、すべてのプラットフォームで常に機能するとは限りません。このアプローチには欠陥があり、十分に文書化されています。このアプローチを使用しないでください。使用すると、問題が発生します。

この問題を解決する唯一の方法は、volatileキーワードを使用することです。

private static volatile Singleton m_instance = null;

これが唯一のスレッドセーフなアプローチです。

2
Johnny
/// <summary> Generic singleton with double check pattern and with instance parameter </summary>
/// <typeparam name="T"></typeparam>
public class SingleObject<T> where T : class, new()
{
    /// <summary> Lock object </summary>
    private static readonly object _lockingObject = new object();

    /// <summary> Instance </summary>
    private static T _singleObject;

    /// <summary> Protected ctor </summary>
    protected SingleObject()
    {
    }

    /// <summary> Instance with parameter </summary>
    /// <param name="param">Parameters</param>
    /// <returns>Instance</returns>
    public static T Instance(params dynamic[] param)
    {
        if (_singleObject == null)
        {
            lock (_lockingObject)
            {
                if (_singleObject == null)
                {
                    _singleObject = (T)Activator.CreateInstance(typeof(T), param);
                }
            }
        }
        return _singleObject;
    }
}
0
Brick

私は実際にあなたのコードにシングルトンを見ることができません。静的なパラメータ化されたgetInstanceメソッドを使用します。このメソッドは、シングルトンを返し、以前に使用されていない場合は作成します。

0
DaVinci

.NET 4(またはそれ以上)を使用している場合は、System.Lazyタイプを使用できます。スレッドの安全性の問題に気を配り、それを怠doにして、不必要にインスタンスを作成しないようにします。このように、コードは短くてきれいです。

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton(),LazyThreadSafetyMode.ExecutionAndPublication);

    private Singleton()  {  }

    public static Singleton Instance { get { return lazy.Value; } }
}
0
yohait