泛型类的静态成员是否绑定到特定实例?


83

这更多是文档而不是实际问题。到目前为止,这似乎还没有得到解决(除非我错过了),所以这里是:

想象一下一个包含静态成员的通用类:

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

每个特定类是否都有成员的新实例,或者所有Foo型类只有一个实例?

可以通过以下代码轻松验证:

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

结果如何?此行为记录在哪里?


4
简短的回答:每个实际类都有一个新实例,即每种T使用的类都有一个新实例(Foo<int>Foo<string>表示两个不同的类,并且每个实例都有一个实例,但是多个Foo<int>实例将共享一个实例member)。有关更详细的示例,请参阅:stackoverflow.com/a/38369256/336648
Kjartan

Answers:


90

在相同类型的static所有实例之间共享一个字段。Foo<int>Foo<string>是两种不同的类型。可以通过以下代码行证明这一点:

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

至于记录在哪里,可以在C#语言规范(对于C#3)的1.6.5字段部分中找到以下内容:

静态字段恰好标识一个存储位置。无论创建一个类的实例有多少,静态字段只有一个副本。

如前所述;Foo<int>并且Foo<string>不是同一类;它们是从同一泛型类构造的两个不同的类。上述文档的第4.4节概述了这种情况。

泛型类型声明本身表示未绑定的泛型类型,它通过应用类型实参而用作“蓝图”以形成许多不同的类型。


26
如果您同时使用C#和Java开发,这是一个陷阱。虽然Foo<int>Foo<String>是C#中的不同类型,但是由于Java处理泛型的方式(类型擦除/编译器技巧),它们在Java中是相同的类型。
Powerlord 2010年

如果是这样的话:Foo <int> foo1 = new Foo <int>(); foo1.member = 10; Foo <int> foo2 =新的Foo <int>(); foo2.member = 20; 这里会发生什么?
大家

@Everyone两个实例的成员值将更改(将为20),因为它们共享相同的类型Foo <int>。
Stas Ivanov

1
如果您将具有静态成员的非泛型基类继承到派生的泛型类中怎么办?这仍然是真的吗?
Brain2000

@ StasIvanov,foo1.member将保持10,而foo2.member将保持20。请验证
SHRI

16

这里的问题实际上是“泛型类”根本不是类的事实。

通用类定义只是类的模板,在指定其类型参数之前,它们只是一段文本(或少数字节)。

在运行时,可以为模板指定一种类型参数,从而使其活灵活现,并创建一个现已完全指定类型的类。这就是为什么静态属性不在模板范围内的原因,这就是为什么您不能在List<string>和之间进行强制转换List<int>

该关系有点像类-对象关系。就像类不存在*,直到您从它们实例化一个对象一样,泛型类也不存在,直到您基于模板创建了一个类。

PS很可能要声明

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

由此可见,静态成员无法共享,因为不同专业的T有所不同。


4

他们没有共享。不确定在哪里记录,但是分析警告CA1000请勿在泛型类型上声明静态成员)仅对此警告,因为存在使代码更复杂的风险。


1
哇,我不会在FX Cop上遵守另一条规则。
马修·怀特

它记录在这里
NtFreX

3

泛型的C#实现更接近C ++。在这两种语言中MyClass<Foo>MyClass<Bar>它们都不共享静态成员,但是在Java中,它们共享静态成员。在C#和C ++中,MyClass<Foo>在编译时内部会创建全新的类型,就好像泛型是宏一样。通常,您可以在堆栈跟踪中看到它们的生成名称,例如MyClass'1MyClass'2。这就是为什么它们不共享静态变量的原因。在Java中,泛型通过编译器使用非泛型类型生成代码并在各处添加类型强制转换的更简单方法来实现。因此MyClass<Foo>MyClass<Bar>不要在Java中生成两个全新的类,而是它们在MyClass下面是同一类,这就是它们共享静态变量的原因。


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

-4

IMO,您需要对其进行测试,但是我认为

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

将会输出,1因为我认为在编译期间,编译器会为您使用的每个泛型类创建一个类(在您的示例中为:Foo<int>Foo<string>)。

但我不是100%确定=)。

备注:我认为使用这种静态属性既不是一个好的设计,也不是一个好的实践。


6
如果不是100%肯定-请不要评论
sll 2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.