← l-artikli kollha

C#

L-użu tar-Result Pattern f’C# Minflok ma Tirritorna Null

Ir-Result Pattern jagħmel l-istati mistennija ta’ suċċess u falliment espliċiti, u jipprovdi alternattiva aktar ċara milli tirritorna null jew tarmi exceptions għal riżultati tan-negozju ta’ rutina.

Tgħallimt ir-Result Pattern waqt li kont qed naħdem b’.NET, u dan biddel il-mod kif naħseb dwar methods li jistgħu jfallu. Minflok jirritorna null u jħalli lil min isejjaħlu jinterpreta xi jfisser, il-method jirritorna object li jiddeskrivi jew suċċess jew falliment.

Dan ma jfissirx li kull nullable value jew exception għandha tisparixxi. Il-pattern huwa l-aktar utli meta l-falliment ikun riżultat mistenni u min isejjaħ ikollu bżonn jifhem x’ġara.

Il-limitazzjoni meta tirritorna null

Ikkunsidra repository method li jirritorna null meta ma jkunx jista’ jsib user. Min isejjaħ jista’ jiċċekkja għal null, iżda l-value ma jispjegax ir-raġuni. Kien l-identifier invalidu? Il-user kien nieqes? Falliet xi validation oħra?

Nullable reference types itejbu din is-sitwazzjoni billi jippermettulna nesprimu jekk reference hijiex maħsuba biex taċċetta null. Il-compiler ta’ C# imbagħad juża static analysis biex iwissi dwar null assignments u dereferences possibbli. Madankollu, din hija compile-time feature; ma toħloqx runtime type separat u lanqas iżżid runtime validation. (learn.microsoft.com)

Result<T> jagħti tifsira lir-riżultat tal-operazzjoni. Minflok jirritorna User?, method jista’ jirritorna Result<User> li jkun fih jew il-user jew error speċifiku.

Ir-rappreżentazzjoni tal-errors

L-ewwel parti hija error type żgħir:

public record Error(string Code, string Message)
{
    public static Error None = new(string.Empty, string.Empty);
    public static Error NullValue = new("Error.NullValue", "Um valor nulo foi fornecido.");
}

Il-code jipprovdi identifier stabbli għal deċiżjonijiet programmatic u message li jista’ jiġi logged jew muri. Error.None jirrappreżenta operazzjoni li rnexxiet, fejn ma jeżisti ebda error.

Il-ħolqien tar-Result type

Ir-Result mhux generic jirrappreżenta operazzjonijiet li jirnexxu mingħajr ma jirritornaw value:

public class Result
{
    protected Result(bool isSuccess, Error error)
    {
        switch (isSuccess)
        {
            case true when error != Error.None:
                throw new InvalidOperationException();

            case false when error == Error.None:
                throw new InvalidOperationException();

            default:
                IsSuccess = isSuccess;
                Error = error;
                break;
        }
    }

    public bool IsSuccess { get; }
    public bool IsFailure => !IsSuccess;
    public Error Error { get; }

    public static Result Success() => new(true, Error.None);
    public static Result Failure(Error error) => new(false, error);

    public static Result<T> Success<T>(T value) => new(value, true, Error.None);
    public static Result<T> Failure<T>(Error error) => new(default, false, error);

    public static Result<T> Create<T>(T? value) =>
        value is not null ? Success(value) : Failure<T>(Error.NullValue);
}

Il-constructor jipproteġi żewġ regoli importanti: result ta’ suċċess ma jistax ikun fih error, u result li falla jrid ikun fih wieħed. Meta dawn l-istati jinżammu validi, jiġu evitati objects kontradittorji bħal result ta’ suċċess b’error message.

Għal operazzjonijiet li jirritornaw data, il-verżjoni generic taħżen il-value ta’ suċċess:

public class Result<T> : Result
{
    private readonly T? _value;

    protected internal Result(T? value, bool isSuccess, Error error) : base(isSuccess, error)
        => _value = value;

    [NotNull]
    public T Value => _value! ?? throw new InvalidOperationException("Result has no value");

    public static implicit operator Result<T>(T? value) => Create(value);
}

Il-property Value hija disponibbli għal results ta’ suċċess. Jekk tiġi aċċessata wara falliment, tintefa’ InvalidOperationException, għalhekk il-konsumaturi għandhom l-ewwel jispezzjonaw l-istat tar-result. Il-pattern jagħmel il-contract aktar ċar, iżda xorta jiddependi fuq użu korrett fil-call site.

Tirritorna user mingħajr ma tirritorna null

Lookup method issa jista’ jikkomunika b’mod espliċitu fallimenti mistennija differenti:

public Result<User> GetUserById(int userId)
{
    if (userId <= 0)
        return Result.Failure<User>(Error.InvalidUserId);

    var user = _userRepository.FindById(userId);
    if (user == null)
        return Result.Failure<User>(Error.UserNotFound);

    return Result.Success(user);
}

Jekk nassumu li InvalidUserId u UserNotFound huma definiti fl-error catalog, ir-return type jgħid lill-konsumaturi li l-method għandu żewġ stati possibbli. Min isejjaħ jista’ jittrattahom direttament:

var result = GetUserById(123);

if (result.IsSuccess)
{
    Console.WriteLine($"User found: {result.Value.Name}");
}
else
{
    Console.WriteLine($"Failed to retrieve user: {result.Error.Message}");
}

Dan huwa aktar deskrittiv milli tiċċekkja jekk User ritornat huwiex null. Iżomm ukoll ir-riżultati tan-negozju mistennija viżibbli fil-control flow normali.

Result ma jiħux post l-exceptions

L-exceptions jibqgħu xierqa għal kundizzjonijiet mhux mistennija li ma jistgħux jiġu ttrattati bħala parti mill-operazzjoni normali. Il-gwida ta’ .NET tirrakkomanda li jiġu evitati exceptions għal kundizzjonijiet ta’ rutina, filwaqt li jintuża exception handling għal avvenimenti ġenwinament eċċezzjonali. (learn.microsoft.com)

Distinzjoni prattika hija:

  • Irritorna result għal validation failures, records neqsin, kunflitti u riżultati mistennija oħra.
  • Itfa’ exceptions għal programming errors, stat invalidu ta’ object, jew infrastructure failures mhux mistennija li l-layer attwali ma jistax jittratta b’mod sinifikanti.

Ir-Result Pattern mhuwiex primarjament dwar li telimina null jew exceptions minn kullimkien. Huwa dwar li tagħti type espliċitu lill-fallimenti mistennija u tagħmel il-method contracts aktar faċli biex jinftiehmu.

Żewġ trattamenti prattiċi addizzjonali tal-pattern huma disponibbli fuq https://www.red-gate.com/simple-talk/development/dotnet-development/the-result-pattern-in-asp-net-core-minimal-apis/ u https://medium.com/@emrecantopaloglu/the-result-pattern-in-net-a-simple-guide-73a8f1b89d73.

Referenzi