在不存在的结构布局中循环

这是我的一些代码的简化版本:

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 

这显然是不可能的,因为它需要你的类型无限大。

您必须使用引用类型(即类)。 您可以使您的类不可变并重写EqualsGetHashCode以提供类似于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? cNullable的简写,它本身就是一个struct

没有任何方法可以实现可变大小项的可变值语义(从语义MyInfo1 = MyInfo2 ,我认为你所拥有的是让MyInfo1 = MyInfo2生成一个与MyInfo2启动的链接列表分离的新链接列表)。 有人可以取代info? 使用info[] (它总是为null或者用单元素数组填充),或者包含一个包含info实例的holder类,但语义可能不是你所追求的。 在MyInfo1 = MyInfo2 ,对MyInfo1.a更改不会影响MyInfo2.aMyInfo1.c更改也不会影响MyInfo2.c ,但更改为MyInfo1.c[0].a会影响MyInfo2.c[0].a

如果.net的未来版本可以有一些“值引用”的概念,那将是很好的,因此复制结构不会简单地复制其所有字段。 .net不支持C ++拷贝构造函数的所有复杂性这一事实是有价值的,但是允许类型为’struct’的存储位置具有与存储位置相关联的标识而不是它的内容。

鉴于.net目前不支持任何此类概念,但是,如果您希望info是可变的,那么您将不得不忍受可变引用语义(包括保护性克隆)或者奇怪且古怪的struct-class-混合语义学。 如果性能是一个问题,我会有一个建议是有一个带有后代MutableInfoImmutableInfo的抽象InfoBase类,以及以下成员:

  1. AsNewFullyMutable – 公共实例 – 返回一个新的MutableInfo对象,其中包含从原始数据复制的数据,在任何嵌套引用上调用AsNewFullyMutable

  2. AsNewMutable – 公共实例 – 返回一个新的MutableInfo对象,其中包含从原始数据复制的数据,在任何嵌套引用上调用AsImmutable

  3. AsNewImmutable – 受保护的实例 – 返回一个新的ImmutableInfo对象,其中包含从AsImmutable复制的数据,在任何嵌套引用上调用AsImmutable (不是AsNewImmutable )。

  4. AsImmutable – 公共虚拟 – 对于ImmutableInfo ,返回自身; 对于MutableInfo ,自己调用AsNewImmutable

  5. AsMutable – Public virtual – 对于MutableInfo ,返回自身; 对于ImmutableInfo ,自己调用AsNewMutable

克隆对象时,根据是否有人预期在必须进行变异之前再次克隆对象或其后代,可以调用AsImmutableAsNewFullyMutableAsNewMutable 。 在人们期望一个对象被反复进行防御性克隆的情况下,该对象将被一个不可变的实例取代,然后不再需要克隆它,直到有人想要改变它为止。