Цикл в макете структуры, который не существует
это упрощенная версия мой код:
public struct info
{
public float a, b;
public info? c;
public info(float a, float b, info? c = null)
{
this.a = a;
this.b = b;
this.c = c;
}
}
проблема-ошибка Struct member 'info' causes a cycle in the struct layout.
Я после структуры, как поведение типа значения. Я мог бы смоделировать это, используя класс и функцию-член клона, но я не вижу, почему мне это нужно.
как эта ошибка истинна? Рекурсия, возможно, может вызвать построение навсегда в некоторых подобных ситуациях, но я не могу придумать, как это может быть в этом случае. Ниже приведены примеры, которые должны быть в порядке, если программа будет компилироваться.
new info(1, 2);
new info(1, 2, null);
new info(1, 2, new info(3, 4));
edit:
решение, которое я использовал, состояло в том, чтобы сделать "info" классом вместо структуры и дать ему функцию-член, чтобы вернуть копию, которую я использовал при ее передаче. Фактически имитируя то же поведение, что и структура, но с классом.
Я также создал следующий вопрос, ища ответ.
4 ответов
незаконно иметь структуру, которая содержит себя в качестве члена. Это потому, что структура имеет фиксированный размер, и он должен быть по крайней мере таким же большим, как сумма размеров каждого из его членов. Ваш тип должен иметь 8 байт для двух поплавков, по крайней мере один байт, чтобы показать, является лиinfo
равно null, плюс размер другого info
. Это дает следующее неравенство:
size of info >= 4 + 4 + 1 + size of info
это, очевидно, невозможно, поскольку для этого потребуется ваш тип бесконечно большой.
вы должны использовать ссылочный тип (т. е. класса). Вы можете сделать свой класс неизменяемым и переопределить Equals
и GetHashCode
чтобы дать значение-подобное поведение, подобное String
класса.
причина, по которой это создает цикл, заключается в том, что Nullable<T>
сама по себе struct
. Потому что это относится к info
у вас есть цикл в макете (info
есть поле Nullable<info>
и поле info
) . Это по существу эквивалентно следующему
public struct MyNullable<T> {
public T value;
public bool hasValue;
}
struct info {
public float a, b;
public MyNullable<info> next;
}
реальная проблема находится на этой линии:
public info? c;
так как это struct
, C# должен знать внутренний info
/s макет, прежде чем он может произвести внешний info
's макет. И внутреннее info
включает в себя внутренняя внутренняя info
, который в свою очередь включает в себя внутренний внутренний внутреннийinfo
и так далее. Компилятор не может создать макет из-за этой проблемы с круговой ссылкой.
Примечание: info? c
это сокращение от Nullable<info>
, который является struct
.
нет никакого способа достичь изменяемой семантики значений элементов переменного размера (семантически, я думаю, что вы хотите иметь MyInfo1 = MyInfo2
создайте новый связанный список, который отделен от списка, запущенного MyInfo2). Можно заменить info?
С info[]
(который всегда будет либо нулевым, либо заполненным одноэлементным массивом), либо классом держателя, который обертывает экземпляр info
, но семантика, вероятно, не будет тем, что вам нужно. Следующий MyInfo1 = MyInfo2
изменения MyInfo1.a
не повлияет MyInfo2.a
, ни изменения MyInfo1.c
влияет MyInfo2.c
, но изменения MyInfo1.c[0].a
повлияет MyInfo2.c[0].a
.
было бы неплохо, если бы будущая версия .net могла иметь некоторое понятие "ссылки на значения", так что копирование структуры не будет просто копировать все ее поля. Существует некоторая ценность в том, что .net не поддерживает все тонкости конструкторов копирования C++, но также было бы полезно разрешить места хранения введите "struct", чтобы иметь идентификатор, который будет связан с местом хранения, а не с его содержимым.
учитывая, что .net в настоящее время не поддерживает такую концепцию, однако, если вы хотите info
чтобы быть изменяемым, вам придется либо мириться с изменяемой ссылочной семантикой (включая защитное клонирование), либо со странной и дурацкой структурой-гибридной семантикой. Одно предложение, которое у меня было бы, если бы производительность была проблемой, было бы иметь абстрактное InfoBase
класс с потомками MutableInfo
и ImmutableInfo
, и со следующими членами:
AsNewFullyMutable
-- Public instance -- возвращает новыйMutableInfo
объект, с данными, скопированными из оригинала, вызываяAsNewFullyMutable
по любым вложенным ссылкам.AsNewMutable
-- Public instance -- возвращает новыйMutableInfo
объект, с данными, скопированными из оригинала, вызываяAsImmutable
по любым вложенным ссылкам.AsNewImmutable
-- защищенный instance -- возвращает новыйImmutableInfo
объект, с данными, скопированными из оригинала, вызываяAsImmutable
(неAsNewImmutable
) по любым вложенным ссылкам.AsImmutable
-- Public virtual -- дляImmutableInfo
, вернись сам; ибоMutableInfo
, называютAsNewImmutable
на себя.AsMutable
-- Public virtual -- дляMutableInfo
, вернись сам; ибоImmutableInfo
, называютAsNewMutable
на себя.
при клонировании объекта, в зависимости от от того, ожидал ли человек, что объект или его потомки будут клонированы снова до того, как он должен был мутировать, можно было бы назвать либо AsImmutable
, AsNewFullyMutable
или AsNewMutable
. В сценариях, где можно было бы ожидать, что объект будет неоднократно клонироваться в целях защиты, объект будет заменен неизменяемым экземпляром, который больше не будет клонироваться до тех пор, пока не возникнет желание его мутировать.