Як ValueTypes походять від Object (ReferenceType) і все ще залишаються ValueTypes?


83

C # не дозволяє структурам отримувати з класів, але всі ValueTypes походять від Object. Де це розмежування?

Як CLR справляється з цим?


Результат чорної магії System.ValueTypeтипу в системі типу CLR.
RBT

Відповіді:


108

C # не дозволяє структурам отримувати з класів

Ваше твердження неправильне, звідси і ваша плутанина. C # робить дозволяють витягати Структури з класів. Всі структури походять від одного класу System.ValueType, який походить від System.Object. І всі переліки походять від System.Enum.

ОНОВЛЕННЯ: У деяких (нині видалених) коментарях була певна плутанина, що вимагає роз'яснень. Я задам кілька додаткових питань:

Чи походять структури від базового типу?

Явно так. Ми можемо переконатися в цьому, прочитавши першу сторінку специфікації:

Усі типи C #, включаючи примітивні типи, такі як int та double, успадковуються від одного кореневого типу об'єкта.

Тепер я зауважу, що специфікація тут завищує випадок. Типи покажчиків не походять від об'єкта, і зв'язок виведення для типів інтерфейсу та типів параметрів типу є більш складним, ніж вказує цей ескіз. Однак очевидно, що це так, що всі типи структур походять від базового типу.

Чи існують інші способи, якими ми знаємо, що типи структур походять від базового типу?

Звичайно. Тип структури може замінити ToString. Що це переважно, як не віртуальний метод свого базового типу? Тому він повинен мати базовий тип. Цей базовий тип - це клас.

Чи можу я вивести визначену користувачем структуру з вибраного мною класу?

Явно ні. Це не означає, що структури не походять від класу . Структури походять від класу і тим самим успадковують спадкових членів цього класу. Насправді, структури повинні походити з певного класу: Enum повинні походити з Enum, структури повинні походити з ValueType. Оскільки вони необхідні , мова C # забороняє вказувати відношення виведення у коді.

Навіщо це забороняти?

Коли потрібні відносини , конструктор мов має варіанти: (1) вимагати від користувача ввести необхідне заклинання, (2) зробити його необов’язковим або (3) заборонити. У кожного є плюси і мінуси, і дизайнери мови C # вибрали по-різному, залежно від конкретних деталей кожного.

Наприклад, поля const повинні бути статичними, але забороняється стверджувати, що вони є такими, оскільки це по-перше, безглуздо словосполучення, а по-друге, означає, що існують нестатичні поля const. Але перевантажені оператори повинні бути позначені як статичні, навіть якщо розробник не має вибору; занадто легко розробникам повірити, що перевантаження оператора - це метод екземпляра в іншому випадку. Це перекриває занепокоєння щодо того, що користувач може прийти до думки, що "статичність" означає, що, скажімо, "віртуальність" також є можливою.

У цьому випадку вимагати від користувача сказати, що їх структура походить від ValueType, здається простим надмірним багатослів’ям, і це означає, що структура може походити від іншого типу. Щоб усунути обидві ці проблеми, C # робить незаконним вказувати в коді, що структура походить від базового типу, хоча, очевидно, так і робить.

Подібно всі типи делегатів походять від MulticastDelegate, але C # вимагає від вас не говорити цього.

Отже, зараз ми встановили, що всі структури в C # походять від класу .

Який взаємозв'язок між успадкуванням та виведенням з класу ?

Багатьох спантеличують відносини спадкування в C #. Відносини успадкування досить прості: якщо структура, клас або делегат типу D походить від класу типу B, то спадкові члени B також є членами D. Це так просто.

Що це означає щодо успадкування, коли ми говоримо, що структура походить від ValueType? Просто всі спадкові члени ValueType також є членами структури. Ось як структури отримують їх реалізацію ToString, наприклад; він успадковується від базового класу структури.

Усі члени, що успадковуються? Звичайно, ні. Чи є приватні члени спадкоємними?

