4

I have a class that indicates if a call to a method was successful (with a value) or has an error.

internal sealed class ValueOrError<TValue>
{
    public bool IsError => _error is not null;
    public bool IsSuccess => !IsError;

    private TValue? _value;
    private Error? _error;

    private ValueOrError() { }

    public TValue? Value =>
        _error is null
        ? _value
        : throw new InvalidOperationException("Is an error type");

    public Error Error => _error ?? throw new InvalidOperationException("Not an error type");

    public static ValueOrError<TValue> FromValue(TValue? result) =>
        new ValueOrError<TValue> { _value = result };

    public static ValueOrError<TValue> FromError(Error error) =>
        new ValueOrError<TValue> { _error = error ?? throw new ArgumentNullException(nameof(error)) };

    public static implicit operator ValueOrError<TValue>(TValue value) => ValueOrError<TValue>.FromValue(value);
    public static implicit operator ValueOrError<TValue>(Error error) => ValueOrError<TValue>.FromError(error);
}

I can cast implicitly just fine unless TValue is an IEnumerable<T>.

// Works
ValueOrError<int> x = 1;
// Fails
ValueOrError<IEnumerable<int>> y = Enumerable.Empty<int>();

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'AppLayer.ValueOrError'

However, I can create a result of that type "the long way"

c# // Works ValueOrError<IEnumerable<int>>.FromValue(Enumerable.Empty<int>());

In case it is useful, the source for Error is

public class Error
{
    public string Message { get; }

    [JsonConstructor]
    public Error(string message)
    {
        Message = message ?? throw new ArgumentNullException(nameof(message));
    }
}
Peter Morris
  • 20,174
  • 9
  • 81
  • 146
  • Implict casts don't work from or to interfaces – krimog May 30 '22 at 11:33
  • Does this answer your question? [Why isn't it possible to define implicit cast operator from interface to class?](https://stackoverflow.com/questions/12533748/why-isnt-it-possible-to-define-implicit-cast-operator-from-interface-to-class) – shingo May 30 '22 at 11:57
  • @krimog Thanks. If I implicitly call `ToArray()` first on the assignment then it works. If you'd like to add this as an answer I will happily accept it. – Peter Morris May 30 '22 at 12:06

1 Answers1

1

If you look at the C# specifications about User-defined conversions:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/conversions#105-user-defined-conversions

One of the prerequisites to use implicit or explicit operators is that

Neither S₀ nor T₀ is an interface_type.

So, if you really want to use implicit conversion, you'll have to convert your interface reference into a class reference (by calling .ToArray() for example).

Or you can create a static class to take advantage of type infering:

internal static class ValueOnError
{
    public static ValueOnError<TValue> FromValue<TValue>(TValue value)
        => ValueOnError<TValue>.FromValue(value);
}

Then you could just write

var y = ValueOnError.FromValue(Enumerable.Empty<int>());
krimog
  • 1,257
  • 11
  • 25