Dlaczego struktury nie wspierają dziedziczenia?

Wiem, że struktury w. Net nie obsługują dziedziczenia, ale nie jest do końca jasne Dlaczego są w ten sposób ograniczone.

Jaki techniczny powód uniemożliwia dziedziczenie struktur PO innych strukturach?

Author: Carl Manaster, 2009-08-03

10 answers

Powodem, dla którego typy wartości nie mogą obsługiwać dziedziczenia, są tablice.

Problem polega na tym, że ze względu na wydajność i GC tablice typów wartości są przechowywane "inline". Na przykład, biorąc pod uwagę new FooType[10] {...}, Jeśli FooType jest typem referencyjnym, na stercie zarządzanej zostanie wytworzonych 11 obiektów (jeden dla tablicy i 10 dla każdej instancji typu). Jeśli FooType jest typem wartości, na stercie zarządzanej zostanie utworzona tylko jedna instancja -- dla samej tablicy ( ponieważ każda wartość tablicy będzie przechowywana "inline" z tablicą).

Załóżmy, że mamy dziedziczenie z typami wartości. W połączeniu z powyższym zachowaniem "inline storage" tablic, złe rzeczy się dzieją, co widać W C++.

Rozważ ten pseudo-kod C#:

struct Base
{
    public int A;
}

struct Derived : Base
{
    public int B;
}

void Square(Base[] values)
{
  for (int i = 0; i < values.Length; ++i)
      values [i].A *= 2;
}

Derived[] v = new Derived[2];
Square (v);

Według normalnych reguł konwersji, {[5] } jest zamieniana na Base[] (na dobre i złe), więc jeśli s / struct / class / g dla powyższego przykładu, skompiluje się i uruchomi zgodnie z oczekiwaniami, bez żadnych problemów. Ale jeśli Base i Derived są wartością typy i tablice przechowują wartości w linii, wtedy mamy problem.

Mamy problem, ponieważ Square() nie wie nic o Derived, używa tylko arytmetyki wskaźników, aby uzyskać dostęp do każdego elementu tablicy, zwiększając ją o stałą wartość (sizeof(A)). Zgromadzenie byłoby niejasno jak:

for (int i = 0; i < values.Length; ++i)
{
    A* value = (A*) (((char*) values) + i * sizeof(A));
    value->A *= 2;
}

(Tak, to obrzydliwe Zgromadzenie, ale chodzi o to, że będziemy zwiększać przez tablicę o znanych stałych czasu kompilacji, bez żadnej wiedzy, że typ Pochodny jest używany.)

Więc, gdyby to się stało, mielibyśmy problemy z uszkodzeniem pamięci. W szczególności, w ramach Square(), values[1].A*=2 czy faktycznie być modyfikowanie values[0].B!

Spróbuj debugować to !

 123
Author: jonp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-09 11:53:04

Imagine structs supported inheritance. Następnie deklaruje:

BaseStruct a;
InheritedStruct b; //inherits from BaseStruct, added fields, etc.

a = b; //?? expand size during assignment?

Oznaczałoby, że zmienne struct nie mają stałego rozmiaru i dlatego mamy typy referencyjne.

Jeszcze lepiej, rozważ to:

BaseStruct[] baseArray = new BaseStruct[1000];

baseArray[500] = new InheritedStruct(); //?? morph/resize the array?
 71
Author: Kenan E. K.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-03 20:54:28

Struktury nie używają referencji (chyba że są pudełkowe, ale powinieneś tego unikać), więc polimorfizm nie ma znaczenia, ponieważ nie ma indrection za pomocą wskaźnika referencyjnego. Obiekty zwykle żyją na stercie i są odwoływane za pomocą wskaźników referencyjnych, ale struktury są przydzielane na stosie (chyba że są pudełkowe) lub są przydzielane "wewnątrz" pamięci zajmowanej przez typ referencyjny na stercie.

 15
Author: Martin Liversage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-14 10:48:20

Oto co docs mówią:

Struktury są szczególnie przydatne dla małych struktur danych, które mają semantykę wartości. Liczby złożone, punkty w układzie współrzędnych lub pary klucz-wartość w słowniku są dobrymi przykładami struktur. Kluczowe dla tych struktur danych jest to, że mają niewiele elementów danych, że nie wymagają one użycia dziedziczenia lub tożsamości referencyjnej, i że mogą być wygodnie zaimplementowane przy użyciu semantyki wartości, gdzie przypisanie kopiuje wartość zamiast odniesienia.

Zasadniczo mają one zawierać proste Dane i dlatego nie mają "dodatkowych funkcji", takich jak dziedziczenie. Prawdopodobnie byłoby technicznie możliwe dla nich wsparcie jakiegoś ograniczonego rodzaju dziedziczenia (nie polimorfizm, ze względu na to, że są na stosie), ale uważam, że jest to również wybór projektowy, aby nie wspierać dziedziczenia (jak wiele innych rzeczy w językach.NET są.)

Z drugiej strony zgadzam się z korzyściami z dziedziczenia, i myślę, że wszyscy doszliśmy do punktu, w którym chcemy, aby nasz struct odziedziczył po innym, i zdaliśmy sobie sprawę, że to niemożliwe. Ale w tym momencie struktura danych jest prawdopodobnie tak zaawansowana, że i tak powinna być klasą.
 8
Author: Blixt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-03 15:37:10

