¿Los miembros estáticos de una clase genérica están vinculados a la instancia específica?

85

Esto es más una documentación que una pregunta real. Esto no parece haberse abordado todavía en SO (a menos que me lo haya perdido), así que aquí va:

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

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

¿Existe una nueva instancia del miembro para cada clase específica, o hay una sola instancia para todas las clases de tipo Foo?

Se puede verificar fácilmente mediante un código como este:

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

¿Cuál es el resultado y dónde se documenta este comportamiento?

mafu
fuente
4
Respuesta corta: hay una nueva instancia para cada clase real , es decir, una para cada tipo de Tusado ( Foo<int>y Foo<string>representan dos clases diferentes, y tendrán una instancia cada una, pero varias intances de Foo<int>compartirán una sola instancia de member). Para obtener un ejemplo más detallado, consulte: stackoverflow.com/a/38369256/336648
Kjartan

Respuestas:

92

Un staticcampo se comparte entre todas las instancias del mismo tipo . Foo<int>y Foo<string>son dos tipos diferentes. Esto se puede probar con la siguiente línea de código:

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

En cuanto a dónde está documentado, lo siguiente se encuentra en la sección 1.6.5 Campos de la Especificación del lenguaje C # (para C # 3):

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

Como se dijo antes; 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 sucede esto se describe en la sección 4.4 del documento mencionado anteriormente:

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

Fredrik Mörk
fuente
26
Aquí tienes un truco si desarrollas tanto en C # como en Java. Si bien Foo<int>y Foo<String>son tipos diferentes en C #, son del mismo tipo en Java debido a cómo Java maneja los genéricos (borrado de tipos / trucos del compilador).
Powerlord
¿Y si este fuera el caso? Foo <int> foo1 = new Foo <int> (); foo1.member = 10; Foo <int> foo2 = new Foo <int> (); foo2.member = 20; ¿Qué pasa aquí?
Todos el
@Todo el mundo, el valor de miembro cambiará para ambas instancias (y será 20) porque comparten el mismo tipo Foo <int>.
Stas Ivanov
1
¿Qué sucede si hereda una clase base no genérica que tiene un miembro estático en una clase genérica derivada? ¿Seguirá siendo cierto esto?
Brain2000
@StasIvanov, foo1.member seguirá siendo 10 y foo2.member tendrá 20. Verifique
SHRI
16

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 especifican sus parámetros de tipo, son solo un fragmento 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 puede lanzar 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.

PD: Es bastante posible declarar

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

A partir de esto, es bastante obvio que los miembros estáticos no se pueden compartir, ya que T es diferente para diferentes especializaciones.

SWeko
fuente
4

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

Hans Olsson
fuente
1
Vaya, otra regla más con la que no me llevaría bien en FX Cop.
Matthew Whited
Está documentado aquí
NtFreX
3

La implementación de genéricos en C # está más cerca de C ++. En ambos lenguajes MyClass<Foo>y MyClass<Bar>no comparten miembros estáticos, pero sí en Java. En C # y C ++ MyClass<Foo>crea internamente un tipo completamente nuevo en tiempo de 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'1y MyClass'2. Por eso no comparten variables estáticas. En Java, los genéricos se implementan mediante un método más simple de compilador que genera código utilizando tipos no genéricos y agregando conversiones de tipos por todas partes. Entonces, MyClass<Foo>y MyClass<Bar>no genere dos clases completamente nuevas en Java, en cambio, ambas son la misma clase por MyClassdebajo y es por eso que comparten variables estáticas.

Shital Shah
fuente
1

Realmente no se comparten. Porque el miembro no pertenece a la instancia en absoluto. Un miembro de clase estática pertenece a la propia clase. Entonces, 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 de 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
Marcas
fuente
-4

En mi opinión, necesitas probarlo, pero creo que

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

saldrá 1porque creo que, durante la compilación, el compilador crea 1 clase para cada clase genérica que usa (en su 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 utilizar este tipo de atributos estáticos.

Clement Herreman
fuente
6
Si no está 100% seguro - no comente
sll