2010-06-14 108 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之下,这就是它们共享静态变量的原因。