Так. Усі приватні члени базового класу також є членами похідного типу. Звичайно, забороняється називати цих учасників іменами, якщо сайт дзвінків не знаходиться в домені доступності учасника. Те, що у вас є учасник, не означає, що ви можете ним скористатися!

Тепер ми продовжуємо з оригінальною відповіддю:


Як CLR справляється з цим?

Надзвичайно добре. :-)

Що робить тип значення типом значення, це те, що його екземпляри копіюються за значенням . Що робить тип посилання типом посилання, це те, що його екземпляри копіюються за допомогою посилання . Ви, здається, певно переконані, що відносини успадкування між типами цінностей та референтними типами є якимось особливим і незвичним, але я не розумію, що це за переконання. Спадщина не має нічого спільного з тим, як речі копіюються.

Подивіться на це так. Припустимо, я сказав вам наступні факти:

  • Є два види коробок, червоні та сині.

  • Кожен червоний ящик порожній.

  • Є три спеціальні сині коробки, що називаються O, V та E.

  • O не знаходиться всередині жодної коробки.

  • V всередині O.

  • Е знаходиться всередині V.

  • Жодної іншої синьої коробки всередині V.

  • Жодної синьої коробки всередині E.

  • Кожне червоне поле знаходиться у V або E.

  • Кожна синя коробка, крім O, сама по собі знаходиться всередині синьої коробки.

Сині поля - це посилальні типи, червоні поля - типи значень, O - System.Object, V - System.ValueType, E - System.Enum, а взаємозв'язок "inside" - "походить від".

Це цілком послідовний і зрозумілий набір правил, який ви могли б легко реалізувати самі, якби у вас було багато картону і багато терпіння. Чи є коробка червоною чи синьою, не має нічого спільного з тим, що вона знаходиться всередині; в реальному світі цілком можливо поставити червону коробку всередині синьої коробки. У CLR абсолютно законно створити тип значення, який успадковується від посилального типу, якщо це або System.ValueType, або System.Enum.

Тож давайте переформулюємо ваше запитання:

Як ValueTypes походять від Object (ReferenceType) і все ще залишаються ValueTypes?

як

Як можливо, що кожне червоне поле (типи значень) знаходиться всередині (походить від) вікна O (System.Object), яке є синім полем (тип посилання) і при цьому залишається червоним полем (тип значення)?

Коли ви формулюєте це так, я сподіваюся, це очевидно. Ніщо не заважає вам помістити червону коробку всередину коробки V, яка знаходиться всередині коробки O, яка є синьою. Чому б там бути?


ДОДАТКОВЕ ОНОВЛЕННЯ:

Первісне запитання Джоан було про те, як це можливощо тип значення походить від посилального типу. Моя оригінальна відповідь насправді не пояснила жодного механізму, який CLR використовує для того, щоб врахувати той факт, що ми маємо відносини виведення між двома речами, які мають абсолютно різні уявлення - а саме: чи мають дані, на які посилаються, заголовок об’єкта, a блок синхронізації, чи володіє він власним сховищем для збирання сміття тощо. Ці механізми складні, занадто складні, щоб пояснити їх однією відповіддю. Правила системи типу CLR є набагато складнішими, ніж дещо спрощений її смак, який ми бачимо в C #, де немає чіткого розрізнення, наприклад, в коробковій та безкартонній версіях типу. Впровадження дженериків також призвело до додаткової складності, яка додалася до ЗРЗ.


8
Конструкції мови повинні бути значущими. Що б це означало мати довільний тип значення, похідний від довільного типу посилання? Чи є щось, що ви могли б досягти за такою схемою, чого ви також не могли б виконати за допомогою визначених користувачем неявних перетворень?
Ерік Ліпперт,

