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

本文关键字:循环 布局 结构 不存在 | 更新日期: 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(。您可以使类不可变,并重写EqualsGetHashCode以提供类似于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? cNullable<info>的简写,它本身就是一个struct

没有任何

方法可以实现可变大小项目的可变值语义(语义上,我认为您所追求的是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可变,则必须忍受可变的引用语义(包括保护性克隆(或奇怪而古怪的结构类混合语义。 如果性能是一个问题,我会有一个建议,那就是有一个抽象的InfoBase类,其中包含MutableInfoImmutableInfo的后代,以及以下成员:

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

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

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

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

  5. AsMutable -- 公共虚拟 -- 对于MutableInfo,返回自身;对于ImmutableInfo,调用自身AsNewMutable

克隆

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

免责声明:这可能无法实现"类似结构的值类型行为"的目标。

一种解决方案是使用一个项目的数组来获取递归引用结构的引用。将我的方法调整到您的代码中,如下所示。

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;
    }
}