Records
Vogen supports records (record class
and record struct
).
For classes and structs, Vogen generates a lot of boilerplate code. But for records, some of this boilerplate code is already generated. This page lists the differences between records (classes and structs) and non-record classes and structs.
Things to note are:
GetHashCode()
The generated code doesn't generate GetHashCode() as the default implementation does that.
Equals
... for Equals(vo left, vo right)
and (vo left, primitive right)
The generated code doesn't generate equals overloads as this is generated automatically by the compiler.
ToString
Vogen overrides ToString
. The implementation of ToString
in record
(class
- not struct
) enumerates fields and properties, which causes a problem in Vogen if the type is not initialized (for instance, if it's being converted or deserialized from JSON)
Note that if you want to override ToString
yourself, i.e., to override what the C# compiler generates and to override what Vogen generates, then seal the method (C# 10 onwards), e.g.
public override sealed string ToString() => "!!"
With
Vogen supports with
. However, Vogen generally just has one property, Value
, but using with
is still supported and will still run normalization and validation
Primary Constructors
Primary constructors can't be used. Vogen is primarily focused on wrapping a single underlying type. When a primary constructor is used, e.g.
We get the following compilation error: error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer.
That compilation error is rather cryptic, so we'll improve the analyzer to spot primary constructors and give a better error message.
Primary constructors break Vogen's constraint of everything is created via the From
method.
Value property changes
To support records, an init
was added to the generated Value
property which is hidden from the API as it's not intended for external use.
This was required to support the with
concept, e.g., given the following
Using it like this creates compilation errors:
error CS0200: Property or indexer 'MyRecord.Value' cannot be assigned to -- it is read only
The init
in the Value
property can only be used via the with
mechanism (as the new Foo { Value = 123 }
would cause analyzer errors).
init
does all the things that From
does:
null checks if needed
run validation
run normalization
These choices were made during the initial implementation for records. With hindsight, it would be better to have more consistency between records and non-records.
Another consideration to come in C# 12 is primary constructors for classes, and they will fit in with Vogen.