2
Я думаю, що не. Я просто думав, що деякі члени можуть бути доступними для багатьох типів цінностей, які ви бачите як групу, що ви можете зробити, використовуючи абстрактний клас для отримання структури. Я думаю, ви могли б використовувати неявні перетворення, але тоді ви заплатили б штраф за продуктивність, так? Якщо ви робите їх мільйони.
Джоан Венге

8
А, бачу. Ви хочете використовувати успадкування не як механізм моделювання відносин "є свого роду", а просто як механізм спільного використання коду між цілою низкою пов'язаних типів. Це здається розумним сценарієм, хоча особисто я намагаюся уникати використання спадщини виключно як зручність спільного використання коду.
Ерік Ліпперт,

3
Джоан, щоб визначити поведінку один раз, ви можете створити інтерфейс, мати структури, якими ви хочете поділитися поведінкою, реалізувати інтерфейс, а потім створити метод розширення, що працює на інтерфейсі. Однією з потенційних проблем цього підходу є те, що при виклику методів інтерфейсу структура спочатку буде вставлена ​​в коробку, а скопійоване значення в коробці буде передано методу розширення. Будь-яка зміна стану відбуватиметься на копії об'єкта, що може бути неінтуїтивно зрозумілим для користувачів API.
Девід Сільва Сміт

2
@Sipo: Тепер, заради справедливості, питання включає "як CLR справляється з цим?" і відповідь добре описує, як CLR впроваджує ці правила. Але ось у чому річ: слід очікувати, що система, яка реалізує мову, не має тих самих правил, як мова! Системи впровадження обов'язково нижчого рівня, але давайте не будемо плутати правила цієї системи нижчого рівня з правилами системи високого рівня, побудованої на ній. Звичайно, система типу CLR робить різницю між типовими значеннями в коробці та без упаковки, як я зазначив у своїй відповіді. Але C # ні .
Ерік Ліпперт

21

Невелика корекція, C # не дозволяє структурам виходити з будь-чого, не лише з класів. Все, що може зробити структура, це реалізація інтерфейсу, який сильно відрізняється від виведення.

Я думаю, що найкращий спосіб відповісти на це ValueType- особливий. По суті, це базовий клас для всіх типів значень у системі типів CLR. Важко знати, як відповісти "як CLR це робить", оскільки це просто правило CLR.


+1 за хороший момент щодо структур, які ні з чого не походять [за винятком неявного походження з System.ValueType].
Рід Копсі,

2
Ви кажете, що ValueTypeце особливе, але варто прямо згадати, що ValueTypeсаме воно насправді є посилальним типом.
LukeH

Якщо внутрішньо можливо, щоб структури виходили з класу, чому вони не виставляють це для всіх?
Джоан Венге

1
@Joan: Вони насправді цього не роблять. Це просто для того, щоб ви могли прив’язати структуру до об’єкта, а там і для корисності. Але технічно, у порівнянні з тим, як реалізуються класи, CLR обробляє типи значень абсолютно по-різному.
Рід Копсі,

2
@JoanVenge Я вважаю, плутанина тут говорить про те, що структури походять від класу ValueType в CLR. Я вважаю, що правильніше стверджувати, що всередині CLR структури насправді не існують, реалізація "struct" всередині CLR насправді є класом ValueType. Отже, це не так, як структура успадковує від ValueType в CLR.
wired_in

20

Це дещо штучна конструкція, яку підтримує CLR, щоб дозволити всі типи оброблятись як System.Object.

Типи значень походять від System.Object через System.ValueType , де відбувається спеціальна обробка (тобто: CLR обробляє бокси / розпакування тощо для будь-якого типу, що походить від ValueType).


6

Ваше твердження неправильне, звідси і ваша плутанина. C # дозволяє структурам виходити з класів. Всі структури походять від одного класу System.ValueType

Тож спробуймо це:

 struct MyStruct :  System.ValueType
 {
 }

Це навіть не буде скомпільовано. Компілятор нагадуватиме вам "Введіть" System.ValueType "у списку інтерфейсів не є інтерфейсом".

