Skip to main content

What Gets Generated

For every [ValueObject] type, the generator emits a partial declaration alongside yours in a .g.cs file.

Class output

// Input
[ValueObject]
public partial class Money
{
public decimal Amount { get; }
public string Currency { get; }
}

// Generated (Money.g.cs)
sealed partial class Money : System.IEquatable<Money>
{
public override bool Equals(object? obj) =>
obj is Money other && Equals(other);

public bool Equals(Money? other) =>
other is not null &&
Amount == other.Amount &&
Currency == other.Currency;

public override int GetHashCode() =>
System.HashCode.Combine(Amount, Currency);

public static bool operator ==(Money? left, Money? right) =>
left is null ? right is null : left.Equals(right);

public static bool operator !=(Money? left, Money? right) =>
!(left == right);

public override string ToString() =>
$"Money {{ Amount = {Amount}, Currency = {Currency} }}";
}

Struct output

// Input
[ValueObject]
public partial struct CustomerId
{
public int Value { get; }
}

// Generated
readonly partial struct CustomerId : System.IEquatable<CustomerId>
{
public override bool Equals(object? obj) =>
obj is CustomerId other && Equals(other);

public bool Equals(CustomerId other) => // no nullable — structs can't be null
Value == other.Value;

public override int GetHashCode() =>
System.HashCode.Combine(Value);

public static bool operator ==(CustomerId left, CustomerId right) =>
left.Equals(right); // no null guard needed

public static bool operator !=(CustomerId left, CustomerId right) =>
!left.Equals(right);

public override string ToString() =>
$"CustomerId {{ Value = {Value} }}";
}

Hash code strategy

Number of propertiesStrategy
0return 0;
1–8System.HashCode.Combine(p1, p2, ...)
9+var hc = new System.HashCode(); hc.Add(p1); ... return hc.ToHashCode();

Type modifiers

TypeGenerated modifier
partial classsealed partial class
partial structreadonly partial struct
partial struct with ForceClass = truesealed partial class

Classes are sealed to prevent subclasses from accidentally inheriting equality semantics and to allow the JIT to devirtualize Equals calls.

Structs are readonly because mutable value-type equality is a known footgun in C#.