在不存在的结构布局中循环
这是我的一些代码的简化版本:
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”成为一个类而不是一个struct,并给它一个成员函数来返回我在传递它时使用的副本。 实际上模拟与结构相同但具有类的行为。
我在寻找答案时也创建了以下问题。
C#中的值类型类定义?
拥有一个包含自己作为成员的结构是不合法的。 这是因为结构具有固定的大小 ,并且它必须至少与其每个成员的大小之和一样大。 您的类型必须为两个浮点数提供8个字节,至少一个字节用于显示info
是否为空,加上另一个info
的大小。 这给出了以下不等式:
size of info >= 4 + 4 + 1 + size of info
这显然是不可能的,因为它需要你的类型无限大。
您必须使用引用类型(即类)。 您可以使您的类不可变并重写Equals
和GetHashCode
以提供类似于String
类的行为。
这创建一个循环的原因是Nullable
本身就是一个struct
。 因为它引用回info
你在布局中有一个循环( info
有一个Nullable
字段,它有一个info
字段)。 它基本上等同于以下内容
public struct MyNullable { public T value; public bool hasValue; } struct info { public float a, b; public MyNullable next; }
真正的问题在于这一行:
public info? c;
由于这是一个struct
,C#需要知道内部info
/ s布局才能生成外部info
的布局。 内部info
包括内部内部info
,内部info
又包括内部内部info
,依此类推。 由于此循环引用问题,编译器无法生成布局。
注意: info? c
info? c
是Nullable
的简写,它本身就是一个struct
。
没有任何方法可以实现可变大小项的可变值语义(从语义MyInfo1 = MyInfo2
,我认为你所拥有的是让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
是可变的,那么您将不得不忍受可变引用语义(包括保护性克隆)或者奇怪且古怪的struct-class-混合语义学。 如果性能是一个问题,我会有一个建议是有一个带有后代MutableInfo
和ImmutableInfo
的抽象InfoBase
类,以及以下成员:
-
AsNewFullyMutable
– 公共实例 – 返回一个新的MutableInfo
对象,其中包含从原始数据复制的数据,在任何嵌套引用上调用AsNewFullyMutable
。 -
AsNewMutable
– 公共实例 – 返回一个新的MutableInfo
对象,其中包含从原始数据复制的数据,在任何嵌套引用上调用AsImmutable
。 -
AsNewImmutable
– 受保护的实例 – 返回一个新的ImmutableInfo
对象,其中包含从AsImmutable
复制的数据,在任何嵌套引用上调用AsImmutable
(不是AsNewImmutable
)。 -
AsImmutable
– 公共虚拟 – 对于ImmutableInfo
,返回自身; 对于MutableInfo
,自己调用AsNewImmutable
。 -
AsMutable
– Public virtual – 对于MutableInfo
,返回自身; 对于ImmutableInfo
,自己调用AsNewMutable
。
克隆对象时,根据是否有人预期在必须进行变异之前再次克隆对象或其后代,可以调用AsImmutable
, AsNewFullyMutable
或AsNewMutable
。 在人们期望一个对象被反复进行防御性克隆的情况下,该对象将被一个不可变的实例取代,然后不再需要克隆它,直到有人想要改变它为止。