Records (also called "Structures" or "Structs", in many other programming languages) are a lot like Classes except for two crucial differences:
- Records are value types, and stored on the stack, while classes are reference types, and stored in global memory.
- While they can have an ancestry hierarchy (i.e. a record type can decent from and extend another record), they do not support polymorphism, e.g., overriding virtual methods.
When using a record type, the value is stored on the stack (or when defined inside a different type, it is stored inline within the memory space of that type). Assigning a record from one variable to another creates a copy of the record, and making changes to on copy does not affect the other. For this reason, records are usually used to hold a small number of related values.
See Value Types vs. Reference Types) for more on stack- vs heap-baswed types.
On .NET, a
StructLayout Aspect can be used to change the size and alignment of a structure, which is useful when used in combination with P/Invoke calls to native platform APIs. Similarly, the
FieldOffset Aspect can be used to set the offset of individual fields within the record.
A record type is declared using the
record keyword, followed by zero or more member declaratins, and closed off with the
end keyword. An optional ancestor and/or a list of one of more interfaces to implement can be provided in parenthesis behind the
type Color = public record(IColor) public R, G, B, A: Byte; end;
Like all custom types, records can be nested in other types with
nested in syntax.
records are not to be confused with the
record types introduced in C# 9 and Mercury, which add special logic to class (or struct). Oxygene
records are the equivalent of a simple
struct in C# or Swift, or a
Structure in Mercury.
By default in-memory layout of a record's individual fields is optimized for fast access first, and memory efficiency second. This means that additional padding might be added to make sure Integers and pointers align at 4 or 8-byte boundaries, and the in-memory order of fields might be rearranged, as well.
When a record is marked as packed with the
packed directive (or the cross-language
Packed Aspect), this will not happen, and the records memory layout will be exactly as specified.
Use packed records when the memory layout matters – for example when reading binary data from disk or the network and accessing it as a record, or when sharing records in-memory with code compiled from non-Elements languages, such as C ort Delphi.
Cocoa and Island Only
Packed records are only supported on Cocoa and the native Island-backed platforms (Windows, Linux, Android NDK and WebAssembly. On .NET and Java, the keyword will be ignored. The Packed aspect is available on the Cocoa and Island platforms only.
Records can define Invariants to help ensure a consistent state. Invariants are boolean expressions that will automatically be enforced by the compiler.
Note that invariants can only be effective for non-public fields, as access to public fields would bypass them. This makes invariants less useful for most typical records rthan they are for Classes.
Refer to the Nested Types topic for more details.
Like Extension Classes, Extension Records can add properties, methods, operators and events to an existing type (but not add anything that requires storage, like fields, events or stored properties). These become available to callers if this type is in scope for the caller. The first type in the ancestor defines which type gets extended; optional interfaces can be used to add/implement as interfaces allowing the type to be compatible with that interface.
Read more about Extension Records here.
The visibility of a record type can be controlled by applying a Visibility Modifier on the declaration. The default visibility is
A number of other Type Modifiers can be applied to records:
extensionMakes this an extension record; see Extensions.
mappedMakes this a mapped record; see Mapped Types.
partialPartial can be used to spread a type over several files in the same project. All parts must have this modifier then.
readonlyMakes this record readonly. All fields in it will be readonly and can only be set by a constructor and not modified afterwards.
staticStatic records are records with only static members.
packedPacked records do not align their members by round offsets. Ignored on .NET and Java