Почему нет проблем с циклическим макетом для классов в C#?

public struct Unit
{
    Unit u;
}

Причины:

Член структуры «Unit.u» типа «Unit» вызывает цикл в макете структуры.

Но

public class Unit
{
    Unit u;
}

компилирует. Я понимаю проблему, я полагаю. При обращении к объекту Unit будет формироваться бесконечный цикл, так как ему придется инициализировать другой элемент Unit и так далее. Но почему компилятор ограничивает проблему только для structs? Разве проблема не сохраняется и для class? Я что-то упускаю?


person nawfal    schedule 13.01.2013    source источник


Ответы (4)


Проблема заключается в макете.

Когда Unit является структурой, любое значение для Unit должно содержать другое значение того же типа (и, следовательно, того же размера), до бесконечности. Это невозможно. Я полагаю, вы могли бы возразить, что без других полей поля для Unit не должны занимать памяти, поэтому вы могли бы содержать их внутри себя, но я считаю, что способ работы CLR гарантирует, что все структуры занимают хотя бы 1 байт...

Когда Unit является классом, объект Unit должен содержать только ссылку на другой объект Unit. Нет проблем с хранением, и значение может быть нулевым для начала.

Подумайте об этом так: у вас не может быть дома, в котором есть другой дом, построенный по тому же чертежу, но вы, безусловно, можете иметь дом, в котором есть лист бумаги с адресом такого же дома...

person Jon Skeet    schedule 13.01.2013
comment
@Jon, пример дома действительно великолепен! :) - person nawfal; 14.01.2013
comment
Кратко объяснил в последнем бите. +1 - person Sion Sheevok; 14.01.2013
comment
О боже, когда я отвечаю на тот же вопрос с Джоном, мой ответ выглядит бессмысленным. Черт! - person Soner Gönül; 14.01.2013
comment
Правильно ли говорить, что Единица содержит себя? Он не содержит себя, но содержит другую Единицу. Просто Единица содержит Единицу, содержащую Единицу... до бесконечности. - person Dave Cousineau; 14.01.2013
comment
@Sahuagin да, ты прав. Джон может отредактировать его. Но смысл в любом случае близок :) - person nawfal; 14.01.2013
comment
@Sahuagin: любое отдельное значение содержит другое значение того же типа. Будет отредактировано, чтобы сделать формулировку несколько более пуленепробиваемой. - person Jon Skeet; 14.01.2013
comment
@Sahuagin: Джон, конечно, прав; чтобы проиллюстрировать это, рассмотрим встроенные структуры. Вы не можете сказать struct Int32 { private Int32 value; }, но с точки зрения CLR Int32 — это именно структура, единственным полем которой является Int32. Очевидно, чтобы это было разумно, CLR должна иметь специальные знания: что Int32 имеет четыре байта. У него нет таких специальных знаний о рекурсивно определенной структуре оригинального плаката. - person Eric Lippert; 14.01.2013
comment
@EricLippert: Я очень чуть не упомянул об этом (я начал писать правку, а потом отказался от нее), когда она появилась на CodeMash. Я не был уверен, насколько публично это должно было быть, хотя :) - person Jon Skeet; 14.01.2013

Классы являются ссылочными типами, поэтому я полагаю, что разница в том, что в примере class нужно только сохранить ссылку на другой экземпляр. Для вашего struct, типа значения, потребуется снова включить всю структуру, следовательно, бесконечный цикл.

person Paul Phillips    schedule 13.01.2013

struct автоматически инициализируется значением, поэтому в вашем примере значение Unit содержит значение Unit, которое содержит значение Unit, которое содержит значение Unit и т. д.

В вашем примере class инициализируется только ссылка на null, которая не имеет бесконечного регресса. Однако вы столкнулись бы с аналогичной проблемой, если бы сделали следующее:

class Unit
{
   Unit mUnit = new Unit();
}

Теперь построение Unit создает Unit, который создает Unit и т. д. В этом случае вы столкнетесь с исключением переполнения стека во время выполнения, если/когда вы попытаетесь создать экземпляр объекта Unit.

Я думал, что вы сможете избежать проблемы struct, используя тип, допускающий значение NULL, но проблема остается. Я считаю, что это связано с тем, что struct с нулевым значением все еще создается, когда установлено значение null.

person Dave Cousineau    schedule 13.01.2013
comment
Оба ваших предположения неверны на самом деле. Отредактируйте это. С первым я имею в виду, что нет никаких предупреждений о времени компиляции, как для структур. - person nawfal; 14.01.2013
comment
@nawfal можно поконкретнее? - person Dave Cousineau; 14.01.2013
comment
Я не говорил, что вы получите предупреждение во время компиляции, я сказал, что вы получите переполнение стека, что вы и делаете. - person Dave Cousineau; 14.01.2013
comment
Хорошо, первый код не так уж и важен, потому что вы говорите о чем-то похожем (но другом, поскольку это исключение времени выполнения). Но второй код тоже не компилируется. - person nawfal; 14.01.2013
comment
Пожалуйста, отредактируйте неверную информацию. Кто-то может понизить это в противном случае. Тем не менее я понимаю вашу точку зрения и дал мне больше понимания. - person nawfal; 14.01.2013

Потому что Struct это value type. Unit может хранить значение как самого себя? Я думаю, что это невозможно.

Но когда Unit это class, class это reference type. Unit может содержать ссылку на другой объект Unit. Это нормально.

person Soner Gönül    schedule 13.01.2013