2010-06-14 54 views
66

這是一個比真正的問題更多的文檔。這似乎不是已經對SO尚未解決(除非我錯過了),所以這裏有雲:泛型類的靜態成員是否與特定實例綁定?

試想一下,包含靜態成員的通用類:

class Foo<T> { 
    public static int member; 
} 

是否存在的一個新實例每個特定類的成員,還是所有Foo類都只有一個實例?

Foo<int>.member = 1; 
Foo<string>.member = 2; 
Console.WriteLine (Foo<int>.member); 

結果是什麼,而這是哪裏的行爲記錄在案:

它可以很容易地通過這樣的代碼驗證?

+3

簡短回答:每個* actual *類都有一個新實例,即每個類型使用一個'T'('Foo '和'Foo '代表兩個不同的類,每個類都有一個實例,但是'Foo '的幾個實例將共享'member'的單個實例)。有關更詳細的示例,請參閱:http://stackoverflow.com/a/38369256/336648 – Kjartan 2016-07-14 08:45:46

回答

76

A static字段在同一類型的所有實例之間共享。 Foo<int>Foo<string>是兩種不同的類型。這可以通過下面的行的代碼被證明:

// this prints "False" 
Console.WriteLine(typeof(Foo<int>) == typeof(Foo<string>)); 

至於這何處記錄,下面是在部分1.6.5字段的C#語言規範的實測值(爲C#3):

靜態字段確切標識一個 存儲位置。無論創建多少個 實例, 都只有一個 靜態字段的副本。

如前所述; Foo<int>Foo<string>不是同一類;它們是由同一個通用類構建的兩個不同的類。這是如何發生在上述文獻的第4.4節中概述:

一個通用類型聲明,其本身 表示未結合的通用類型的 用作「藍圖」,以形成許多 不同的類型,通過應用 類型參數。

+20

如果您在C#和Java中開發,下面是一個問題。雖然'Foo '和'Foo '在C#中是不同的類型,但它們是Java中**相同的**類型,因爲Java處理泛型(類型擦除/編譯器技巧)的方式。 – Powerlord 2010-06-14 15:29:04

+0

如果是這樣的話: Foo foo1 = new Foo (); foo1。構件= 10; Foo foo2 = new Foo (); foo2.member = 20; 這裏發生了什麼? – Everyone 2016-11-20 11:29:13

+0

@Everyone成員的價值將改變兩個實例(並將20),因爲他們共享相同的類型Foo 。 – 2017-04-11 09:03:16

4

他們沒有共享。不確定它在哪裏存在記錄,但分析警告CA1000不要在泛型類型上聲明靜態成員)由於可能導致代碼更復雜的風險而發出警告。

+1

哇,另一個規則我不會在FX Cop上相處。 – 2010-06-14 13:58:48

-2

IMO,你需要測試它,但我認爲

Foo<int>.member = 1; 
Foo<string>.member = 2; 
Console.WriteLine (Foo<int>.member); 

將輸出1因爲我認爲,在編譯過程中,compilator創建1類爲您使用的每個泛型類(在您例如: Foo<int>Foo<string>)。

但我不是100%肯定=)。

備註:我認爲使用這種靜態屬性並不是一個好的設計,也不是一個好習慣。

+3

如果不是100%確定 - 不發表評論 – sll 2016-04-14 16:19:52

1

他們沒有真正分享。 因爲該成員根本不屬於該實例。 靜態類成員屬於類本身。 因此,如果您有MyClass.Number,它對所有MyClass.Number對象都是相同的,因爲它甚至不依賴於該對象。 您甚至可以調用或修改沒有任何對象的MyClass.Number。

但由於Foo < int>與Foo < string不同,所以這兩個數字不共享。

一個例子來說明這一點:

TestClass<string>.Number = 5; 
TestClass<int>.Number = 3; 

Console.WriteLine(TestClass<string>.Number); //prints 5 
Console.WriteLine(TestClass<int>.Number);  //prints 3 
13

這裏的問題實際上是一個事實,即「通用類」不是類的。

通用類定義只是類的模板,直到它們的類型參數被指定爲止,它們只是一段文本(或幾個字節)。

在運行時,可以爲模板指定一個類型參數,從而使其生效,並創建一個現在完全指定類型的類。這就是爲什麼靜態屬性不是模板範圍的原因,這就是爲什麼你不能在List<string>List<int>之間投射。

這種關係有點類似於類與對象的關係。就像類沒有存在*直到你從它們實例化一個對象,泛型類就不存在,除非你根據模板創建一個類。

P.S.這是很有可能宣佈

class Foo<T> { 
    public static T Member; 
} 

從這是有點顯而易見靜態成員不能共享,因爲T是不同的專業化。

3

泛型的C#實現更接近於C++。在這兩種語言中,MyClass<Foo>MyClass<Bar>不共享靜態成員,但是它們使用Java。在C#和C++ MyClass<Foo>內部在編譯時創建全新的類型,就像泛型是一種宏一樣。您通常可以在堆棧跟蹤中看到他們生成的名稱,如MyClass'1MyClass'2。這就是他們不共享靜態變量的原因。在Java中,泛型是通過使用非泛型類型的編譯器生成代碼更簡單的方法來實現的,並且添加了類型轉換。因此,MyClass<Foo>MyClass<Bar>不會在Java中生成兩個全新的類,而是它們都在同一類MyClass之下,這就是它們共享靜態變量的原因。