При декомпіляції Int32, яка є структурою, ви знайдете:

відкрита структура Int32: IComparable, IFormattable, IConvertible {}, не кажучи вже про те, що вона походить від System.ValueType. Але в браузері об’єктів ви виявляєте, що Int32 успадковує від System.ValueType.

Тож усе це змушує мене повірити:

Я думаю, що найкращий спосіб відповісти на це - це значення ValueType. По суті, це базовий клас для всіх типів значень у системі типів CLR. Важко знати, як відповісти "як CLR це робить", оскільки це просто правило CLR.


Ті самі структури даних використовуються в .NET для опису вмісту типів значень та типів посилань, але коли CLR бачить визначення типу, яке визначається як похідне від ValueType, воно використовує це для визначення двох типів об'єктів: типу об'єкта купи, що поводиться як посилальний тип і тип місця зберігання, який фактично знаходиться поза системою успадкування типів. Оскільки ці два типи речей використовуються у взаємовиключних контекстах, дескриптори одного типу можна використовувати для обох. На рівні CLR структура визначається як клас, батьком якого є System.ValueType, але C # ...
supercat

... забороняє вказувати, що структури успадковують що-небудь, тому що існує лише одна річ, яку вони можуть успадкувати від ( System.ValueType), і забороняє класам вказувати, що вони успадковують, System.ValueTypeоскільки будь-який клас, який був оголошений таким чином, поводився б як тип значення.
supercat

3

Тип значення в коробці є фактично еталонним типом (він ходить як один, а шаркає як один, настільки ефективно). Я б припустив, що ValueType насправді не є базовим типом типів значень, а є базовим типом посилання, до якого типи значень можуть бути перетворені при передачі в тип Object. Типи значень, що не містяться в коробці, самі знаходяться поза ієрархією об’єктів.


1
Я думаю, ви маєте на увазі: "ValueType насправді не є базовим типом типів значень "
wired_in

1
@wired_in: Дякую. Виправлено.
supercat

2

Обґрунтування

З усіх відповідей відповідь @ supercat наближається до фактичної відповіді. Оскільки інші відповіді насправді не відповідають на питання, і прямо кажуть неправильні твердження (наприклад, що типи значень успадковуються від чого завгодно), я вирішив відповісти на питання.

 

Пролог

Ця відповідь базується на моєму власному зворотному проектуванні та специфікації CLI.

struct і class є ключовими словами C #. Що стосується CLI, то всі типи (класи, інтерфейси, структури тощо) визначаються визначеннями класів.

Наприклад, тип об'єкта (відомий в C # as class) визначається наступним чином:

.class MyClass
{
}

 

Інтерфейс визначається визначенням класу із interfaceсемантичним атрибутом:

.class interface MyInterface
{
}

 

А як щодо типів цінностей?

Причина, по якій структури можуть успадковувати System.ValueTypeі все ще бути типами цінностей, полягає в тому, що .. вони цього не роблять.

Типи значень - це прості структури даних. Типи значень ні з чого не успадковуються і не можуть реалізувати інтерфейси. Типи значень не є підтипами будь-якого типу, і вони не мають жодної інформації про тип. Враховуючи адресу пам'яті типу значення, неможливо визначити, що представляє тип значення, на відміну від посилального типу, який містить інформацію про тип у прихованому полі.

Якщо уявити собі таку структуру C #:

namespace MyNamespace
{
    struct MyValueType : ICloneable
    {
        public int A;
        public int B;
        public int C;

        public object Clone()
        {
            // body omitted
        }
    }
}

Далі наведено визначення класу IL цієї структури:

.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
    }
}

То що тут відбувається? Він чітко розширюється System.ValueType, що є типом об'єкта / посилання, і реалізує System.ICloneable.

Пояснення полягає в тому, що коли визначення класу розширюється, System.ValueTypeвоно насправді визначає 2 речі: тип значення та відповідний тип значення в коробці. Члени визначення класу визначають представлення як для типу значення, так і для відповідного типу в коробці. Це не тип значення, який розширює та реалізує, а відповідний тип у коробці. extends і implementsключових словах відносяться тільки до коробкового типу.

Для уточнення, визначення класу вище робить 2 речі:

  1. Визначає тип значення з 3 полями (і одним методом). Він не успадковує нічого і не реалізує жодних інтерфейсів (типи значень не можуть зробити ні те, ні інше).
  2. Визначає тип об'єкта (тип в коробці) з 3 полями (І реалізація одного методу інтерфейсу), успадкування System.ValueTypeта реалізаціяSystem.ICloneable інтерфейсу.

Також зауважте, що будь-яке розширення визначення класу System.ValueTypeтакож є внутрішньо герметичним, незалежно від того sealed, вказано ключове слово чи ні.

Оскільки типи значень - це просто прості структури, не успадковуйте, не реалізовуйте і не підтримуйте поліморфізм, їх не можна використовувати з рештою системи типів. Щоб обійти це, поверх типу значення, CLR також визначає відповідний тип посилання з тими ж полями, відомий як коробковий тип. Отже, хоча тип значення не може бути переданий методам, що приймають an object, його відповідний тип в коробці може .

 

Тепер, якби вам потрібно було визначити метод у C # like

public static void BlaBla(MyNamespace.MyValueType x),

Ви знаєте, що метод прийме тип значення MyNamespace.MyValueType.

Вище ми дізналися, що визначення класу, яке є результатом struct ключового слова в C #, насправді визначає як тип значення, так і тип об'єкта. Однак ми можемо посилатися лише на визначений тип значення. Незважаючи на те, що в специфікації CLI зазначено, що ключове слово constraint boxedможе використовуватися для посилання на коробку версії типу, це ключове слово не існує (див. ECMA-335, II.13.1 Типи посилань на значення). Але давайте уявимо, що це відбувається на мить.

Коли йдеться про типи в IL, підтримується кілька обмежень, серед яких є classі valuetype. Якщо ми використовуємо, valuetype MyNamespace.MyTypeми вказуємо визначення класу типу значення під назвою MyNamespace.MyType. Подібним чином ми можемо використовувати class MyNamespace.MyTypeдля визначення визначення класу типу об’єкта під назвою MyNamespace.MyType. Це означає, що в ІЛ ви можете мати тип значення (struct) та тип об’єкта (клас) з однаковим іменем і все одно розрізняти їх. Тепер, якби boxedключове слово, зазначене специфікацією CLI, насправді було реалізоване, ми змогли б його використовуватиboxed MyNamespace.MyType для вказівки на тип типу значення значення класу визначення MyNamespace.MyType.

Отже, .method static void Print(valuetype MyNamespace.MyType test) cil managedприймає тип значення, визначений визначенням класу типу значення з іменем MyNamespace.MyType,

while .method static void Print(class MyNamespace.MyType test) cil managedприймає тип об’єкта, визначений визначенням класу типу об’єкта з іменем MyNamespace.MyType.

так само, якби boxedбуло ключовим словом, .method static void Print(boxed MyNamespace.MyType test) cil managedвзяв би тип типу значення, визначений визначенням класу з іменем MyNamespace.MyType.

Ви б тоді бути в змозі створити екземпляр упакованого типу , як і будь-який інший тип об'єкту і передавати його будь-який спосіб , який приймає System.ValueType, objectабо в boxed MyNamespace.MyValueTypeякості аргументу, і це було б, для всіх намірів і цілей, робота , як і будь-якого іншого довідкового типу. Це НЕ тип значення, а відповідний тип типу значення в коробці.

 

Резюме

Отже, підсумовуючи і відповісти на питання:

Типи значень не є посилальними типами і не успадковуються від System.ValueTypeбудь-якого іншого типу, і вони не можуть реалізовувати інтерфейси. Відповідні типи в коробці , які також визначені , успадковують інтерфейси System.ValueTypeта можуть реалізовувати їх.

