Home > Blockchain >  C# Optional<TObject> as a return type?
C# Optional<TObject> as a return type?

Time:11-08

Often i have a method where i want to return the error if something goes wrong, and instead of returning null, I want something less prone to errors at runtime and more easy to consume. Is there anything already done in .Net or maybe a nuget package? Maybe have a constructor with optional parameters or object initializer would be enough?

This would have been the first approach but then every new Dto has to either have these Error property or inherit from a base class.

 if (condition)
 {
     return new MyDto(null, error);
 }
 return new MyDto(someVariable, null); 

So I've made this class to use a return type:

public class Optional<TObject> where TObject : class
{
   public Optional(TObject? value)
   {
       Value = value;
   }

   public Optional(String error)
   {
       Error = error;
   }

   public TObject? Value { get; }
   public String Error { get;} = String.Empty;
   public Boolean IsError => !String.IsNullOrEmpty(Error);
}

I return it in the method:

 if (condition)
 {
     return new Optional(error);
 }

 return new Optional(new MyDto(someVariable));

And then consume it like this:

var result = await myService.GetSomethingAsync();
if(result.IsError)
{
   await DisplayAlert("error", result.Error, "Ok");
}
else
{
   await DoSomethingElse(result.Value);
}

CodePudding user response:

By creating a small class hierarchy, you could ensure that the Value property is only available when no error occurred

public abstract class Result
{
    public virtual string Message => null;

    public static Error Error(string message) => new Error(message);
    public static Okay<T> Okay<T>(T value) where T : class => new Okay<T>(value);
}

public class Error : Result
{
    public Error(string errorMessage) => Message = errorMessage;

    override public string Message { get; }
}

public class Okay<T> : Result
    where T : class
{
    public Okay(T value) => Value = value;

    public T Value { get; }
}

Usage

Result result = Result.Error("Something went wrong");
// OR
Result result = Result.Okay(new MyDto(someVariable));

if (result is Okay<MyDto> dtoResult) {
    Console.WriteLine(dtoResult.Value);
} else {
    Console.WriteLine(result.Message);
}

Or by using a recursive pattern, we can retrieve the value into a variable directly

if (result is Okay<MyDto> { Value: var dto }) {
    Console.WriteLine(dto);
} else {
    Console.WriteLine(result.Message);
}

Note that I have declared the Message property in the abstract base class Result, so that you don't have to cast to the Error type to get the message.

I used null as defualt value for the error message, as it allows us to write

Console.Writeline(result.Message ?? "okay");

CodePudding user response:

This OneOf recommendation you got looks promising. I will personally have a look at it later.

What I do with my services is to standardize the result they return by using a SvcResult class or an inherited class.

Example:

public class SvcResult
{
    public List<Error> Errors { get; } // Error is a class of my own.  Add set; if deserialization is needed.
    public bool Success { get; } // Add set; if deserialization is needed.

    // Then parameterless constructor for a successful result.
    // Then parameterized constructor to receive errors for a failed result.
}

That is the class for side-effect service calling. If The service returns data, I derive from the above to create DataSvcResult:

public class DataSvcResult<TResult> : SvcResult
{
    public TResult Data { get; }

    // Add constructor that receives TResult for a successful object result.
    // Expose base class constructor that takes errors.
}

Basically that's what I do. But that OneOf thing, though. Looks super intersting.

  • Related