2011-04-04 67 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()的單個條目)

相關問題