C # nie zezwala strukturom na pochodzenie z klas, ale wszystkie ValueTypes pochodzą z Object. Gdzie dokonuje się tego rozróżnienia?
Jak CLR sobie z tym radzi?
C # nie zezwala strukturom na pochodzenie z klas, ale wszystkie ValueTypes pochodzą z Object. Gdzie dokonuje się tego rozróżnienia?
Jak CLR sobie z tym radzi?
Odpowiedzi:
C # nie zezwala strukturom na pochodzenie z klas
Twoje stwierdzenie jest nieprawidłowe, stąd twoje zamieszanie. C # nie pozwalają kodowanym czerpać z klas. Wszystkie struktury pochodzą z tej samej klasy System.ValueType, która pochodzi od System.Object. Wszystkie wyliczenia pochodzą od System.Enum.
AKTUALIZACJA: W niektórych (obecnie usuniętych) komentarzach było pewne zamieszanie, które wymaga wyjaśnienia. Zadam dodatkowe pytania:
Czy struktury pochodzą z typu podstawowego?
Oczywiście tak. Widzimy to czytając pierwszą stronę specyfikacji:
Wszystkie typy C #, w tym typy pierwotne, takie jak int i double, dziedziczą z pojedynczego typu obiektu głównego.
Teraz zauważam, że specyfikacja wyolbrzymia tutaj przypadek. Typy wskaźników nie pochodzą z obiektu, a relacja wyprowadzenia dla typów interfejsów i typów parametrów typu jest bardziej złożona, niż wskazuje ten szkic. Jednak oczywiście jest tak, że wszystkie typy struktur wywodzą się z typu podstawowego.
Czy istnieją inne sposoby, dzięki którym wiemy, że typy struktur wywodzą się z typu podstawowego?
Pewnie. Typ struktury może przesłonić ToString
. Co zastępuje, jeśli nie jest metodą wirtualną swojego typu podstawowego? Dlatego musi mieć typ podstawowy. Ten typ bazowy to klasa.
Czy mogę wyprowadzić strukturę zdefiniowaną przez użytkownika z wybranej przeze mnie klasy?
Oczywiście nie. Nie oznacza to, że struktury nie pochodzą z klasy . Struktury wywodzą się z klasy iw ten sposób dziedziczą dziedziczone elementy tej klasy. W rzeczywistości struktury muszą pochodzić z określonej klasy: wyliczenia są wymagane, aby pochodzić z Enum
, struktury muszą pochodzić z ValueType
. Ponieważ są one wymagane , język C # zabrania określania relacji wyprowadzania w kodzie.
Dlaczego tego zabronić?
Gdy wymagana jest relacja , projektant języka ma opcje: (1) wymagaj od użytkownika wpisania wymaganej inkantacji, (2) uczyń ją opcjonalną lub (3) zabron. Każdy z nich ma zalety i wady, a projektanci języka C # wybrali inaczej w zależności od konkretnych szczegółów każdego z nich.
Na przykład pola const muszą być statyczne, ale nie wolno mówić, że tak jest, ponieważ po pierwsze jest to bezcelowe rozwlekanie, a po drugie, oznacza, że istnieją niestatyczne pola stałe. Jednak przeciążone operatory muszą być oznaczone jako statyczne, mimo że deweloper nie ma wyboru; programistom zbyt łatwo jest uwierzyć, że w przeciwnym razie przeciążenie operatora jest metodą instancji. Eliminuje to obawę, że użytkownik może uwierzyć, że „statyczny” oznacza, że „wirtualny” jest również możliwy.
W tym przypadku wymaganie od użytkownika, aby powiedział, że jego struktura wywodzi się z ValueType, wydaje się zwykłym nadmiarem słownictwa, a to oznacza, że struktura może pochodzić z innego typu. Aby wyeliminować oba te problemy, C # sprawia, że stwierdzenie w kodzie, że struktura pochodzi od typu podstawowego, jest nielegalne , chociaż oczywiście tak jest.
Podobnie wszystkie typy delegatów pochodzą z MulticastDelegate
, ale C # wymaga, abyś tego nie mówił.
Więc teraz ustaliliśmy, że wszystkie struktury w C # pochodzą z klasy .
Jaki jest związek między dziedziczeniem a wyprowadzeniem z klasy ?
Wiele osób jest zdezorientowanych relacją dziedziczenia w języku C #. Relacja dziedziczenia jest dość prosta: jeśli typ struktury, klasy lub delegata D wywodzi się z klasy B, wówczas dziedziczone elementy członkowskie B są również członkami D. To takie proste.
Co to oznacza w odniesieniu do dziedziczenia, gdy mówimy, że struktura pochodzi od ValueType? Po prostu wszyscy dziedziczni członkowie ValueType są również członkami struktury. W ten sposób struktury uzyskują implementację ToString
, na przykład; jest dziedziczony z klasy bazowej struktury.
Wszyscy dziedziczni członkowie? Na pewno nie. Czy członkowie prywatni są dziedziczeni?
Tak. Wszyscy prywatni członkowie klasy bazowej są również członkami typu pochodnego. Oczywiście, dzwonienie do tych członków po imieniu jest nielegalne, jeśli strona wywoławcza nie znajduje się w domenie dostępności członka. To, że masz członka, nie oznacza, że możesz go używać!
Kontynuujemy teraz pierwotną odpowiedź:
Jak CLR sobie z tym radzi?
Bardzo dobrze. :-)
To, co sprawia, że typ wartości jest typem wartości, polega na tym, że jego wystąpienia są kopiowane przez wartość . To, co sprawia, że typ referencyjny jest typem referencyjnym, polega na tym, że jego wystąpienia są kopiowane przez odwołanie . Wydaje się, że masz pewne przekonanie, że związek dziedziczenia między typami wartości i typami odniesienia jest w jakiś sposób wyjątkowy i niezwykły, ale nie rozumiem, czym jest to przekonanie. Dziedziczenie nie ma nic wspólnego ze sposobem kopiowania rzeczy.
Spójrz na to w ten sposób. Załóżmy, że przedstawiłem następujące fakty:
Istnieją dwa rodzaje pudełek, czerwone i niebieskie.
Każde czerwone pudełko jest puste.
Istnieją trzy specjalne niebieskie pola zwane O, V i E.
O nie ma w żadnym pudełku.
V jest wewnątrz O.
E jest wewnątrz V.
Żadne inne niebieskie pudełko nie znajduje się w V.
Wewnątrz E. nie ma niebieskiego pudełka.
Każde czerwone pole jest w V lub E.
Każde niebieskie pudełko inne niż O znajduje się w niebieskim pudełku.
Niebieskie pola to typy referencyjne, czerwone pola to typy wartości, O to System.Object, V to System.ValueType, E to System.Enum, a relacja „wewnętrzna” to „pochodzi z”.
To doskonale spójny i prosty zestaw reguł, które możesz łatwo wdrożyć samodzielnie, mając dużo kartonu i dużo cierpliwości. To, czy pudełko jest czerwone czy niebieskie, nie ma nic wspólnego z tym, co jest w środku; w prawdziwym świecie całkowicie możliwe jest umieszczenie czerwonego pudełka w niebieskim pudełku. W środowisku CLR jest całkowicie legalne utworzenie typu wartości, który dziedziczy po typie referencyjnym, o ile jest to System.ValueType lub System.Enum.
Więc przeformułujmy twoje pytanie:
W jaki sposób ValueTypes wywodzą się z Object (ReferenceType) i nadal są ValueTypes?
tak jak
Jak to możliwe, że każda czerwona ramka (typy wartości) znajduje się wewnątrz (pochodzi z) ramki O (System.Object), która jest niebieską ramką (typ odniesienia) i nadal jest czerwoną ramką (typ wartości)?
Kiedy tak to wyrażasz, mam nadzieję, że to oczywiste. Nic nie stoi na przeszkodzie, aby umieścić czerwone pudełko w pudełku V, które znajduje się w pudełku O, które jest niebieskie. Dlaczego miałoby być?
DODATKOWA AKTUALIZACJA:
Pierwotne pytanie Joan dotyczyło tego, jak to jest możliweże typ wartości pochodzi od typu referencyjnego. Moja pierwotna odpowiedź tak naprawdę nie wyjaśniła żadnego z mechanizmów używanych przez środowisko CLR do wyjaśnienia faktu, że mamy relację wyprowadzania między dwiema rzeczami, które mają zupełnie różne reprezentacje - a mianowicie, czy dane, do których się odwołujemy, mają nagłówek obiektu, sync block, niezależnie od tego, czy jest właścicielem własnego magazynu na potrzeby czyszczenia pamięci i tak dalej. Mechanizmy te są skomplikowane, zbyt skomplikowane, by można je było wyjaśnić w jednej odpowiedzi. Zasady systemu typów CLR są nieco bardziej złożone niż nieco uproszczony jego smak, który widzimy na przykład w C #, gdzie nie ma wyraźnego rozróżnienia między wersjami pudełkowymi i nieopakowanymi. Wprowadzenie leków generycznych spowodowało również dodanie dużej złożoności do CLR.
Niewielka poprawka, C # nie pozwala strukturom na niestandardowe pochodzenie z niczego, nie tylko z klas. Wszystko, co może zrobić struktura, to zaimplementować interfejs, który bardzo różni się od wyprowadzenia.
Myślę, że najlepszym sposobem odpowiedzi na to jest to, że ValueType
jest wyjątkowy. Zasadniczo jest to klasa bazowa dla wszystkich typów wartości w systemie typów CLR. Trudno wiedzieć, jak odpowiedzieć „jak CLR sobie z tym radzi”, ponieważ jest to po prostu reguła CLR.
ValueType
jest to wyjątkowe, ale warto wyraźnie wspomnieć, że ValueType
sam w sobie jest typem referencyjnym.
Jest to nieco sztuczna konstrukcja obsługiwana przez środowisko CLR, aby umożliwić traktowanie wszystkich typów jako System.Object.
Typy wartości pochodzą od System.Object do System.ValueType , czyli tam, gdzie występuje specjalna obsługa (tj .: środowisko CLR obsługuje pakowanie / rozpakowywanie itp. Dla dowolnego typu pochodzącego z ValueType).
Twoje stwierdzenie jest nieprawidłowe, stąd twoje zamieszanie. C # zezwala strukturom na pochodzenie z klas. Wszystkie struktury pochodzą z tej samej klasy System.ValueType
Więc spróbujmy tego:
struct MyStruct : System.ValueType
{
}
To się nawet nie skompiluje. Kompilator przypomni Ci, że „Typ„ System.ValueType ”na liście interfejsów nie jest interfejsem”.
Podczas dekompilacji struktury Int32, która jest strukturą, znajdziesz:
public struct Int32: IComparable, IFormattable, IConvertible {}, nie wspominając o tym, że pochodzi z System.ValueType. Ale w przeglądarce obiektów widać, że Int32 dziedziczy po System.ValueType.
Wszystko to prowadzi mnie do wiary:
Myślę, że najlepszym sposobem odpowiedzi na to jest to, że ValueType jest wyjątkowy. Zasadniczo jest to klasa bazowa dla wszystkich typów wartości w systemie typów CLR. Trudno wiedzieć, jak odpowiedzieć „jak CLR sobie z tym radzi”, ponieważ jest to po prostu reguła CLR.
ValueType
, używa jej do zdefiniowania dwóch rodzajów obiektów: typu obiektu sterty, który zachowuje się jak typ referencyjny i typ miejsca przechowywania, który faktycznie znajduje się poza systemem dziedziczenia typów. Ponieważ te dwa rodzaje rzeczy są używane w wzajemnie wykluczających się kontekstach, te same deskryptory typów mogą być używane do obu. Na poziomie CLR struktura jest definiowana jako klasa, której rodzicem jest System.ValueType
, ale C # ...
System.ValueType
), i zabrania klasom określania, że dziedziczą z, System.ValueType
ponieważ każda klasa, która została w ten sposób zadeklarowana, zachowywałaby się jak typ wartości.
Typ wartości w ramce jest w rzeczywistości typem referencyjnym (chodzi jak jeden i kwacze jak jeden, więc skutecznie jest jednym). Sugerowałbym, że ValueType nie jest w rzeczywistości typem podstawowym typów wartości, ale raczej jest podstawowym typem odniesienia, do którego typy wartości mogą być konwertowane podczas rzutowania na typ Object. Same typy wartości bez pudełek znajdują się poza hierarchią obiektów.
Ze wszystkich odpowiedzi odpowiedź @ supercat jest najbliższa rzeczywistej odpowiedzi. Ponieważ inne odpowiedzi tak naprawdę nie odpowiadają na pytanie i wręcz zawierają nieprawidłowe twierdzenia (na przykład, że typy wartości dziedziczą po wszystkim), postanowiłem odpowiedzieć na to pytanie.
Ta odpowiedź jest oparta na mojej własnej inżynierii wstecznej i specyfikacji CLI.
struct
i class
są słowami kluczowymi C #. Jeśli chodzi o CLI, wszystkie typy (klasy, interfejsy, struktury itp.) Są zdefiniowane w definicjach klas.
Na przykład typ obiektu (znany w C # jako class
) jest zdefiniowany w następujący sposób:
.class MyClass
{
}
Interfejs jest definiowany przez definicję klasy z interface
atrybutem semantycznym:
.class interface MyInterface
{
}
Powodem, dla którego struktury mogą dziedziczyć System.ValueType
i nadal być typami wartości, jest to, że ... tak nie jest.
Typy wartości to proste struktury danych. Typy wartości nie dziedziczą z niczego i nie mogą implementować interfejsów. Typy wartości nie są podtypami żadnego typu i nie zawierają żadnych informacji o typie. Biorąc pod uwagę adres pamięci typu wartości, nie można zidentyfikować, co reprezentuje typ wartości, w przeciwieństwie do typu referencyjnego, który zawiera informacje o typie w ukrytym polu.
Jeśli wyobrazimy sobie następującą strukturę C #:
namespace MyNamespace
{
struct MyValueType : ICloneable
{
public int A;
public int B;
public int C;
public object Clone()
{
// body omitted
}
}
}
Poniżej znajduje się definicja klasy IL tej struktury:
.class MyNamespace.MyValueType extends [mscorlib]System.ValueType implements [mscorlib]System.ICloneable
{
.field public int32 A;
.field public int32 B;
.field public int32 C;
.method public final hidebysig newslot virtual instance object Clone() cil managed
{
// body omitted
}
}
Więc co się tutaj dzieje? Wyraźnie rozszerza System.ValueType
, który jest typem obiektowym / referencyjnym, i implementuje System.ICloneable
.
Wyjaśnienie jest takie, że kiedy definicja klasy się rozszerza System.ValueType
, w rzeczywistości definiuje dwie rzeczy: typ wartości i odpowiadający mu typ pudełkowy. Elementy członkowskie definicji klasy definiują reprezentację zarówno typu wartości, jak i odpowiedniego typu pudełkowego. Nie jest to typ wartości, który rozszerza i implementuje, jest to odpowiedni typ pudełkowy. extends
iimplements
słowa kluczowe odnoszą się tylko do wersji pudełkowej typu.
Aby wyjaśnić, powyższa definicja klasy ma 2 rzeczy:
System.ValueType
i implementując System.ICloneable
interfejs.Należy również zauważyć, że każde rozszerzenie definicji klasy System.ValueType
jest również wewnętrznie zapieczętowane, niezależnie od tego, czysealed
słowo kluczowe jest określone, czy nie.
Ponieważ typy wartości to tylko proste struktury, nie dziedziczą, nie implementują i nie obsługują polimorfizmu, nie można ich używać z resztą systemu typów. Aby obejść ten problem, oprócz typu wartości środowisko CLR definiuje również odpowiedni typ odwołania z tymi samymi polami, zwany typem pudełkowym. Więc gdy typ wartości nie mogą być przekazywane wokół metod biorących object
, odpowiadającej jej pudełkowej typu puszki .
Teraz, gdybyś miał zdefiniować metodę w C # jak
public static void BlaBla(MyNamespace.MyValueType x)
,
wiesz, że metoda przyjmie typ wartości MyNamespace.MyValueType
.
Powyżej dowiedzieliśmy się, że definicja klasy, która wynika ze struct
słowa kluczowego w języku C #, w rzeczywistości definiuje zarówno typ wartości, jak i typ obiektu. Możemy jednak odnosić się tylko do zdefiniowanego typu wartości. Mimo że specyfikacja CLI stwierdza, że słowo kluczowe constraint boxed
może być użyte do odniesienia się do pudełkowej wersji typu, to słowo kluczowe nie istnieje (patrz ECMA-335, II.13.1 Odwoływanie się do typów wartości). Ale wyobraźmy sobie przez chwilę, że tak.
W przypadku odwoływania się do typów w języku IL obsługiwanych jest kilka ograniczeń, wśród których są class
i valuetype
. Jeśli używamy, valuetype MyNamespace.MyType
określamy definicję klasy typu wartości o nazwie MyNamespace.MyType. Podobnie możemy użyć class MyNamespace.MyType
do określenia definicji klasy obiektu o nazwie MyNamespace.MyType. Oznacza to, że w IL możesz mieć typ wartości (struct) i typ obiektu (klasa) o tej samej nazwie i nadal je rozróżniać. Teraz, gdyby boxed
słowo kluczowe wskazane w specyfikacji CLI zostało faktycznie zaimplementowane, moglibyśmy użyćboxed MyNamespace.MyType
do określenia typu pudełkowego definicji klasy wartości o nazwie MyNamespace.MyType.
Tak więc .method static void Print(valuetype MyNamespace.MyType test) cil managed
przyjmuje typ wartości zdefiniowany przez definicję klasy typu o nazwie MyNamespace.MyType
,
while .method static void Print(class MyNamespace.MyType test) cil managed
przyjmuje typ obiektu zdefiniowany przez definicję klasy typu obiektu o nazwie MyNamespace.MyType
.
podobnie gdyby boxed
było słowem kluczowym, .method static void Print(boxed MyNamespace.MyType test) cil managed
przyjąłby typ pudełkowy typu wartości zdefiniowanego przez definicję klasy o nazwie MyNamespace.MyType
.
Można by następnie móc instancji pudełkowej typ jak każdy inny rodzaj obiektu i przekazać ją wokół każdej metody, które wykonuje System.ValueType
, object
lub boxed MyNamespace.MyValueType
jako argument, i to będzie dla wszystkich zamiarów i celów, pracy jak każdy inny rodzaj odniesienia. NIE jest to typ wartości, ale odpowiadający mu typ pudełkowy typu wartości.
Podsumowując, i odpowiadając na pytanie:
Typy wartości nie są typami odwołań i nie dziedziczą System.ValueType
ani z żadnego innego typu, ani nie mogą implementować interfejsów. Odpowiednie typy pudełkowe , które również są zdefiniowane , dziedziczą System.ValueType
i mogą implementować interfejsy.
.class
Definicja określa różne rzeczy w zależności od okoliczności.
interface
atrybut semantyczny, definicja klasy definiuje interfejs.interface
atrybut semantyczny nie jest określony, a definicja się nie rozszerza System.ValueType
, definicja klasy definiuje typ obiektu (klasę).interface
atrybut semantyczny nie jest określony, a definicja się rozszerza System.ValueType
, definicja klasy definiuje typ wartości i odpowiadający mu typ pudełkowy (struct).W tej sekcji przyjęto proces 32-bitowy
Jak już wspomniano, typy wartości nie zawierają informacji o typie, a zatem nie można zidentyfikować, co reprezentuje typ wartości na podstawie jego lokalizacji w pamięci. Struktura opisuje prosty typ danych i zawiera tylko pola, które definiuje:
public struct MyStruct
{
public int A;
public short B;
public int C;
}
Jeśli wyobrazimy sobie, że instancja MyStruct została przydzielona pod adresem 0x1000, to jest to układ pamięci:
0x1000: int A;
0x1004: short B;
0x1006: 2 byte padding
0x1008: int C;
Struktury domyślnie mają układ sekwencyjny. Pola są wyrównane do granic własnego rozmiaru. Aby to spełnić, dodano wypełnienie.
Jeśli zdefiniujemy klasę dokładnie w ten sam sposób, jak:
public class MyClass
{
public int A;
public short B;
public int C;
}
Wyobrażając sobie ten sam adres, układ pamięci wygląda następująco:
0x1000: Pointer to object header
0x1004: int A;
0x1008: int C;
0x100C: short B;
0x100E: 2 byte padding
0x1010: 4 bytes extra
Klasy mają domyślnie układ automatyczny, a kompilator JIT ułoży je w najbardziej optymalnej kolejności. Pola są wyrównane do granic własnego rozmiaru. Aby to spełnić, dodano wypełnienie. Nie wiem dlaczego, ale każda klasa ma zawsze na końcu dodatkowe 4 bajty.
Przesunięcie 0 zawiera adres nagłówka obiektu, który zawiera informacje o typie, wirtualną tabelę metod itp. Pozwala to środowisku wykonawczemu zidentyfikować, co reprezentują dane pod adresem, w przeciwieństwie do typów wartości.
Dlatego typy wartości nie obsługują dziedziczenia, interfejsów ani polimorfizmu.
Typy wartości nie mają tabel metod wirtualnych, a zatem nie obsługują polimorfizmu. Jednak ich odpowiedni typ pudełkowy tak .
Kiedy masz instancję struktury i próbujesz wywołać metodę wirtualną, taką jak ToString()
zdefiniowano w System.Object
, środowisko wykonawcze musi opakować strukturę.
MyStruct myStruct = new MyStruct();
Console.WriteLine(myStruct.ToString()); // ToString() call causes boxing of MyStruct.
Jednakże, jeśli struktura nadpisuje, ToString()
wówczas wywołanie będzie statycznie związane, a środowisko uruchomieniowe będzie wywoływać MyStruct.ToString()
bez pakowania i bez zaglądania do żadnych tabel metod wirtualnych (struktury nie mają żadnych). Z tego powodu jest również w stanie wbudować ToString()
połączenie.
Jeśli struktura zastępuje ToString()
i jest zapakowana, wywołanie zostanie rozwiązane przy użyciu tabeli metod wirtualnych.
System.ValueType myStruct = new MyStruct(); // Creates a new instance of the boxed type of MyStruct.
Console.WriteLine(myStruct.ToString()); // ToString() is now called through the virtual method table.
Pamiętaj jednak, że ToString()
jest to zdefiniowane w strukturze, a zatem działa na wartości struktury, więc oczekuje typu wartości. Typ pudełkowy, jak każda inna klasa, ma nagłówek obiektu. Gdyby ToString()
metoda zdefiniowana w strukturze została wywołana bezpośrednio z typem pudełkowym we this
wskaźniku, przy próbie dostępu do pola A
w MyStruct
, uzyskałaby dostęp do offsetu 0, który w typie pudełkowym byłby wskaźnikiem nagłówka obiektu. Tak więc typ pudełkowy ma ukrytą metodę, która faktycznie zastępujeToString()
. Ta ukryta metoda odpakowuje (tylko obliczanie adresu, podobnie jak unbox
instrukcja IL) typ pudełkowy, a następnie statycznie wywołuje ToString()
zdefiniowany w strukturze.
Podobnie, typ pudełkowy ma ukrytą metodę dla każdej zaimplementowanej metody interfejsu, która wykonuje to samo rozpakowywanie, a następnie statycznie wywołuje metodę zdefiniowaną w strukturze.
I.8.2.4 Dla każdego typu wartości CTS definiuje odpowiadający mu typ odniesienia zwany typem pudełkowym. Odwrotna sytuacja nie jest prawdą: ogólnie typy odwołań nie mają odpowiedniego typu wartości. Reprezentacja wartości typu pudełkowego (wartość w pudełku) to lokalizacja, w której można przechowywać wartość typu wartości. Typ pudełkowy to typ obiektu, a wartość w pudełku to obiekt.
I.8.9.7 Nie wszystkie typy zdefiniowane w definicji klasy są typami obiektów (patrz §I.8.2.3); w szczególności typy wartości nie są typami obiektów, ale są definiowane przy użyciu definicji klasy. Definicja klasy dla typu wartości definiuje zarówno (bez pudełka) typ wartości, jak i powiązany typ pudełkowy (patrz §I.8.2.4). Członkowie definicji klasy definiują reprezentację obu.
II.10.1.3 Atrybuty semantyczne typu określają, czy należy zdefiniować interfejs, klasę czy typ wartości. Atrybut interfejsu określa interfejs. Jeśli ten atrybut nie jest obecny, a definicja rozszerza (bezpośrednio lub pośrednio) System.ValueType, a definicja nie dotyczy System.Enum, należy zdefiniować typ wartości (§II.13). W przeciwnym razie określa się klasę (§II.11).
I.8.9.10 W formie rozpakowanej typy wartości nie dziedziczą po żadnym typie. Typy wartości w pudełkach dziedziczą bezpośrednio po System.ValueType, chyba że są wyliczeniami, w takim przypadku dziedziczą po System.Enum. Typy wartości w pudełkach są zapieczętowane.
II.13 Typy wartości bez pudełka nie są uważane za podtypy innego typu i nie jest właściwe stosowanie instrukcji isinst (patrz część III) w przypadku typów wartości bez pudełka. Instrukcja isinst może być jednak używana dla typów wartości w pudełkach.
I.8.9.10 Typ wartości nie dziedziczy; raczej typ podstawowy określony w definicji klasy definiuje typ podstawowy typu pudełkowego.
I.8.9.7 Typy wartości nie obsługują kontraktów interfejsowych, ale powiązane z nimi typy pudełkowe tak.
II.13 Typy wartości muszą implementować zero lub więcej interfejsów, ale ma to znaczenie tylko w formie pudełkowej (§II.13.3).
I.8.2.4 Interfejsy i dziedziczenie definiuje się tylko dla typów referencyjnych. Tak więc, chociaż definicja typu wartości (§I.8.9.7) może określać zarówno interfejsy, które mają być implementowane przez typ wartości, jak i klasę (System.ValueType lub System.Enum), z której dziedziczy, mają one zastosowanie tylko do wartości opakowanych. .
II.13.1 Odniesienie do otwartej formy typu wartości następuje za pomocą słowa kluczowego valueetype, po którym następuje odniesienie do typu. Forma pudełkowa typu wartości powinna być określana za pomocą słowa kluczowego w ramce, po którym następuje odniesienie do typu.
Uwaga: tutaj specyfikacja jest nieprawidłowa, nie ma boxed
słowa kluczowego.
Myślę, że część zamieszania związanego z tym, jak typy wartości wydają się dziedziczyć, wynika z faktu, że C # używa składni rzutowania do wykonywania pudełek i rozpakowywania, co sprawia, że wygląda na to, że wykonujesz rzutowania, co tak naprawdę nie ma miejsca (chociaż CLR zgłosi InvalidCastException w przypadku próby rozpakowania niewłaściwego typu).
(object)myStruct
w C # tworzy nowe wystąpienie typu pudełkowego typu wartości; nie wykonuje żadnych rzutów. Podobnie, (MyStruct)obj
w C # unboxing a boxed type, copy the value part out; nie wykonuje żadnych rzutów.
System.ValueType
czcionek w systemie typów CLR.