Dziedziczenie klasy nie jest możliwe, ponieważ struktura jest układana bezpośrednio na stosie. Dziedzicząca struktura byłaby większa niż rodzic, ale JIT o tym nie wie i stara się umieścić za dużo na zbyt mniejszej przestrzeni. Brzmi trochę niejasno, napiszmy przykład:

struct A {
    int property;
} // sizeof A == sizeof int

struct B : A {
    int childproperty;
} // sizeof B == sizeof int * 2

Jeśli byłoby to możliwe, rozbiłoby się na poniższym fragmencie:

void DoSomething(A arg){};

...

B b;
DoSomething(b);

Przestrzeń jest przydzielana dla rozmiaru A, nie dla rozmiaru B.

 5
Author: Dykam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-03 15:32:55

Struktury są przydzielane na stosie. Oznacza to, że semantyka wartości jest w dużej mierze darmowa, a dostęp do elementów struct jest bardzo tani. Nie zapobiega to polimorfizmowi.

Każda struktura może zaczynać się od wskaźnika do jej tabeli funkcji wirtualnych. Byłoby to problemem z wydajnością (każda struktura byłaby co najmniej wielkości wskaźnika), ale jest to wykonalne. Umożliwiłoby to wykonywanie funkcji wirtualnych.

A co z dodawaniem pól?

Cóż, kiedy przydzielisz strukturę na stack, przydzielasz pewną ilość miejsca. Wymagana przestrzeń jest określana w czasie kompilacji (przed czasem lub podczas Jittingu). Jeśli dodasz pola, a następnie przypisz do typu bazowego:

struct A
{
    public int Integer1;
}

struct B : A
{
    public int Integer2;
}

A a = new B();

To nadpisze jakąś nieznaną część stosu.

Alternatywą jest, aby runtime zapobiegał temu tylko przez zapis sizeof (a) bajtów do dowolnej zmiennej A.

Co się stanie, jeśli B nadpisuje metodę w A i odwołuje się do jej pola Integer2? Albo runtime rzuca MemberAccessException, czyli metoda uzyskuje dostęp do losowych danych na stosie. Żadne z nich nie jest dozwolone.

Dziedziczenie struktur jest całkowicie bezpieczne, o ile nie używa się struktur polimorficznych lub o ile nie dodaje się pól podczas dziedziczenia. Ale te nie są zbyt przydatne.

 5
Author: user38001,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-03 22:23:21

Jest kwestia, którą chciałbym poprawić. Mimo że powodem, dla którego struktury nie mogą być dziedziczone, jest to, że żyją na stosie, jest to jednocześnie w połowie poprawne Wyjaśnienie. Struktury, jak każdy inny typ wartości Mogą żyć w stosie. Ponieważ będzie zależeć od tego, gdzie zmienna zostanie zadeklarowana, będą one żyć w stosie lub w stosie . Dzieje się tak, gdy są to odpowiednio zmienne lokalne lub pola instancji.

Mówiąc, że, Cecil ma poprawne Imię.

Chciałbym to podkreślić, typy wartości Mogą żyć na stosie. To nie znaczy, że zawsze tak robią. Zmienne lokalne, w tym parametry metody, będą. Wszyscy inni nie. Niemniej jednak nadal pozostaje powodem, dla którego nie można ich odziedziczyć. :-)

 3
Author: Rui Craveiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-03 15:45:28

To wydaje się być bardzo częste pytanie. Mam ochotę dodać, że typy wartości są przechowywane "w miejscu", w którym deklarujesz zmienną; oprócz szczegółów implementacji, oznacza to, że nie ma nagłówka obiektu, który mówi coś o obiekcie, tylko zmienna wie, jakiego rodzaju dane tam się znajdują.

 2
Author: Cecil Has a Name,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-03 15:36:06

Struktury obsługują interfejsy, więc można w ten sposób zrobić kilka polimorficznych rzeczy.

 1
Author: Wyatt Barnett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-03 16:05:49

IL jest językiem stosowym, więc wywołanie metody z argumentem przebiega mniej więcej tak:

  1. wciśnij argument na stos
  2. Wywołanie metody.

Gdy metoda jest uruchomiona, wyskakuje kilka bajtów ze stosu, aby uzyskać jej argument. Wie dokładnie ile bajtów ma się wyskakiwać, ponieważ argument jest albo wskaźnikiem typu referencyjnego (zawsze 4 bajty na 32-bitach), albo typem wartości, dla którego rozmiar jest zawsze dokładnie znany.

Jeśli jest to wskaźnik typu referencyjnego następnie metoda wyszukuje obiekt w stercie i otrzymuje jego uchwyt typu, który wskazuje na tabelę metod, która obsługuje daną metodę dla tego dokładnego typu. Jeżeli jest to typ wartości, wtedy nie jest konieczne wyszukiwanie do tabeli metod, ponieważ typy wartości nie obsługują dziedziczenia, więc jest tylko jedna możliwa kombinacja metoda / typ.

Jeśli typy wartości obsługują dziedziczenie, to będzie dodatkowy narzut w tym, że dany typ struktury musiałby umieszczone na stosie, jak również jego wartość, co oznaczałoby jakieś wyszukiwanie w tabeli metod dla konkretnej konkretnej instancji tego typu. Wyeliminowałoby to zalety szybkości i wydajności typów wartości.

 0
Author: Matt Howells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-03 15:48:18