[ACCEPTED]-Cycle in the struct layout that doesn't exist-cyclic-reference
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.
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;
}
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
.
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:
AsNewFullyMutable
-- Public 21 instance -- Returns a newMutableInfo
object, with 20 data copied from the original, callingAsNewFullyMutable
on 19 any nested references.AsNewMutable
-- Public instance 18 -- Returns a newMutableInfo
object, with data copied 17 from the original, callingAsImmutable
on any nested 16 references.AsNewImmutable
-- Protected instance -- Returns 15 a newImmutableInfo
object, with data copied from the 14 orignal, callingAsImmutable
(notAsNewImmutable
) on any nested references.AsImmutable
-- Public 13 virtual -- For anImmutableInfo
, return itself; for a 12MutableInfo
, callAsNewImmutable
on itself.AsMutable
-- Public virtual -- For 11 aMutableInfo
, return itself; for anImmutableInfo
, callAsNewMutable
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.
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
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.