I membri statici di una classe generica sono legati all'istanza specifica?


85

Questa è più una documentazione che una vera domanda. Questo non sembra ancora essere stato risolto su SO (a meno che non me lo sia perso), quindi ecco qui:

Immagina una classe generica che contiene un membro statico:

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

C'è una nuova istanza del membro per ogni classe specifica o c'è solo una singola istanza per tutte le classi di tipo Foo?

Può essere facilmente verificato da un codice come questo:

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

Qual è il risultato e dove è documentato questo comportamento?


4
Risposta breve: c'è una nuova istanza per ogni classe effettiva , cioè una per ogni tipo di Tusato ( Foo<int>e Foo<string>rappresenta due classi differenti, e avrà un'istanza ciascuna, ma diverse intenzioni di Foo<int>condivideranno una singola istanza di member). Per un esempio più dettagliato, vedere: stackoverflow.com/a/38369256/336648
Kjartan

Risposte:


92

Un staticcampo è condiviso tra tutte le istanze dello stesso tipo . Foo<int>e Foo<string>sono due tipi diversi. Ciò può essere dimostrato dalla seguente riga di codice:

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

Per quanto riguarda dove questo è documentato, quanto segue si trova nella sezione 1.6.5 Campi della specifica del linguaggio C # (per C # 3):

Un campo statico identifica esattamente una posizione di archiviazione. Non importa quante istanze di una classe vengono create, c'è sempre una sola copia di un campo statico.

Come affermato prima; Foo<int>e Foo<string>non sono la stessa classe; sono due classi differenti costruite dalla stessa classe generica. Il modo in cui ciò accade è delineato nella sezione 4.4 del documento sopra menzionato:

Una dichiarazione di tipo generico, di per sé, denota un tipo generico non associato che viene utilizzato come "progetto" per formare molti tipi diversi, mediante l'applicazione di argomenti di tipo.


26
Ecco un trucco se sviluppi sia in C # che in Java. Sebbene Foo<int>e Foo<String>siano tipi diversi in C #, sono dello stesso tipo in Java a causa del modo in cui Java si occupa dei generici (tipo di cancellazione / trucchi del compilatore).
Powerlord

E se fosse così: Foo <int> foo1 = new Foo <int> (); foo1.member = 10; Foo <int> foo2 = new Foo <int> (); foo2.member = 20; Che succede qui?
Tutti il

@Tutti il ​​valore di membro verrà modificato per entrambe le istanze (e sarà 20) perché condividono lo stesso tipo Foo <int>.
Stas Ivanov

1
Cosa succede se erediti una classe base non generica che ha un membro statico in una classe generica derivata? Sarà ancora vero?
Brain2000

@StasIvanov, foo1.member rimarrà 10 e foo2.member sarà 20. Si prega di verificare
SHRI

16

Il problema qui è in realtà il fatto che le "classi generiche" non sono affatto classi.

Le definizioni di classe generiche sono solo modelli per classi e fino a quando non vengono specificati i parametri di tipo, sono solo un pezzo di testo (o una manciata di byte).

In fase di esecuzione, è possibile specificare un parametro di tipo per il modello, portandolo così alla vita e creando una classe del tipo, ora, completamente specificato. Ecco perché le proprietà statiche non sono a livello di modello ed è per questo che non è possibile eseguire il cast tra List<string>e List<int>.

Quella relazione rispecchia in qualche modo la relazione classe-oggetto. Proprio come le classi non esistono * finché non si crea un'istanza di un oggetto da esse, le classi generiche non esistono, finché non si crea una classe basata sul modello.

PS È del tutto possibile dichiarare

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

Da questo è abbastanza ovvio che i membri statici non possono essere condivisi, poiché T è diverso per le diverse specializzazioni.


4

Non sono condivisi. Non sono sicuro di dove sia documentato, ma l'avviso di analisi CA1000 ( Non dichiarare membri statici su tipi generici ) mette in guardia proprio da questo a causa del rischio di rendere il codice più complicato.


1
Wow, ancora un'altra regola con cui non andrei d'accordo su FX Cop.
Matthew Whited

È documentato qui
NtFreX

3

L'implementazione di generics in C # è più vicina a C ++. In entrambi questi linguaggi MyClass<Foo>e MyClass<Bar>non condividono membri statici, ma in Java lo fanno. In C # e C ++ MyClass<Foo>crea internamente un tipo completamente nuovo in fase di compilazione, come se i generici fossero una specie di macro. Di solito puoi vedere i loro nomi generati nella traccia dello stack, come MyClass'1e MyClass'2. Questo è il motivo per cui non condividono variabili statiche. In Java, i generici sono implementati da un metodo più semplice di compilatore che genera codice utilizzando tipi non generici e aggiungendo cast di tipo dappertutto. Quindi MyClass<Foo>e MyClass<Bar>non generare due classi completamente nuove in Java, invece sono entrambe la stessa classe MyClasssottostante ed è per questo che condividono variabili statiche.


1

Non sono realmente condivisi. Perché il membro non appartiene affatto all'istanza. Un membro della classe statica appartiene alla classe stessa. Quindi, se hai MyClass.Number è lo stesso per tutti gli oggetti MyClass.Number perché non dipende nemmeno dall'oggetto. Puoi anche chiamare o modificare MyClass.Number senza alcun oggetto.

Ma poiché Foo <int> non è la stessa classe di Foo <string> questi due numeri non sono condivisi.

Un esempio per dimostrarlo:

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

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

-4

IMO, devi provarlo, ma penso che

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

uscirà 1perché penso che, durante la compilazione, il compilatore crei 1 classe per ogni classe generica che usi (nel tuo esempio: Foo<int>e Foo<string>).

Ma non sono sicuro al 100% =).

Nota: penso che non sia un buon design né una buona pratica usare questo tipo di attributi statici.


6
Se non
sei
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.