【问题标题】:Implicit conversion with a generic type failing for an interface type接口类型的泛型类型的隐式转换失败
【发布时间】:2020-09-11 14:56:32
【问题描述】:
        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;
        }

小提琴(不要使用):https://dotnetfiddle.net/NxAw9l

更新小提琴:https://dotnetfiddle.net/NrARTl

某些泛型类型在上述代码中的隐式转换失败。请参阅 Ok()NotOk() 函数调用和返回类型。一个复杂的泛型类型失败了,我不明白为什么。我已经从返回类型为IEnumerable&lt;IEnumerable&lt;T&gt;&gt; 的函数中简化了这一点。这个IEnumerable&lt;T&gt; 仍然失败。我想如果我能理解为什么会失败,我想我也会解决真正的问题。感谢您的帮助和时间。

如果您愿意,这里是错误消息:

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

更新: 从 NotOK() 返回 Byte[] 不起作用,因为在我的真实源代码中,我有一个 LINQ 查询,我必须依赖它的延迟延迟执行(即它有严格返回 IEnumerable)(参见相同答案 => https://stackoverflow.com/a/63880804/5917087)。

【问题讨论】:

  • 你的意思是让你的 dotnetfiddle 网络核心?因为作为net4.7.2的错误是完全不同的
  • 我没有说实话。我只是把它放在那里帮助别人加快速度。我将 C# 6.0 与 .NET 4.8 一起使用。
  • 你可能应该 ifx 小提琴所以它显示相同的错误然后
  • 当我打开链接时,它会为我显示 4.7.2。
  • 如果你尝试运行它会发生什么?我得到Compilation error (line 16, col 53): ; expected

标签: c# type-inference


【解决方案1】:

C# 标准目前不允许从接口或到接口的隐式转换。

在 C# 中实现 Maybe&lt;T&gt;(或 Optional&lt;T&gt;,通常称为)类型时,这是一个众所周知的问题。在 C# 语言 github 论坛上有关于此的持续讨论:


作为一种解决方法,您可以创建 Maybe&lt;T&gt; 构造函数 internal 并添加一个静态非泛型帮助器类:

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

它允许您使用类型推断并编写Maybe.From(a),它比new Maybe&lt;IEnumerable&lt;byte&gt;&gt;(a) 短一点。

【讨论】:

  • 啊,这是我现在正在做的最接近的事情。请参阅此扩展答案 => dotnetfiddle.net/btd13e 我认为我无法像您的答案那样认为是一种解决方法。
【解决方案2】:

我将扩展@Heinzi 的答案:

你也可以使用扩展方法:

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

在你的调用者方法中,你可以像这样使用它们:

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

对于可以相互转换的值类型,您需要 AsMaybe&lt;T, TResult&gt; 重载。例如,当您执行5.AsMaybe() 时,它会返回Maybe&lt;int&gt;,如果您的方法的返回类型是Maybe&lt;byte&gt;,则您需要将Maybe&lt;int&gt; 转换为Maybe&lt;byte&gt;,而重载会为您执行此操作。

现在,Maybe&lt;T&gt; 中的类型转换运算符变得多余了。您可以使用var 代替完整的类型名称:

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

【讨论】:

  • 这看起来很酷,确实对于函数式编程风格的扩展方法的流畅使用非常有用。
【解决方案3】:

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

【讨论】:

  • 呃,这真的很烦人,因为我大量使用 Linq 并且非常需要 IEnumerable 接口访问。而且我认为您发送的上面的链接描述了与链接中的问题类似的内容(这不适用于我的情况,恕我直言,我的更多是关于进入泛型类型字段的接口,而不是转换类型是接口) => stackoverflow.com/questions/5746161/…
  • @EmirhanÖzlen:你不是唯一一个因为他们的Optional&lt;T&gt; 实现而错过这个功能的人。看看github.com/dotnet/roslyn/issues/14186
  • @Heinzi 我已经开始为这个问题悬赏。似乎您的答案是一个不错的候选人。如果你愿意,你可以写下这个问题的一般答案。感谢参考
  • @EmirhanÖzlen:我将我的评论扩展为答案,并添加了我能想到的最佳解决方法。让我们希望有人找到更好的解决方案。 :-)
【解决方案4】:

改变这个:

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

进入这个:

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

结构:

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

用法:

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

引用IEnumerable&lt;byte&gt; a不会改变类型,你可以继续varbyte[]LINQ查询,之后,见完整示例

查看完整示例: https://dotnetfiddle.net/V8RHQe

【讨论】:

  • 不错的收获。我很惊讶这行得通,即使Maybe&lt;T&gt; 不是协变的。谁能解释一下为什么?
  • 嗯。我认为这是可行的,因为我的假设是,返回有点在幕后做一些工作,比如首先将 Byte[] 转换为 Maybe 然后它与 Maybe 兼容,尽管事情是同时的这个答案是完全有效的,不能解决我的问题,因为我严格限制返回 IEnum 并且这个 byte[] hack 只适用于我的简化答案,而不适用于产品源代码。我应该给出一个从 LINQ 查询中返回某些内容的严格示例。
  • 这个越来越复杂了,好像不支持
【解决方案5】:

IEnumerable 是一个接口。编译器不知道使用哪种类型。将ToList() 放在您选择的末尾,如下所示:

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

要了解正在发生的事情,请尝试在您的类中创建一个类似 follow 的方法并观察编译器哭泣 :)

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    相关资源
    最近更新 更多