views:

142

answers:

5

This is more of a documentation than a real question. I noticed the principle it is not described on SO yet (did I miss it?), so here goes:

Imagine a generic class that contains a static member:

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

Is there a new instance of the member for each specific class, or is there only a single instance for all Foo-type classes?

It can easily be verified by code like this:

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

What is the result, and where is this behavior documented?

+3  A: 

They are not shared. Not sure where it's documented but analysis warning CA1000 (Do not declare static members on generic types) warns against just this due to the risk of making the code more complicated.

ho1
Wow, yet another rule I wouldn't get along with on FX Cop.
Matthew Whited
A: 

IMO, you need to test it, but I think

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

will output 1 because I think that, during compilation, the compilator create 1 class for every generic class you use (in you example : Foo and Foo ).

But I'm not 100% sure =).

Remark : I think it's not a good design nor a good practise to use such kind of static attributes.

Clement Herreman
+16  A: 

A static field is shared across all instances of the same type. Foo<int> and Foo<string> are two different types. This can be proven by the following line of code:

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

As for where this is documented, the following is found in section 1.6.5 Fields of the C# Language Specification (for C# 3):

A static field identifies exactly one storage location. No matter how many instances of a class are created, there is only ever one copy of a static field.

As stated before; Foo<int> and Foo<string> are not the same class; they are two different classes constructed from the same generic class. How this happens is outlined in section 4.4 of the above mentioned document:

A generic type declaration, by itself, denotes an unbound generic type that is used as a “blueprint” to form many different types, by way of applying type arguments.

Fredrik Mörk
Here's a gotcha if you develop in both C# and Java. While `Foo<int>` and `Foo<String>` are different types in C#, they are the **same** type in Java because of how Java deals with generics (type erasure/compiler tricks).
R. Bemrose
A: 

They are not really shared. Because the member doesn't belong to the instance at all. A static class member belongs to the class itself. So, if you have MyClass.Number it is the same for all MyClass.Number objects because it not even depends on the object. You can even call or modify MyClass.Number without any object.

But since Foo< int > is not the same class as Foo< string > these two numbers are not shared.

An example to show this:

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

Console.WriteLine(TestClass<string>.Number);  //prints 5
Console.WriteLine(TestClass<int>.Number);     //prints 3
Marks
+2  A: 

The problem here is actually the fact that "generic classes" are not classes at all.

Generic class definitions are just templates for classes, and until their type parameters are specified, they are just a piece of text (or a handful of bytes).

At runtime, one can specify a type parameter for the template, thus bringing it to life, and creating a class of the, now, fully specified type. That's why static properties are not template-wide, and that's why you cannot cast between List<string> and List<int>.

That relationship kinda mirrors the class-object relationship. Just like classes do not exist* until you instantiate an object from them, generic classes do not exist, until you make a class based on the template.

P.S. It's quite possible to declare

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

From this is kinda obvious that the static members cannot be shared, as T is different for different specializations.

SWeko