[ACCEPTED]-Cycle in the struct layout that doesn't exist-cyclic-reference

Accepted answer
Score: 36

It's not legal to have a struct that contains 13 itself as a member. This is because a struct 12 has fixed size, and it must be at least as large as 11 the sum of the sizes of each of its members. Your 10 type would have to have 8 bytes for the 9 two floats, at least one byte to show whether 8 or not info is null, plus the size of another 7 info. This gives the following inequality:

 size of info >= 4 + 4 + 1 + size of info

This 6 is obviously impossible as it would require 5 your type to be infinitely large.

You have 4 to use a reference type (i.e. class). You 3 can make your class immutable and override 2 Equals and GetHashCode to give value-like behaviour, similar 1 to the String class.

Score: 13

The reason why this creates a cycle is that 4 Nullable<T> is itself a struct. Because it refers back to 3 info you have a cycle in the layout (info has a 2 field of Nullable<info> and it has a field of info) . It's 1 essentially equivalent to the following

public struct MyNullable<T> {
  public T value;
  public bool hasValue;
}

struct info { 
  public float a, b;
  public MyNullable<info> next;
}
Score: 5

The real problem is on this line:

public info? c;

Since this 7 is a struct, C# needs to know the inner info/s layout 6 before it could produce outer info's layout. And 5 the inner info includes an inner inner info, which 4 in turn includes an inner inner inner info, and 3 so on. The compiler cannot produce a layout 2 because of this circular reference issue.

Note: info? c is 1 a shorthand for Nullable<info> which is itself a struct.

Score: 2

There isn't any way to achieve mutable value 52 semantics of variable-sized items (semantically, I 51 think what you're after is to have MyInfo1 = MyInfo2 generate 50 a new linked list which is detached from 49 the one started by MyInfo2). One could 48 replace the info? with an info[] (which would always 47 either be null or else populated with a 46 single-element array), or with a holder 45 class that wraps an instance of info, but the 44 semantics would probably not be what you're 43 after. Following MyInfo1 = MyInfo2, changes to MyInfo1.a would not 42 affect MyInfo2.a, nor would changes to MyInfo1.c affect MyInfo2.c, but 41 changes to MyInfo1.c[0].a would affect MyInfo2.c[0].a.

It would be nice 40 if a future version of .net could have some 39 concept of "value references", so that copying 38 a struct wouldn't simply copy all of its 37 fields. There is some value to the fact 36 that .net does not support all the intricacies 35 of C++ copy constructors, but there would 34 also be value in allowing storage locations 33 of type 'struct' to have an identity which 32 would be associated with the storage location 31 rather than its content.

Given that .net 30 does not presently support any such concept, however, if 29 you want info to be mutable, you're going to 28 have to either put up with mutable reference 27 semantics (including protective cloning) or 26 with weird and wacky struct-class-hybrid 25 semantics. One suggestion I would have 24 if performance is a concern would be to 23 have an abstract InfoBase class with descendants 22 MutableInfo and ImmutableInfo, and with the following members:

  1. AsNewFullyMutable -- Public 21 instance -- Returns a new MutableInfo object, with 20 data copied from the original, calling AsNewFullyMutable on 19 any nested references.

  2. AsNewMutable -- Public instance 18 -- Returns a new MutableInfo object, with data copied 17 from the original, calling AsImmutable on any nested 16 references.

  3. AsNewImmutable -- Protected instance -- Returns 15 a new ImmutableInfo object, with data copied from the 14 orignal, calling AsImmutable (not AsNewImmutable) on any nested references.

  4. AsImmutable -- Public 13 virtual -- For an ImmutableInfo, return itself; for a 12 MutableInfo, call AsNewImmutable on itself.

  5. AsMutable -- Public virtual -- For 11 a MutableInfo, return itself; for an ImmutableInfo, call AsNewMutable on itself.

When 10 cloning an object, depending upon whether 9 one expected that the object or its descendants 8 would be cloned again before it had to be 7 mutated, one would call either AsImmutable, AsNewFullyMutable, or AsNewMutable. In 6 scenarios where one would expect an object 5 to be repeatedly defensively cloned, the 4 object would be replaced by an immutable 3 instance which would then no longer have 2 to be cloned until there was a desire to 1 mutate it.

Score: 1

Disclaimer: This may not achieve the goal of "struct 5 like value type behaviour."

One solution 4 is to use an array of one item to essentially 3 get a reference the recursively referenced 2 structure. Adapting my approach to your 1 code looks something like this.

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

More Related questions