Structs
Structs define concrete structured types. Their values may live directly in stack storage, inline inside another value, or in explicitly allocated heap memory.
Declaration
A struct is declared at namespace level. Its fields have fixed types and are laid out in declaration order according to the selected target.
namespace Example;
struct Vector3
{
public float X;
public float Y;
public float Z;
}
Member visibility is described separately in Access modifiers.
Fields may themselves contain other struct values. Recursive data structures use pointer fields so every value retains a finite, target-defined size.
Object creation and initialization are covered in Constructors.
Readonly, sealed and static structs
A readonly struct has readonly instance state. Its instance fields and instance methods/accessors receive readonly semantics automatically, while constructors may initialize the fields. Mutable references or pointers derived from hidden instance state cannot escape. Readonly structs do not participate in struct inheritance.
public readonly struct Point
{
int X;
int Y;
public Point(int x, int y) { X = x; Y = y; }
public int Sum() { return X + Y; }
}
A sealed struct may be constructed and may itself derive from a valid base, but cannot be used as a base. It cannot introduce new virtual or abstract members. readonly sealed struct is valid.
A static struct is a namespace-level container for constants and explicitly static fields and methods. It has no instance layout and cannot be constructed, allocated, used as a value/pointer/reference type, inherited, inherit another type, implement an instance interface, or be queried with sizeof/alignof. Constructors, destructors, indexers and instance members are rejected.
public static struct Console
{
public static int WriteCount;
public static void Reset() { Console.WriteCount = 0; }
}
Members retain explicit static; the type modifier does not convert ordinary members. The compiler does not support generic static structs. static readonly, static sealed and abstract sealed/static combinations are invalid.
Value semantics
Struct assignment, parameters and return values use value semantics. Assigning a struct produces an independent copy of its fields. Pointer fields are copied as pointer values and do not deep-copy the referenced memory.
Local structs and nested fields remain mutable inside readonly functions. Copying a pointer field preserves its origin: explicit mutable parameters remain writable through local copies, while pointers from the readonly method's receiver or static state remain hidden. See local structs and pointer origins.
Given an existing Vector3 value named original:
Vector3 copy = original;
copy.X = 10.0f; // original.X is unchanged
Use . to access a field on a value:
float x = copy.X;
copy.Y = 8.0f;
Value semantics describe how a struct is copied and passed. They apply whether the value is stored on the stack, inside another value or in heap memory.
Value equality
Without an applicable user-defined operator, left == right compares two values of the same struct type field by field, recursively, and != is the logical inverse. A declared operator takes precedence independently for each token. Padding bytes are ignored by built-in equality; Xenon never implements it with a raw byte comparison.
struct Position
{
public int X;
public int Y;
}
bool same = first == second;
Primitive and enum fields use their normal value equality. Floating fields therefore treat +0.0 and -0.0 as equal and NaN as unequal. Raw pointers compare addresses without reading their pointees. Plain array fields compare unmanaged handle identity rather than their elements. shared and weak fields compare their existing handle identities and equality does not retain or release ownership.
Nested and specialized generic structs use the same recursive rules. Equality is rejected before code generation when a field type has no equality semantics. Structs containing atomic fields have no aggregate equality semantics; access the atomic field through its supported operations instead.
Methods
Methods operate on an implicit instance. Fields may be referenced directly inside the method body.
Readonly code may call a mutable instance method on a local or explicitly mutable pointer/reference receiver if the compiler can verify its body has no hidden effects. Nested method calls retain that receiver's origin; static writes and non-readonly free/static calls remain forbidden. This does not allow mutable methods through readonly pointers/references. See contextual method analysis for examples.
struct Counter
{
private int Value;
public void Reset(int value)
{
Value = value;
}
public void Add(int amount)
{
Value += amount;
}
public int Read()
{
return Value;
}
}
For an existing Counter value named counter, call methods with .:
counter.Reset(10);
counter.Add(5);
int current = counter.Read();
Pointers and memory
Take the address of an existing value with &. Pointer member access uses -> to make the raw access explicit.
Vector3* pointer = ©
pointer->X = 12.0f;
float y = pointer->Y;
For heap allocation, see Constructing values and heap objects. Storage ownership and release are covered in Manual memory.
If the struct declares or inherits a destructor, delete invokes it before releasing the allocation. free skips object cleanup and releases storage only. Elements of stack arrays are destroyed on scope exit. See Destructors for syntax, base cleanup and virtual dispatch.