2010-06-14 8 views
66

Esto es más una documentación que una pregunta real. Esto no parece haber sido abordado en SO todavía (a no ser que me lo perdí), así que aquí va:¿Los miembros estáticos de una clase genérica están vinculados a la instancia específica?

imaginar una clase genérica que contiene un miembro estático:

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

¿Hay una nueva instancia de la miembro para cada clase específica, o solo hay una instancia única para todas las clases de tipo Foo?

puede ser fácilmente verificado por código como este:

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

Cuál es el resultado, y donde se documenta este comportamiento?

+3

Respuesta breve: Hay una nueva instancia para cada clase * actual *, es decir, una para cada tipo de 'T' utilizada (' Foo 'y' Foo 'representan dos clases diferentes, y tendrán una instancia cada una, pero varias intancias de 'Foo ' compartirán una sola instancia de 'miembro'). Para un ejemplo más detallado, vea: http://stackoverflow.com/a/38369256/336648 – Kjartan

Respuesta

76

Un campo static se comparte en todas las instancias del mismo tipo. Foo<int> y Foo<string> son dos tipos diferentes. Esto puede ser demostrado por la siguiente línea de código:

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

En cuanto a dónde esto está documentado, la siguiente es encontrar en la sección 1.6.5 campos de la C# Language Specification (para C# 3):

Un campo estático identifica exactamente una ubicación de almacenamiento . No importa cuántas instancias de se creen, , solo hay una copia de un campo estático .

Como se indicó anteriormente; Foo<int> y Foo<string> no son de la misma clase; son dos clases diferentes construidas a partir de la misma clase genérica. ¿Cómo ocurre esto se describe en la sección 4.4 del documento mencionado anteriormente:

Una declaración de tipo genérico, por sí mismo, denota un tipo genérico no unido que se utiliza como un “modelo” para formar muchos tipos diferentes, mediante la aplicación de argumentos de tipo .

+20

Aquí hay un mensaje si desarrollas tanto en C# como en Java. Mientras 'Foo ' y 'Foo ' son tipos diferentes en C#, son del mismo tipo ** en Java debido a la forma en que Java maneja los genéricos (tipo trucos de borrado/compilación). – Powerlord

+0

¿Qué pasa si este fue el caso? Foo foo1 = new Foo (); foo1.miembro = 10; Foo foo2 = new Foo (); foo2.member = 20; ¿Qué sucede aquí? – Everyone

+0

@Todo el valor del miembro se cambiará para ambas instancias (y será 20) porque comparten el mismo tipo Foo . –

4

No se comparten. No estoy seguro de dónde está documentado, pero la advertencia de análisis CA1000 (No declare los miembros estáticos en los tipos genéricos) advierte contra esto debido al riesgo de complicar el código.

+1

Wow, otra regla más con la que no me llevaría bien en FX Cop. –

-2

OMI, tienes que probarlo, pero creo que

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

es la salida 1 porque creo que, durante la compilación, el compilador crear 1 clase para cada clase genérica que utiliza (en ti ejemplo: Foo<int> y Foo<string>).

Pero no estoy 100% seguro =).

Observación: Creo que no es un buen diseño ni una buena práctica usar ese tipo de atributos estáticos.

+3

Si no está 100% seguro, no comentes – sll

1

Realmente no se comparten. Porque el miembro no pertenece a la instancia en absoluto. Un miembro de clase estático pertenece a la clase misma. Por lo tanto, si tiene MyClass.Number, es el mismo para todos los objetos MyClass.Number porque ni siquiera depende del objeto. Incluso puede llamar o modificar MyClass.Number sin ningún objeto.

Pero como Foo < int> no es la misma clase que Foo < string> estos dos números no se comparten.

un ejemplo para mostrar esto:

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

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

El problema aquí es en realidad el hecho de que las "clases genéricas" no son clases en absoluto.

Las definiciones de clases genéricas son solo plantillas para clases, y hasta que se especifiquen sus parámetros de tipo, son solo una pieza de texto (o un puñado de bytes).

En tiempo de ejecución, se puede especificar un parámetro de tipo para la plantilla, dándole vida y creando una clase del tipo ahora completamente especificado. Es por eso que las propiedades estáticas no abarcan toda la plantilla, y es por eso que no se puede convertir entre List<string> y List<int>.

Esa relación refleja un poco la relación clase-objeto. Al igual que las clases no existen * hasta que crea una instancia de un objeto a partir de ellas, las clases genéricas no existen hasta que crea una clase basada en la plantilla.

P.S. Es bastante posible declarar

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

de todo esto es un poco obvio que los miembros estáticos no se pueden compartir, como T es diferente para diferentes especializaciones.

3

La implementación de C# de los genéricos está más cerca de C++. En ambos idiomas, MyClass<Foo> y MyClass<Bar> no comparten miembros estáticos, pero en Java sí lo hacen. En C# y C++ MyClass<Foo> crea internamente un tipo completamente nuevo en el momento de la compilación, como si los genéricos fueran una especie de macros. Por lo general, puede ver sus nombres generados en el seguimiento de la pila, como MyClass'1 y MyClass'2. Es por eso que no comparten variables estáticas. En Java, los genéricos se implementan mediante un método más simple de compilación que genera código usando tipos no genéricos y agregando tipos de moldes por todas partes. Por lo tanto, MyClass<Foo> y MyClass<Bar> no generan dos clases totalmente nuevas en Java, sino que ambas son de la misma clase MyClass y es por eso que comparten variables estáticas.

Cuestiones relacionadas