.classВизначення визначає різні дії в залежності від обставин.

  • Якщо interfaceвказано семантичний атрибут, визначення класу визначає інтерфейс.
  • Якщо interfaceсемантичний атрибут не вказаний, і визначення не поширюється System.ValueType, визначення класу визначає тип об'єкта (клас).
  • Якщо interfaceсемантичний атрибут не вказаний, а визначення дійсно поширюється System.ValueType, визначення класу визначає тип значення і його відповідний коробковий тип (структура).

Розмітка пам'яті

Цей розділ передбачає 32-розрядний процес

Як уже зазначалося, типи значень не мають інформації про тип, і, отже, неможливо визначити, що тип значення представляє за місцем його пам'яті. Структура описує простий тип даних і містить лише поля, які вона визначає:

public struct MyStruct
{
    public int A;
    public short B;
    public int C;
}

Якщо ми уявимо, що екземпляр MyStruct був виділений за адресою 0x1000, то це макет пам'яті:

0x1000: int A;
0x1004: short B;
0x1006: 2 byte padding
0x1008: int C;

Структури за замовчуванням для послідовного макетування. Поля вирівнюються за межами власного розміру. Для цього додається прокладка.

 

Якщо ми визначимо клас точно так само, як:

public class MyClass
{
    public int A;
    public short B;
    public int C;
}

Якщо уявити одну і ту ж адресу, макет пам'яті такий:

0x1000: Pointer to object header
0x1004: int A;
0x1008: int C;
0x100C: short B;
0x100E: 2 byte padding
0x1010: 4 bytes extra

Класи за замовчуванням мають автоматичне розміщення, і компілятор JIT розташує їх у найбільш оптимальному порядку. Поля вирівнюються за межами власного розміру. Для цього додається прокладка. Я не впевнений, чому, але в кінці кожного класу завжди є додаткові 4 байти.

Зсув 0 містить адресу заголовка об’єкта, який містить інформацію про тип, таблицю віртуальних методів тощо. Це дозволяє середовищу виконання ідентифікувати, що представляють дані за адресою, на відміну від типів значень.

Таким чином, типи значень не підтримують успадкування, інтерфейси та поліморфізм.

Методи

Типи значень не мають віртуальних таблиць методів і, отже, не підтримують поліморфізм. Однак їхній відповідний тип у коробці робить .

Коли ви маєте екземпляр структури і намагаєтесь викликати віртуальний метод, як ToString()визначено далі System.Object, час виконання повинен помітити структуру.

MyStruct myStruct = new MyStruct();
Console.WriteLine(myStruct.ToString()); // ToString() call causes boxing of MyStruct.

Однак, якщо структура перевизначає, ToString()тоді виклик буде статично прив'язаний, і час виконання буде викликати MyStruct.ToString()без боксу та без перегляду будь-яких віртуальних таблиць методів (структури не мають таких). З цієї причини він також може вбудовувати ToString()дзвінок.

Якщо структура перевизначає ToString()та встановлена ​​в поле, тоді виклик буде вирішено за допомогою таблиці віртуального методу.

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.

Однак пам’ятайте, що ToString()це визначено у структурі і, таким чином, оперує значенням структури, тому воно очікує тип значення. Тип коробки, як і будь-який інший клас, має заголовок об’єкта. Якби ToString()метод, визначений у структурі, викликався безпосередньо з boxed-типом у thisпокажчику, при спробі отримати доступ до поля Aв MyStruct, він отримав би доступ до зміщення 0, яке в boxed-типі було б покажчиком заголовка об’єкта. Отже, тип коробки має прихований метод, який робить фактичне перевизначення ToString(). Цей прихований метод розпаковує (лише обчислення адреси, як unboxінструкція IL) тип boxed, після чого статично викликає ToString()визначений у структурі.

