I've implemented the Result
pattern for error handling in C# using record types, as shown in the code snippet below:
namespace WebApi.Utils
{
public struct Roid { }
public record Result<TResult, TError>
{
public static Success<TResult> Success(TResult result) => new Success<TResult>(result);
public static Failure<TError> Failed(TError error) => new Failure<TError>(error);
}
public record Success<TResult>(TResult Value) : Result<TResult, Roid>;
public record Failure<TError>(TError Error) : Result<Roid, TError>;
}
In this setup, Success
and Failure
are record types that inherit from a generic Result
record type. My goal is to ensure that instances of Success<TResult>
and Failure<TError>
can only be created through the Success
and Failure
static methods defined in the Result
record, respectively.
How can I enforce this constraint, ensuring that Success
and Failure
instances are only created via these specific methods, and not through any other means?
You can't enforce this while still using the record with positional syntax for property definition/primary constructor, so the only option is to migrate to "explicit" property definitions combined with explicit default ctor:
With corresponding changes to the method:
Note that this will still allow creation of new instances via
with
(see the nondestructive mutation section of the docs):You can prevent this in runtime by introducing copy ctor which will throw:
But for compile time you will need to write a custom Roslyn analyzer (or just switch to using "ordinary" classes).
P.S.
I would argue that moving the static methods to static non-generic class would be beneficial in terms ease of usage :