2011-04-04 87 views
6

这是这个问题的细微差异:Possible to use a singleton with a non-default constructor in C#?可能在C#中使用带有非默认构造函数的单例吗?

我有一个类,它的构造函数需要参数。我想做这个单例,使得参数在初始化单例时被采用,因此在每次实例被检索时不需要被传递。

我的解决方案(这不是优雅),这是一个CreateInstance()静态方法,它接受参数并构造单例实例。然后我会有另一个静态方法GetInstance()这将无参数来获得单例实例。在代码中,我需要在调用GetInstance之前确保逻辑调用CreateInstance。但是,我无法在编译时强制执行此操作。不过,如果在CreateInstance之前调用它,则可以在运行时检查GetInstance中的异常。

是否有无论如何我可以通过编译时执行来实现这种行为?或者至少,有没有更好的方法来做同样的事情?

+0

@ user691226你应该为你如何做到这一点已经,我可能有一些指针。 – msarchet 2011-04-04 14:22:39

回答

5

在编译时没有办法做到这一点,因为这就像是要求编译器“你能证明在代码Y被执行之前,在多线程的情况下从不执行代码X吗?”。它不能做到。

至于你的设计的运行时行为,我认为这是最好的。

通过在单例类中暴露Func<SingletonType>属性,可以使它稍微好一些。当有人要求单例实例并且实例还没有被创建时,你的类会调用这个“工厂方法”来构造单例。如果工厂方法是null,那么您使用某些默认参数来引发异常或(如果适用)构造。

这样做基本上推迟了单身人士的建设,直到它第一次真正需要,所以它的一些改进。但基本原则是一样的。

更新:

由于LukeH指出,其实这就是什么Lazy<T>做(.NET 4只)。如果可能的话,使用那个而不是写你自己的。

+0

感谢您的回复。我想我希望我可能错过了一种完全不同的方式来实现相同的行为,使我可以编译时间检查。 – millie 2011-04-04 14:29:42

+2

您几乎已经描述了'懒惰'的工作方式:http://msdn.microsoft.com/en-us/library/dd642331.aspx – LukeH 2011-04-04 14:34:26

+0

@LukeH:感谢您的提示,我从未使用过那样的远。 – Jon 2011-04-04 14:37:58

0

如果单体对象不存在,您可以只需要GetInstance()调用CreateInstance()方法。

+1

您不能这样做,因为GetInstance没有CreateInstance需要的参数。 – millie 2011-04-04 14:24:49

+0

你能确定需要什么吗 – msarchet 2011-04-04 14:26:40

0

我会这样做。您可能需要添加锁或其他事情,以确保:

public class ClassA {  
    private static ClassA instance; 

    private int param; 

    private ClassA(int param) { 
     this.param = param; 
    } 

    public static ClassA getInstance() { 
     if (instance == null) { 
      throw new CustomException("Not yet initialised"); 
     } else { 
      return instance; 
     } 
    } 

    public static void createInstance(int param) { 
     if (instance == null) { 
      instance = new ClassA(param); 
     } 
    } 
} 
+1

是的,这是我已经有的代码。不幸的是,问题是我是否可以改变这种行为的运行时执行来编译时间行为。但这似乎不可能。 – millie 2011-04-04 14:31:19

+0

哦,对不起,我误解了你的问题。但是,不,你不能。我认为这或多或少是唯一的方法。 – 2011-04-04 14:33:32

0

在你GetInstance()方法,你为什么不只是打电话的CreateInstance如果你的价值是零,那么你有懒初始化..

+0

因为他需要将参数传递给'CreateInstance'函数。这并不能解决所问的问题。 – 2011-04-04 14:29:18

+0

他从哪里得到这些神奇的参数,如果他们没有传入GetInstance,因为没有传入GetInstance我认为他们会知道在这一点.. – 2011-04-04 14:33:31

+0

这个问题已经解决这个问题:*“我的解决方案(这不是优雅),因为它有一个CreateInstance()静态方法,该方法接受参数并构造单例实例,然后我有另一个静态方法GetInstance(),它将无参数地获取单例实例。“* – 2011-04-04 14:34:12

1

在一个经典单中,真正的魔力发生在static readonly其中只要它是用来创建实例:

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

    private MySingleton() {} 

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

} 

如果你有参数传递给构造函数,你必须实现自己锁(注意双if sandwitching的lock):

public class MySingletonWithConstructor 
{ 
    private static _instance; 
    private static object _lock = new Object(); 

    private MySingletonWithConstructor(string myArg) 
    { 
     // ... do whatever necessary 
    } 

    public static MySingletonWithConstructor Instance 
    { 
     get 
     { 
      if(_instance==null) 
      { 
       lock(_lock) 
       { 
        if(_instance==null) // double if to prevent race condition 
        { 
         _instance = new MySingletonWithConstructor("Something"); 
        } 
       } 
      } 
      return _instance; 
     } 
    } 

} 
+0

+1尼斯......我从来不会想到这个双“if”结构,花了我大约一分钟的时间来理解它。简单而有效。 – corlettk 2011-04-30 11:38:13

+0

这是一个完全无用的构造,因为如果单例实例getter总是传递“Something”(或其他内部定义的值),那么myArg不起参数的作用 - 至少不像消费者所关心的那样 - 如此有效这个类仍然有一个无参数的构造函数 – BaltoStar 2017-06-21 03:19:28

0

使用的CreateInstance()是一个Lazy<T>的装载机具有的GetInstance RET你可能想创建一个静态只读字段,该字段设置为= thelazy.Value以确保进入CreateInstance()的单个条目)