Аналогічним чином, тип boxed має прихований метод для кожного реалізованого методу інтерфейсу, який робить те саме розпакування, а потім статично викликає метод, визначений у структурі.

 

Специфікація CLI

Бокс

I.8.2.4 Для кожного типу значення CTS визначає відповідний тип посилання, який називається коробковим типом. Зворотне не відповідає дійсності: загалом типи посилань не мають відповідного типу значення. Представлення значення типу boxed (boxed value) - це місце, де може зберігатися значення типу значення. Тип в коробці - це тип об’єкта, а значення в коробці - об’єкт.

Визначення типів цінностей

I.8.9.7 Не всі типи, визначені визначенням класу, є об'єктними типами (див. §I.8.2.3); зокрема, типи значень не є об’єктними типами, але вони визначаються за допомогою визначення класу. Визначення класу для типу значення визначає як тип значення (без упаковки), так і асоційований тип у коробці (див. §I.8.2.4). Члени визначення класу визначають представлення обох.

II.10.1.3 Семантичні атрибути типу визначають, чи повинен бути визначений інтерфейс, клас чи тип значення. Атрибут interface визначає інтерфейс. Якщо цього атрибута немає і визначення поширюється (прямо чи опосередковано) System.ValueType, а визначення не стосується System.Enum, слід визначити тип значення (§II.13). В іншому випадку повинен бути визначений клас (§II.11).

Типи значень не успадковуються

I.8.9.10 У безкартонній формі типи значень не успадковуються від жодного типу. Типи значень, що вкладаються, повинні успадковувати безпосередньо від System.ValueType, якщо вони не є переліченнями, і в цьому випадку вони успадковуватимуть від System.Enum. Типи цінних паперів повинні бути скріплені печаткою.

II.13 Типи значень без упаковки не вважаються підтипами іншого типу, і неприпустимо використовувати інструкцію isinst (див. Розділ III) для типів значень без упаковки. Однак інструкція isinst може бути використана для типів значень, що містяться в коробці.

I.8.9.10 Тип значення не успадковує; швидше базовий тип, зазначений у визначенні класу, визначає базовий тип коробчатого типу.

Типи значень не реалізують інтерфейси

I.8.9.7 Типи значень не підтримують інтерфейсні контракти, але пов'язані з ними типи, що містяться в коробці, підтримують.

II.13 Типи значень повинні реалізовувати нуль або більше інтерфейсів, але це має значення лише в коробковій формі (§II.13.3).

I.8.2.4 Інтерфейси та успадкування визначаються лише для посилальних типів. Таким чином, хоча визначення типу значення (§I.8.9.7) може вказувати як інтерфейси, які повинні бути реалізовані типом значення, так і клас (System.ValueType або System.Enum), від якого воно успадковується, вони стосуються лише значень, що містяться в коробці. .

Неіснуюче ключове слово в коробці

II.13.1 Посилання на форму типу цінності без упаковки має бути використано за допомогою ключового слова valueetype, за яким слідує посилання на тип. Форма типу значення, що знаходиться в коробці, повинна посилатися, використовуючи ключове слово з коробкою, а потім посилання на тип.

Примітка: Тут специфікація помилкова, boxedключового слова немає .

Епілог

Я думаю, що частина плутанини щодо того, як типи значень успадковуються, походить від того, що C # використовує синтаксис кастингу для виконання боксу та розпакування, через що здається, що ви виконуєте касти, що насправді не так (хоча, CLR викине InvalidCastException при спробі розпакувати неправильний тип). (object)myStructу C # створює новий екземпляр коробчатого типу типу значення; він не виконує жодних приводів. Так само, (MyStruct)objв C # розпаковує коробковий тип, копіюючи частину значення; він не виконує жодних приводів.


1
Нарешті, відповідь, яка чітко описує, як це працює! Цей заслуговує на прийняту відповідь. Хороша робота!
Just Shadow
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.