C
C#2mo ago
peppy

Use of inline arrays to model arrays of structs

For a native type with the following shape:
public struct CHRDATA {
public uint hp;
public uint mp; // ... hundreds more fields
}
public struct SAVEDATA {
public CHRDATA chr1;
public CHRDATA chr2;
public CHRDATA chr3; // ... tens more entries
}
public struct CHRDATA {
public uint hp;
public uint mp; // ... hundreds more fields
}
public struct SAVEDATA {
public CHRDATA chr1;
public CHRDATA chr2;
public CHRDATA chr3; // ... tens more entries
}
it occurred to me I could represent the sequential CHRDATAs with an InlineArray as such:
[InlineArray(80)]
public struct CHRDATA_ARRAY {
private CHRDATA _c;
}
public struct SAVEDATA {
public CHRDATA_ARRAY chrs;
}
[InlineArray(80)]
public struct CHRDATA_ARRAY {
private CHRDATA _c;
}
public struct SAVEDATA {
public CHRDATA_ARRAY chrs;
}
which raises a few questions, since I'm unfamiliar with the feature: 1) is this legal? can you make InlineArrays of blittable structs, or should usage of InlineArray be restricted to primitives? (i.e. int, float) 2) does chrs[62].hp = 300; in the above case operate on a copy or actually mutate the relevant part of SAVEDATA?
12 Replies
ティナ
ティナ2mo ago
Inline arrays, or fixed sized buffers - C# feature specifications
Inline arrays provide a general-purpose and safe mechanism for declaring inline arrays within C# classes, structs, and interfaces.
peppy
peppyOP2mo ago
alright, that clears it up, just one follow-up: if one of these was special and I wanted to provide a named accessor, by putting a property on CHRDATA_ARRAY like:
public CHRDATA special_character_slot { get { return this[37]; } }
public CHRDATA special_character_slot { get { return this[37]; } }
I assume this will not return ref CHRDATA - is there a way to make that happen? if I try:
public ref CHRDATA special_character_slot { get { return ref this[37]; } }
public ref CHRDATA special_character_slot { get { return ref this[37]; } }
it fires CS9084: Struct member returns 'this' or other instance members by reference, but I've not been able to find documentation for this
Anton
Anton2mo ago
you can work around this by slapping [UnscopedRef] on the getter or make an extension method that takes in a ref
peppy
peppyOP2mo ago
:HmmNoted: Thanks, I'll write that down somewhere
cned
cned2mo ago
You may get in hot water with this, since you’ve got a ref to something that might be in the stack that the compiler doesn’t know is in the stack anymore… Any reason to not just add a setter and skip the dangers of ref? Heck, you might have a ref to a struct that doesn’t even really exist anymore (if it made a copy of the struct to call this method on).
peppy
peppyOP2mo ago
in this case, the structs are just mapping/projection for something at a fixed location in memory in a subordinate native process in that sense, I don't know what would have to occur for them to move, and they're not intended to be 'used' as such in regular calls
cned
cned2mo ago
I guess if you have some fixed pointer to a native, returning a ref is... probably safe...? Though "safe" is certainly an interesting word when working with pointers. 🙂 I'm surpzied the compiler bothered warning you about that in an unsafe context.
Aaron
Aaron2mo ago
you do not get in hot water for this
MODiX
MODiX2mo ago
Aaron
REPL Result: Failure
struct S
{
int a;
[System.Diagnostics.CodeAnalysis.UnscopedRef]
public ref int M() => ref a;
}

ref int Test()
{
S s = default;
// try to return data that will no longer exist, this will error because the compiler knows what UnscopedRef means and knows not to allow this
return ref s.M();
}
struct S
{
int a;
[System.Diagnostics.CodeAnalysis.UnscopedRef]
public ref int M() => ref a;
}

ref int Test()
{
S s = default;
// try to return data that will no longer exist, this will error because the compiler knows what UnscopedRef means and knows not to allow this
return ref s.M();
}
Exception: CompilationErrorException
- Cannot return local 's' by reference because it is not a ref local
- Cannot return local 's' by reference because it is not a ref local
Compile: 376.485ms | Execution: 0.000ms | React with ❌ to remove this embed.
Aaron
Aaron2mo ago
the compiler keeps track of this and does not let you do invalid things with refs it does warn you about this in an unsafe context, but only because it is normally an outright error and you really shouldn't be doing it they were not in one when they tried this anyway, I don't think
MODiX
MODiX2mo ago
Aaron
REPL Result: Success
struct S
{
int a;
public unsafe ref int M() => ref a;
}
struct S
{
int a;
public unsafe ref int M() => ref a;
}
Compile: 274.488ms | Execution: 39.816ms | React with ❌ to remove this embed.
Aaron
Aaron2mo ago
(there is a warning there still, the repl just doesn't show warnings) but yeah, [UnscopedRef] isn't "shut up and do what I want, who cares about safety", it does something very specific and restricts how callers can use the ref that is returned

Did you find this page helpful?