POC for emitting record struct
-like types (for use in PowerShell)
C# 10.0 (currently in preview) introduces value-type records (record struct
) as a native part of the language syntax.
In reality, record struct
is just a macro for generating structs with a constructor that takes all members as arguments and provides safe implementations of IEquatable<T>.Equals()
, object.ToString()
and a tuple deconstructor - all of which are type traits we can already emit using System.Reflection.Emit
.
This POC intends to showcase how an identical type compilation routine could be introduced for emission of record struct
-like types in PowerShell.
Much like the existing type emitter in PowerShell, RecordEmitter does the following:
- Create dynamic assembly at runtime (currently it caches a single static assembly for reuse = name collisions might occur)
- Define a new struct type (eg. a subtype of
System.ValueType
) - Generate property bindings for all members
- Generate IL for default get/set routines for each property
- Generate safe GetHashCode() + object.Equals() overrides based on available members
- Generate
ToString()
override +PrintMembers()
helper function, to predictably construct meaningful string representations - Generate a single public constructor based on member layout
- Generate tuple deconstructor with signature based on member layout
The following features are currently incomplete or not implemented at all:
- Explicit implementation of
IEquatable<T>
(the emitter currently generates a safe/correctEquals(T, T)
implementation, but the type doesn't declareIEquatable<t>
as an implemented interface) ToString()
override - the heavy lifting is done inPrintMembers()
, but no method override currently exists forbase.ToString()
object.Equals(object obj)
override is buggy - invoking the type-specific Equals implementation works, butobject.Equals(obj)
(which is the entry point when using$record -eq $record2
for example) results in invalid IL generation at runtime (likely a problem with argument alignment)- Deconstructor -
record struct
results in the generation of avoid Deconstruct(out <member1Type> member1, out <member2Type> member2, ..., out <memberNType> memberN)
method, not prioritized because of PowerShell's limited use of tuple deconstruction