简体   繁体   中英

Implicit conversion with a generic type failing for an interface type

        private struct Maybe<T>
        {
            private readonly T value;
            private readonly bool hasValue;

            private Maybe(T value)
            {
                this.value = value;
                hasValue = true;
            }

            public static implicit operator Maybe<T>(T value) =>
                value == null ? new Maybe<T>() : new Maybe<T>(value);
        }

        private static Maybe<byte> OK()
        {
            return 5;
        }

        private static Maybe<IEnumerable<byte>> NotOK()
        {
            var e = new[] { 1, 2, 3 }.Select(x => (byte)x);
            Console.WriteLine(e.GetType().Name);
            return e;
        }

Fiddle (don't use): https://dotnetfiddle.net/NxAw9l

Updated fiddle: https://dotnetfiddle.net/NrARTl

Some generic type is failing for implicit conversion at above code. See the Ok() and NotOk() function calls and return types. A complex generic type is failing and I dont' understand why. I have simplified this from a function of a return type of IEnumerable<IEnumerable<T>> . This IEnumerable<T> still fails. I think If I can understand why this fails, I'd solve the real one too I suppose. Thanks for your help and time.

Here is the error message if you'd like:

Error    CS0029    Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<byte>' to 'Maybe<System.Collections.Generic.IEnumerable<byte>>'

Update: Returning Byte[] from the NotOK() can't work because in my real source code I have a LINQ query I have to depend on its lazy-deferred execution (ie It has to be strictly returning IEnumerable) (see alike answer => https://stackoverflow.com/a/63880804/5917087 ).

The C# standard currently does not allow implicit conversions from or to interfaces.

This is a well-known problem when implementing a Maybe<T> (or Optional<T> , as it is often called) type in C#. There is an ongoing discussion about this on the C# language github forum:


As a workaround, you could make the Maybe<T> constructor internal and add a static non-generic helper class:

private static class Maybe
{
    public static Maybe<T> From<T>(T value) => 
        value == null ? new Maybe<T>() : new Maybe<T>(value);
}

which allows you to use type inference and write Maybe.From(a) , which is a bit shorter than new Maybe<IEnumerable<byte>>(a) .

I'm going to extend @Heinzi's answer:

You could also use extension methods:

static class MaybeExtensions
{
    public static Maybe<T> AsMaybe<T>(this T value)
    {
        return new Maybe<T>(value);
    }

    public static Maybe<TResult> AsMaybe<T, TResult>(this T value)
        where T : unmanaged
        where TResult : unmanaged
    {
        return new Maybe<TResult>(Unsafe.As<T, TResult>(ref value));
    }
}

And in your caller methods, you could use them like:

private static Maybe<IEnumerable<byte>> NotOK()
{
    var e = new[] { 1, 2, 3 }.Select(x => (byte)x);
    return e.AsMaybe();
}

private static Maybe<byte> OK()
{
    return 5.AsMaybe<int, byte>();
}

// Alternatively
private static Maybe<byte> OK()
{
    return ((byte)5).AsMaybe();
}

You need the AsMaybe<T, TResult> overload for value types that can convert to each other. For example when you do 5.AsMaybe() it returns Maybe<int> , if your method's return type is Maybe<byte> you will need to convert Maybe<int> to Maybe<byte> , and the overload does that for you.

Now, the type conversion operator in Maybe<T> becomes redundant. And you can use var instead of full type name:

Maybe<int> obj1 = 5; // use operator
var obj2 = 5.AsMaybe(); // use extension method

您无法定义与接口类型之间的转换,如果您将示例更改为使用List<T>而不是IEnumerable<T>它将编译 - https://docs.microsoft.com/en-us/dotnet /csharp/language-reference/language-specification/conversions#user-defined-conversions

Change this :

    private static Maybe<IEnumerable<byte>> NotOK()
    {
        IEnumerable<byte> a = new byte[] { 1, 2 };
        return a;
    }

into this :

    private static Maybe<IEnumerable<byte>> NotOK()
    {
        var a = new byte[] { 1, 2 };
        return a;
    }

The struct :

private struct Maybe<T>
{
    private readonly T value;
    private readonly bool hasValue;

    private Maybe(T value)
    {
        this.value = value;
        hasValue = true;
    }

    public static implicit operator Maybe<T>(T value)
    {
        return value == null ? new Maybe<T>() : new Maybe<T>(value);
    }
    
    public bool HasValue(){
        return this.hasValue;   
    }
    
    public T GetValue(){
        return this.value;  
    }
    
}
private static Maybe<byte> OK()
{
    return 5;
}
private static Maybe<IEnumerable<byte>> NotOK()
{
    Byte[] a = new byte[] { 1, 2 };
    Console.WriteLine(a.GetType().Name);
    return a;
}

Usage :

public static void Main(string[] args){
        
    var t1 = OK();
    var t2 = NotOK();
    
    Console.WriteLine("t1 type is "  + t1.GetType().Name);
    Console.WriteLine("t2 type is "  + t2.GetType().Name);
    
    if(t2.HasValue())
    {
        List<byte> search = t2.GetValue().Where(b => b > 0).ToList();
        foreach(byte num in search){
            Console.WriteLine(num); 
        }
    }
}

The reference IEnumerable<byte> a doesn't change the type, you can continue to var or byte[] and the query with LINQ , after, see in the full example

See the full example : https://dotnetfiddle.net/V8RHQe

IEnumerable is an interface. Compiler does not know which type to work with. Put ToList() to end of your select like follows :

private static Maybe<IEnumerable<byte>> NotOK()
{
        var e = new[] { 1, 2, 3 }.Select(x => (byte)x).ToList();
        Console.WriteLine(e.GetType().Name);
        return e;
}

To understand what is happening try to create a method like follow in your class and watch compiler to cry :)

public static implicit operator Maybe<IEnumerable<T>>(IEnumerable<T> value)
{
        return value == null ? new Maybe<IEnumerable<T>>() : new Maybe<IEnumerable<T>>(value);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM