Czy statyczne elementy członkowskie klasy ogólnej są powiązane z konkretną instancją?

85

To bardziej dokumentacja niż prawdziwe pytanie. Wydaje się, że to nie zostało jeszcze rozwiązane w SO (chyba że to przegapiłem), więc oto:

Wyobraź sobie klasę ogólną, która zawiera statyczny element członkowski:

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

Czy istnieje nowe wystąpienie elementu członkowskiego dla każdej określonej klasy, czy też jest tylko jedno wystąpienie dla wszystkich klas typu Foo?

Można to łatwo zweryfikować za pomocą takiego kodu:

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

Jaki jest wynik i gdzie jest to udokumentowane?

mafu
źródło
4
Krótka odpowiedź: istnieje nowa instancja dla każdej aktualnej klasy, tj. Po jednej dla każdego typu Tużywanego ( Foo<int>i Foo<string>reprezentują dwie różne klasy i każda będzie miała po jednej instancji, ale kilka intencji Foo<int>będzie współdzielić jedną instancję member). Bardziej szczegółowy przykład można znaleźć na stronie: stackoverflow.com/a/38369256/336648
Kjartan

Odpowiedzi:

92

staticPole jest wspólna dla wszystkich wystąpień tego samego typu . Foo<int>i Foo<string>są dwa różne typy. Można to udowodnić za pomocą następującego wiersza kodu:

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

Jeśli chodzi o miejsca, w których jest to udokumentowane, w sekcji 1.6.5 Pola specyfikacji języka C # (dla języka C # 3):

Pole statyczne identyfikuje dokładnie jedno miejsce przechowywania. Bez względu na to, ile instancji klasy zostanie utworzonych, zawsze istnieje tylko jedna kopia pola statycznego.

Jak wspomniano wcześniej; Foo<int>i Foo<string>nie są tą samą klasą; są to dwie różne klasy zbudowane z tej samej klasy ogólnej. Jak to się dzieje, opisano w sekcji 4.4 wyżej wymienionego dokumentu:

Sama deklaracja typu ogólnego oznacza niezwiązany typ ogólny, który jest używany jako „plan” do tworzenia wielu różnych typów poprzez stosowanie argumentów typu.

Fredrik Mörk
źródło
26
Oto pułapka, jeśli tworzysz zarówno w języku C #, jak i Javie. Chociaż Foo<int>i Foo<String>są różnymi typami w C #, są tego samego typu w Javie ze względu na sposób, w jaki Java radzi sobie z rodzajami (sztuczki typu wymazywanie / kompilator).
Powerlord
A co by było, gdyby tak było: Foo <int> foo1 = new Foo <int> (); foo1.member = 10; Foo <int> foo2 = new Foo <int> (); foo2.member = 20; co się tutaj stało?
Wszyscy
@ Za każdym razem wartość elementu członkowskiego zostanie zmieniona dla obu instancji (i będzie wynosić 20), ponieważ mają one ten sam typ Foo <int>.
Stas Ivanov
1
Co się stanie, jeśli odziedziczysz nieogólną klasę bazową, która ma statyczny element członkowski, do pochodnej klasy ogólnej? Czy to nadal będzie prawdą?
Brain2000
@StasIvanov, foo1.member pozostanie 10, a foo2.member będzie 20. Proszę zweryfikować
SHRI,
16

Problem polega na tym, że „klasy generyczne” w ogóle nie są klasami.

Ogólne definicje klas są tylko szablonami klas i dopóki nie zostaną określone parametry typu, są tylko fragmentem tekstu (lub garścią bajtów).

W czasie wykonywania można określić parametr typu dla szablonu, ożywiając go w ten sposób i tworząc klasę w pełni określonego typu. Dlatego właściwości statyczne nie obejmują całego szablonu i dlatego nie można rzutować między List<string>a List<int>.

Ta relacja w pewnym sensie odzwierciedla relację klasy-obiekt. Tak jak klasy nie istnieją * dopóki nie utworzysz z nich instancji obiektu, tak ogólne klasy nie istnieją, dopóki nie utworzysz klasy opartej na szablonie.

PS Można to zadeklarować

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

Z tego jest dość oczywiste, że statycznych członków nie można udostępniać, ponieważ T jest różne dla różnych specjalizacji.

SWeko
źródło
4

Nie są udostępniane. Nie jestem pewien, gdzie jest to udokumentowane, ale ostrzeżenie analizy CA1000 ( Nie deklaruj statycznych elementów członkowskich w typach ogólnych ) ostrzega przed tym tylko ze względu na ryzyko skomplikowania kodu.

Hans Olsson
źródło
1
Wow, kolejna zasada, z którą nie zgadzałbym się na FX Cop.
Matthew Whited
Jest to udokumentowane tutaj
NtFreX
3

Implementacja typów ogólnych w języku C # jest bliższa C ++. W obu tych językach MyClass<Foo>i MyClass<Bar>nie współdzielą statycznych elementów członkowskich, ale w Javie tak. W językach C # i C ++ MyClass<Foo>wewnętrznie tworzy całkowicie nowy typ w czasie kompilacji, tak jakby typy generyczne były rodzajem makr. Zazwyczaj można zobaczyć ich wygenerowane nazwy w śladzie stosu, na przykład MyClass'1i MyClass'2. Dlatego nie mają wspólnych zmiennych statycznych. W Javie typy generyczne są implementowane przez prostszą metodę generowania kodu przez kompilator przy użyciu typów nieogólnych i dodawania rzutów typów na całym świecie. Tak MyClass<Foo>i MyClass<Bar>nie generują dwa zupełnie nową klasę w Javie, zamiast oboje są tej samej klasy MyClassspodu i dlatego oni dzielą zmiennych statycznych.

Shital Shah
źródło
1

Tak naprawdę nie są udostępniane. Ponieważ członek w ogóle nie należy do instancji. Statyczny element członkowski klasy należy do samej klasy. Tak więc, jeśli masz MyClass.Number, jest to takie samo dla wszystkich obiektów MyClass.Number, ponieważ nie zależy to nawet od obiektu. Możesz nawet wywołać lub zmodyfikować MyClass.Number bez żadnego obiektu.

Ale ponieważ Foo <int> nie jest tą samą klasą co Foo <string>, te dwie liczby nie są wspólne.

Przykład, aby to pokazać:

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

Console.WriteLine(TestClass<string>.Number);  //prints 5
Console.WriteLine(TestClass<int>.Number);     //prints 3
Znaki
źródło
-4

IMO, musisz to przetestować, ale tak myślę

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

wyświetli wynik, 1ponieważ myślę, że podczas kompilacji kompilator utworzy 1 klasę dla każdej klasy ogólnej, której używasz (w Twoim przykładzie: Foo<int>i Foo<string>).

Ale nie jestem w 100% pewien =).

Uwaga: myślę, że używanie tego rodzaju statycznych atrybutów nie jest dobrym projektem ani dobrą praktyką.

Clement Herreman
źródło
6
Jeśli nie w 100% - nie komentuj
sll Kwietnia