![](/img/trans.png)
[英]A question about static members inside non-static classes and garbage collection
[英]Question about non-static members of a singleton (C#)
我有一個關於單身人士的問題,我認為我知道答案......但每次情況彈出時我都會稍微猜測一下自己,所以我想知道具體的答案。
假設我有兩個類設置如此...
public class ClassA
{
private static ClassA _classA;
public static ClassA Instance { get { return _classA ?? LoadClassA(); } }
private ClassA(){}
public static ClassA LoadClassA()
{
_classA = new ClassA();
return _classA;
}
private ClassB _classB = new ClassB();
public ClassB ClassB { get { return _classB; } set { _classB = value; } }
}
public class ClassB
{
}
我的問題很簡單。
我想知道如果我訪問ClassA的單例,_classB字段是否也被視為靜態? 即使我沒有將_classB聲明為靜態成員。
我總是基本上猜到_classB它被視為靜態(一個內存位置),但我想知道肯定。 我錯了嗎? 每次從singleton ClassA訪問時都會為_classB創建一個新對象...即使內存中只有一個ClassA? 或者是因為我在聲明中新建了_classB,導致只有一個實例?
提前謝謝,-Matt
創建單例時,您將創建一個非靜態類型的靜態實例。
在這種情況下,您的類型(A類)包含對另一種類型(B類)的引用。 靜態實例將保存對B類對象的單個實例的單個引用。 從技術上講,它不是“靜態的”,但由於它的根源是靜態對象(類A實例),它的行為就像一個靜態變量。 您將始終擁有一個且僅有一個B類對象(由A類實例指向)。 您永遠不會在A類中創建多個B類實例。
但是,沒有什么能阻止在其他地方生成第二個B類實例 - 這將是一個不同的實例。
_classB是ClassA的實例(非靜態)成員。 ClassA的每個實例都有一個_classB實例(給定你編寫的字段初始值設定項)。 因此,如果您使用Singleton模式訪問ClassA,因此總是(最多)加載一個ClassA實例,您將始終(最多)通過ClassA加載一個ClassB實例。
現在,由於ClassB有一個公共默認構造函數,所以遠在其他地方可能會自己創建實例。 如果這是一個問題,請考慮將ClassB類設為私有。 此外,由於ClassA具有公共默認構造函數,因此沒有什么能阻止人們創建任意數量的實例。 您可以將no-arg構造函數設為私有:
private ClassA() {}
單例模式確保始終只能從ClassA
的單例實例訪問ClassB
一個實例。 單例模式的重點在於它保證在任何時候只有一個 ClassA
實例可用,因此只有一個對_classB
引用(盡管因為ClassA
是可變的,所以這個引用可以改變)。
但請注意, ClassB
的范圍仍然是實例級而不是靜態級。 編譯器永遠不會做任何奇怪的事情,以至於使用與您指示的不同的范圍說明符。 無論您是否使用單例,您仍必須通過實例訪問ClassB
的引用。
通過這樣的聲明:
private ClassB _classB = new ClassB();
每當調用ClassA的構造函數時,您都會將_classB實例化為ClassB的新實例。
使用單例模式,調用ClassA的構造函數的唯一方法是使用靜態方法(在您的情況下通過Instance屬性),這有效地保證只創建一個ClassA。
這樣可以確保_classB只會被新建一次,但它是非靜態的。 但是,如果有人將ClassA更改為將來不再是單例,那么您將開始創建多個ClassB實例。 如果_classB真的是靜態的,那么情況就不是這樣了。
如定義的,ClassA違反了單身人士的定義。 想象一下兩個線程同時調用靜態Instance屬性。 由於訪問不同步,您可以使用兩個不同的ClassA實例,從而獲得兩個不同的ClassB實例。
如果只需要一個實例,為什么不將ClassB標記為靜態。
在我看來,編寫清晰的代碼以向您展示完整的意圖總是更好。 這樣,下一個處理代碼的人就不必玩猜謎游戲來確定你想做什么。
因為ClassA是單例,所以在您的應用程序中只有一個實例。 在該類實例中,有一個存儲ClassB變量的成員。 初始化此變量后,它是一個實例變量,但由於您只有1個ClassA實例,因此您將始終獲得相同的ClassB實例。
另外,我建議不要創建內部實例,因為它不是線程安全的。
private static ClassA _instance = new ClassA();
private ClassA() {}
public static ClassA Insance { get { return _instance; } }
通過不延遲加載和立即初始化,您可以確保線程安全。
_classB不是靜態的; ClassA的實例包含對_classB實例的引用; 如果ClassA的實例是靜態的(因為它在單例實例中),那么它仍將包含對_classB的引用,但ClassA的新實例將包含對(可能)_classB的不同實例的DIFFERENT引用。 因此,如果您引用相同的ClassA實例(通過單例模式),您仍將獲得相同的_classB實例; 但是如果你創建一個新的ClassA實例,你會得到一個不同的_classB實例,這就是為什么_classB不是靜態的。
令我驚訝的是,沒有人指出ClassB
是具有公共二傳手的ClassA
的公共財產。 因此,沒有什么能阻止任何人寫下這樣的東西:
ClassA.Instance.ClassB = new ClassB();
這將丟棄的實例ClassB
當時創建ClassA
的孤實例被創建,並會用新創建的實例替換它ClassB
。
一個相關的場景是,只要_classB
在派生類中是可設置的(例如,如果ClassB
的setter受到保護),就不能保證只有ClassB
的單個實例。
我想編寫這種代碼的正確方法是將ClassA
和ClassB
都實現為單例,並使ClassB
成為ClassA
的內部類(否則你將有2個不相關的類ClassA
和ClassB
,通過ClassA.Instance.ClassB
可以引用ClassB
ClassA.Instance.ClassB
,它(在那種情況下)與ClassB.Instance
沒有什么不同。
如果ClassB
只能在ClassA
實例化,那么可能在派生類中可以有其他ClassB
實例化實例化。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.