在不存在的结构布局中循环
本文关键字:循环 布局 结构 不存在 | 更新日期: 2023-09-27 18:24:48
这是我的一些代码的简化版本:
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));
编辑:
我使用的解决方案是使"info"成为类而不是结构,并为其提供一个成员函数以返回我在传递它时使用的副本。实际上模拟与结构相同的行为,但使用类。
在寻找答案时,我还创建了以下问题。
C# 中的值类型类定义?
将自身包含为成员的结构是不合法的。这是因为结构具有固定大小,并且它必须至少与其每个成员的大小之和一样大。您的类型必须有两个浮点数的 8 个字节,至少一个字节来显示info
是否为 null,再加上另一个info
的大小。这给出了以下不平等:
size of info >= 4 + 4 + 1 + size of info
这显然是不可能的,因为它需要您的类型无限大。
您必须使用引用类型(即.class(。您可以使类不可变,并重写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
的布局。而内info
包括一个内在info
,而内在又包括一个内内info
,等等。由于此循环引用问题,编译器无法生成布局。
注意:info? c
是Nullable<info>
的简写,它本身就是一个struct
。
方法可以实现可变大小项目的可变值语义(语义上,我认为您所追求的是MyInfo1 = MyInfo2
生成一个新的链表,该链表与 MyInfo2 启动的链表分离(。 可以用info[]
替换info?
(它总是为 null 或填充单元素数组(,或者用包装 info
实例的 holder 类替换,但语义可能不是你所追求的。 在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
-- 公共实例 -- 返回一个新的MutableInfo
对象,其中包含从原始对象复制的数据,对任何嵌套引用调用AsNewFullyMutable
。AsNewMutable
-- 公共实例 -- 返回一个新的MutableInfo
对象,其中包含从原始对象复制的数据,对任何嵌套引用调用AsImmutable
。AsNewImmutable
-- 受保护的实例 -- 返回一个新的ImmutableInfo
对象,其中包含从原始对象复制的数据,在任何嵌套引用上调用AsImmutable
(而不是AsNewImmutable
(。AsImmutable
-- 公共虚拟 -- 对于ImmutableInfo
,返回自身;对于MutableInfo
,调用自身AsNewImmutable
。AsMutable
-- 公共虚拟 -- 对于MutableInfo
,返回自身;对于ImmutableInfo
,调用自身AsNewMutable
。
对象时,根据是否预期对象或其后代会在必须变异之前再次克隆,可以调用AsImmutable
、AsNewFullyMutable
或AsNewMutable
。 在人们期望一个对象被反复防御性克隆的情况下,该对象将被一个不可变的实例所取代,然后不再需要克隆该实例,直到有改变它的愿望。
免责声明:这可能无法实现"类似结构的值类型行为"的目标。
一种解决方案是使用一个项目的数组来获取递归引用结构的引用。将我的方法调整到您的代码中,如下所示。
public struct info
{
public float a, b;
public info? c
{
get
{
return cArray[nextIndex];
}
set
{
steps[nextIndex] = value;
}
}
private info?[] cArray;
public info(float a, float b, info? c = null)
{
this.a = a;
this.b = b;
this.cArray = new info?[] { c }
this.c = c;
}
}