Class RequiredEnum<TSelf>
- Namespace
- Trellis
- Assembly
- Trellis.Primitives.dll
Base class for creating strongly-typed, behavior-rich enumeration value objects. Enum value objects are a DDD pattern that replaces C# enums with full-featured classes.
public abstract class RequiredEnum<TSelf> : IEquatable<RequiredEnum<TSelf>> where TSelf : RequiredEnum<TSelf>, IScalarValue<TSelf, string>
Type Parameters
TSelfThe derived enum value object type itself (CRTP pattern).
- Inheritance
-
RequiredEnum<TSelf>
- Implements
-
IEquatable<RequiredEnum<TSelf>>
- Inherited Members
- Extension Methods
Examples
Basic enum value object:
public partial class OrderState : RequiredEnum<OrderState>
{
public static readonly OrderState Draft = new();
public static readonly OrderState Confirmed = new();
public static readonly OrderState Shipped = new();
public static readonly OrderState Delivered = new();
public static readonly OrderState Cancelled = new();
}
// The source generator automatically creates:
// - IScalarValue<OrderState, string> interface implementation
// - public static Result<OrderState> TryCreate(string value)
// - public static Result<OrderState> TryCreate(string? value, string? fieldName = null)
// - public static OrderState Parse(string s, IFormatProvider? provider)
// - public static bool TryParse(string? s, IFormatProvider? provider, out OrderState result)
// - [JsonConverter(typeof(RequiredEnumJsonConverter<OrderState>))] attribute
// Usage - Name is auto-derived from field name
var state = OrderState.Draft; // Name = "Draft"
var all = OrderState.GetAll();
var result = OrderState.TryCreate("Draft"); // Result<OrderState>
Enum value object with behavior:
public partial class PaymentMethod : RequiredEnum<PaymentMethod>
{
public static readonly PaymentMethod CreditCard = new(fee: 0.029m);
public static readonly PaymentMethod BankTransfer = new(fee: 0.005m);
public static readonly PaymentMethod Cash = new(fee: 0m);
public decimal Fee { get; }
private PaymentMethod(decimal fee) => Fee = fee;
public decimal CalculateFee(decimal amount) => amount * Fee;
}
Using in ASP.NET Core DTOs with automatic validation:
public record UpdateOrderDto
{
public OrderState State { get; init; } = null!;
}
// In controller - validation happens automatically
[HttpPut("{id}")]
public IActionResult UpdateOrder(Guid id, UpdateOrderDto dto)
{
// If we reach here, dto.State is already validated!
return Ok(_orderService.UpdateState(id, dto.State));
}
Remarks
Enum value objects address limitations of C# enums:
- Behavior: Each value can have associated behavior and properties
- Type safety: Invalid values are impossible (no
(OrderStatus)999) - Extensibility: Add methods, computed properties, and domain logic
- State machines: Model valid transitions between states
Each enum value object member is defined as a static readonly field:
- Members are discovered via reflection and cached for performance
- The Name property is auto-derived from the field name (infrastructure concern)
- The Value property is auto-generated for persistence (infrastructure concern)
When used with the partial keyword, the PrimitiveValueObjectGenerator source generator
automatically creates:
IScalarValue<TSelf, string>implementation for ASP.NET Core automatic validationTryCreate(string)- Factory method for non-nullable strings (required by IScalarValue)TryCreate(string?, string?)- Factory method with validation and custom field nameIParsable<T>implementation (Parse,TryParse)- JSON serialization support via
RequiredEnumJsonConverter<T> - ASP.NET Core model binding from route/query/form/headers
- OpenTelemetry activity tracing
Common use cases:
- Order/payment/shipping statuses
- User roles and permissions
- Document states in workflows
- Any finite set of domain values with behavior
Constructors
RequiredEnum()
Initializes a new instance. The Name is auto-derived from the field name.
protected RequiredEnum()
Properties
Name
Gets the name of this enum value object member. Auto-derived from the field name during discovery. This is an infrastructure concern for serialization and display.
public string Name { get; }
Property Value
Remarks
Name is lazily initialized on first access to avoid chicken-and-egg issues with static field initialization order.
Value
Gets the auto-generated integer value for persistence. This is an infrastructure concern - values are assigned based on declaration order (0, 1, 2, ...).
public int Value { get; }
Property Value
Remarks
Value is lazily initialized on first access to avoid chicken-and-egg issues with static field initialization order.
Methods
Equals(object?)
Determines whether the specified object is equal to the current object.
public override bool Equals(object? obj)
Parameters
objobjectThe object to compare with the current object.
Returns
Equals(RequiredEnum<TSelf>?)
Indicates whether the current object is equal to another object of the same type.
public bool Equals(RequiredEnum<TSelf>? other)
Parameters
otherRequiredEnum<TSelf>An object to compare with this object.
Returns
GetAll()
Gets all defined members of this enum value object type.
public static IReadOnlyCollection<TSelf> GetAll()
Returns
- IReadOnlyCollection<TSelf>
GetHashCode()
Serves as the default hash function.
public override int GetHashCode()
Returns
- int
A hash code for the current object.
Is(params TSelf[])
Checks if this instance is one of the specified values.
public bool Is(params TSelf[] values)
Parameters
valuesTSelf[]
Returns
IsNot(params TSelf[])
Checks if this instance is not one of the specified values.
public bool IsNot(params TSelf[] values)
Parameters
valuesTSelf[]
Returns
ToString()
Returns a string that represents the current object.
public override string ToString()
Returns
- string
A string that represents the current object.
TryFromName(string?, string?)
Attempts to find a member by its name (case-insensitive).
public static Result<TSelf> TryFromName(string? name, string? fieldName = null)
Parameters
namestringThe name to search for.
fieldNamestringOptional field name for validation error messages.
Returns
- Result<TSelf>
A Result<TValue> containing the matching member or a validation error.
Operators
operator ==(RequiredEnum<TSelf>?, RequiredEnum<TSelf>?)
Determines whether two instances are equal.
public static bool operator ==(RequiredEnum<TSelf>? left, RequiredEnum<TSelf>? right)
Parameters
leftRequiredEnum<TSelf>rightRequiredEnum<TSelf>
Returns
operator !=(RequiredEnum<TSelf>?, RequiredEnum<TSelf>?)
Determines whether two instances are not equal.
public static bool operator !=(RequiredEnum<TSelf>? left, RequiredEnum<TSelf>? right)
Parameters
leftRequiredEnum<TSelf>rightRequiredEnum<TSelf>