Peter Morris
Peter Morris

Reputation: 23284

Cannot implicitly cast IEnumerable

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));
    }
}

Upvotes: 4

Views: 242

Answers (1)

krimog
krimog

Reputation: 1276

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>());

Upvotes: 1

Related Questions