带参数的单例

Posted

技术标签:

【中文标题】带参数的单例【英文标题】:Singleton with parameters 【发布时间】:2011-05-11 08:35:41 【问题描述】:

我需要使用一些参数来实例化一个单例类。我现在的做法是:

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()...

您认为这是一个可行的方案吗?你有别的想法吗?

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

【问题讨论】:

不是单例。互斥在哪里? (即lock)。加上您的 getter 实例应该是创建实例,而不是 ctor - 这就是重点。 我同意 RPM1984,这不是单例。你的论点会改变吗?或者在你的应用程序的整个生命周期中它们会保持不变吗?单例不应该依赖于变量,它可以依赖于配置或另一个单例。但是,请仔细考虑您的设计,并问问自己它是否是您需要的单例。过度使用单例对您的设计非常不利。 它是单例的,相信我 :) getter 无法创建实例,因为它没有参数 - 它可以有,但这样写更方便。而且,参数一旦获得就不会改变。 论据从何而来? @mastoj - 来自一些服务调用 - 它是半静态数据,在我调用后它是静态的。 【参考方案1】:

Singleton 很丑,但由于用户 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 只是为了方便而在这里使用。

【讨论】:

现在,这是一个单例,而不是我明确使用它们(我让 DI 做它们,并且只用于日志记录之类的事情)。 +1 我会继续假设 whateva 决定否决功能正确的答案。去组队。 不,他没有(查看他的个人资料)。不管怎样,所有这些关于单身的话题都让我昏昏欲睡,我要去睡觉了。 :) 谈论修复你的代码......天哪,这是错误的。为你修好了。 T1、T2 并非旨在作为泛型,而只是作为真实类型的代理。【参考方案2】:

一个带有参数的单例对我来说闻起来很腥。

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

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

【讨论】:

-1 我不同意该解决方案,因为我认为您建议在不应该使用的地方使用单例模式。单例是一个类,它只允许创建自己的单个实例,并且通常提供对该实例的简单访问如果应该为具有相同参数的所有请求访问相同的实例,则工厂模式是合适的。在此解决方案中,您可以多次调用 Create(.....) 来破坏类的不变性。 Massimiliano,在我的解决方案中,您可以只调用一次 create。而且我认为我的短信已经很清楚地表明我也不是很喜欢参数化单例的想法。 @Massimiliano Peluso - 现在 that 是一个有用的观察 - 一个工厂......我所追求的实际上是单身/工厂的混合,实际上归结为国际奥委会...无论如何,我想我现在有了答案。谢谢 您可以根据需要多次调用 create(...) :如果它被调用两次或更多次,您只需抛出一个异常。在 SingletonPattern 中,如果和实例已经存在,您应该只返回它。在这种情况下,Songleton 模式不合适,这就是为什么你必须找到一种方法来解决它(抛出异常) 我同意“创建”不是最好的名称,我更喜欢“初始化”以使意图更清楚,但我从原始问题中取了命名以避免混淆。多次调用“创建”不会破坏不变性。我不同意单例模式在任何情况下都是不合适的。如果(且仅当)创建参数在运行时保持不变(例如,通过命令行参数传递或从配置文件中读取),这样的设置实际上可能有意义。【参考方案3】:

更好的答案:

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

    还有你的类型:Singleton : ISingleton

    假设您可以访问 UnityContainer:

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

    当您准备好创建类型时(假设您使用 Unity for DI):

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

    如果您想获取单例,只需使用:

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:非线程安全代码:)

【讨论】:

【参考方案4】:

annakata 提供的双锁单例解决方案并非每次都适用于所有平台。这种方法有一个缺陷,有据可查。不要使用这种方法,否则你会遇到问题。

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

private static volatile Singleton m_instance = null;

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

【讨论】:

【参考方案5】:
/// <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;
    

【讨论】:

【参考方案6】:

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

【讨论】:

以上是关于带参数的单例的主要内容,如果未能解决你的问题,请参考以下文章

CodeWorldkotlin带参数的单例模式封装实现

CodeWorldkotlin带参数的单例模式封装实现

Kotlin 中带参数的单例

Java中带参数的单例

使Daniel Brodie&ŧ039;的单例配方使用关键字参数

简单介绍如何使用PowerMock和Mockito来mock 1. 构造函数 2. 静态函数 3. 枚举实现的单例 4. 选择参数值做为函数的返回值(转)