Noah Stahl
Noah Stahl

Reputation: 7553

C# - Implicitly convert or infer generic return type

I've started using the pattern of a generic result type as a wrapper object that includes a return value and information about the operation like whether it succeeded. Here's an example:

public class Researcher
{
    public Result<string> LearnThing(bool factDesired)
    {
        // Validate and bail if failure
        var validationResult = ValidateIntentions(factDesired);
        if (!validationResult.Succeeded)
        {
            // Ideally: return validationResult directly without converting to new instance
            return Result.Failure<string>(validationResult.Error);
        }

        return StateOpinion();
    }

    public Result<string> StateOpinion()
    {
        return Result.Success("I like turtles");
    }

    public Result<bool> ValidateIntentions(bool factDesired)
    {
        if (factDesired)
        {
            // Ideally: no <bool> required, infer default instead
            return Result.Failure<bool>("Only opinions here, sorry");
        }
        else
        {
            return Result.Success(true);
        }
    }
}

public class Result<T>
{
    public bool Succeeded { get; set; }

    public string Error { get; set; }

    public T Value { get; set; }
}

// Static helpers
public static class Result
{
    public static Result<T> Success<T>(T value)
    {
        return new Result<T> { Succeeded = true, Value = value };
    }

    public static Result<T> Failure<T>(string error)
    {
        return new Result<T> { Succeeded = false, Error = error };
    }
}

Here, the generic Result<T> class is used on each method and a static helper class provides a mechanism to create the results with success status implied. So far, this is working nicely.

The one bikeshedding annoyance I have with this approach is that I need to restate the <T> often where ideally it could be inferred or when I no longer care about T Value (which would be default) and only about Error, as in the case of failures. I somewhat understand that C# doesn't infer from method return types, but I have come across some mentions of implicit operators that seem to allow some cool tricks that I don't quite understand.

So, I humbly submit the question to the C# wizards among you: is there some variation or magic I can add to this approach to achieve more type inference and effectively an implicit Result<"I don't care"> for failure results?

Upvotes: 1

Views: 508

Answers (3)

Heinzi
Heinzi

Reputation: 172220

You can use exactly the same technique as described in the article you linked to.

Step 1: You define a non-generic helper class for your Failure case:

public class FailureResult
{
    public string Error { get; }
    
    public FailureResult(string error) { Error = error; }
}

Step 2: You change your static helper to return a FailureResult instead of a Result<T>:

public static class Result
{
    ...

    public static FailureResult Failure(string error)
    {
        return new FailureResult(error);
    }
}

Step 3: You define an implicit conversion from FailureResult to Result<T>:

public class Result<T>
{
    ...

    public static implicit operator Result<T>(FailureResult result) 
    {
        return new Result<T> { Succeeded = false, Error = result.Error };
    }
}

Step 4: Profit

public Result<bool> ValidateIntentions(bool factDesired)
{
    if (factDesired)
    {
        // No <bool> required!
        return Result.Failure("Only opinions here, sorry");
    }
    else
    {
        return Result.Success(true);
    }
}

(fiddle)

Upvotes: 3

TheGeneral
TheGeneral

Reputation: 81493

You could get around the bool issue with just an overload:

public static Result<bool> Failure(string error)
{
   return new Result<bool> { Succeeded = false, Error = error };
}

Allowing this:

return Result.Failure("Only opinions here, sorry");

As for:

// Ideally: return validationResult directly without converting to new instance
return Result.Failure<string>(validationResult.Error);

You could use an implicit operator:

public static implicit operator Result<string>(Result<T> result)
{ 
   return Result.Failure<string>(result.Error);
}

Which would allow you to do:

if (!validationResult.Succeeded)
{
    // Ideally: return validationResult directly without converting to new instance
    return validationResult;
}

Though I personally wouldn't do this, it's unexpected and misusing the language feature.

You could however use an instance method or extension method:

public Result<string> AsError()
{        
   return Result.Failure<string>(Error);
}

In all honesty, I think what you have is declarative and not trying to be magic. I would just stick with some helper (extension) methods if need be.

Upvotes: 1

Andy
Andy

Reputation: 13527

Maybe something like this:

    public class Result<T>
    {
        public bool Succeeded { get; set; }
        public string Error { get; set; }
        public T Value { get; set; }
        public bool HasValue { get; protected set; } = true;
    }

    public class Result : Result<object>
    {
        public Result() { HasValue = false; }
    }

    public static class ResultFactory
    {
        public static Result<T> Success<T>(T value)
        {
            return new Result<T> { Succeeded = true, Value = value };
        }

        public static Result Success()
        {
            return new Result { Succeeded = true };
        }

        public static Result Failure(string error)
        {
            return new Result { Succeeded = false, Error = error };
        }
    }

This allows you not to have to "kludge" in a bool somewhere to to make it fit your pattern. Sure you are creating a null object, but that is more-or-less tucked away.

Upvotes: 0

Related Questions