带参数的单身人士

时间:2010-11-17 10:54:09

标签: c# design-patterns parameters singleton arguments

我需要使用一些参数来实例化单例类。我现在这样做的方式是:

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...
     } 
}

实例是'迟到'创建的,这意味着我没有在app启动时获得所有必需的参数。

一般来说,我不喜欢强制方法调用的排序,但我没有看到另一种方法。 IoC也不会解决它,因为我可以在容器中注册它,我也可以调用Create()......

你认为这是一个好的方案吗?你有其他想法吗?

编辑:我知道我写的是一个例子,它不是线程安全的,线程安全不是问题的一部分

7 个答案:

答案 0 :(得分:32)

单身人士很难看,但因为用户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

答案 1 :(得分:22)

一个带有参数的单身人士对我来说有点腥味。

考虑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);             
     } 
}

在多线程环境中,添加同步以避免竞争条件。

答案 2 :(得分:6)

更好的回答:

  1. 创建一个界面:ISingleton(包含您希望它执行的任何操作)

  2. 您的类型:Singleton : ISingleton

  3. 假设您有权访问UnityContainer:

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

    1. 当您准备创建类型使用时(假设您使用Unity进行DI):
    2. _singletonContainer.RegisterType(typeof(ISingleton), new Singleton(params));

      1. 如果您想抓住单身人士,请使用:
      2. 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:非线程安全代码:)

答案 3 :(得分:2)

annakata提供的双锁单例解决方案不会在所有平台上每次都有效。这种方法存在一个缺陷,有很好的记录。不要使用这种方法,否则你最终会遇到问题。

解决此问题的唯一方法是使用volatile关键字,例如

private static volatile Singleton m_instance = null;

这是唯一的线程安全方法。

答案 4 :(得分:1)

如果您使用的是.NET 4(或更高版本),则可以使用System.Lazy类型。 它会照顾你的线程安全问题并且做得很懒,所以你不会不必要地创建实例。 这样代码简洁干净。

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; } }
}

答案 5 :(得分:0)

我实际上在代码中看不到单例。 使用静态的参数化getInstance方法,该方法返回单例并在之前未使用时创建它。

答案 6 :(得分:0)

/// <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;
    }
}