C# mono 出现奇怪的编译器错误

strange compiler error with C# mono

我有这个代码:

int? sum=Enumerable.Range(0, 1000).Sum((i) =>
        {
            if (((i % 3) == 0) && ((i % 5) == 0))
                return i;
            return null;
        });

此代码无法编译,它会生成此错误:

error CS0266: Cannot implicitly convert type 'decimal?' to 'int?'. An explicit conversion exists (are you missing a cast?)

我将代码更改为:

int? sum=Enumerable.Range(0, 1000).Sum<int>((int i) =>
        {
            if (((i % 3) == 0) && ((i % 5) == 0))
                return i;
            return null;
        });

但编译器错误仍然存​​在。

我不明白,Enumerable.Range returns 一个整数集合,很明显我想要 Sum 那个 returns 整数? ,否则 i 应该是 decimal 并且正如我所说,我是否在 i 之前插入 int 并不重要。

当我将 sum 设为 decimal? 变量时,它会编译。

我认为这可能是一个单声道错误,我在 this 错误列表中没有看到类似的错误。

那么我是不是遗漏了什么或者我应该提交错误?

我的单声道版本是 4.4.1,我的 OS 是 Arch linux x64.

绝对是一个错误,因为 VisualStudio 附带的编译器不会发生这种情况。

mono 的解决方法似乎是 return default(int?) 而不是 null。

int? sum=Enumerable.Range(0, 1000).Sum((i) =>
    {
        if (((i % 3) == 0) && ((i % 5) == 0))
            return i;
        return default(int?);
    });

这是一个精简的测试程序,它演示了 BCL 之外的问题:

using System;
using static System.Console;
static class Program {
  static void Test1(Func<int?> f)     { WriteLine("Test1(Func<int?>)");     }
  static void Test1(Func<decimal?> f) { WriteLine("Test1(Func<decimal?>)"); }
  static void Test2(Func<decimal?> f) { WriteLine("Test2(Func<decimal?>)"); }
  static void Test2(Func<int?> f)     { WriteLine("Test2(Func<int?>)");     }
  static void Main() {
    Test1(() => null);
    Test2(() => null);
  }
}

使用 Mono (4.4) 编译时,打印:

Test1(Func<decimal?>)
Test2(Func<int?>)

使用 Roslyn 编译时,打印出:

Test1(Func<int?>)
Test2(Func<int?>)

在查看 C# 5.0 语言规范时,最终应该选择哪个重载归结为 7.5.3.3 更好地从表达式 转换,但它没有解决这种情况.它给出了一些特定的场景,其中一个重载比另一个更好,但要让它在这里工作,它需要根据它所谓的 推断的 return 类型 采取行动。但是,null 没有类型。没有推断的 return 类型。这使我们在描述要使用的重载的语言规范中没有任何内容。

5.0 语言规范说调用不明确。编译器应该生成一个错误。

不幸的是,语言规范没有描述微软的编译器。这迫使 Mono 实施试图模仿微软行为的丑陋黑客,因为拒绝编译确实使用微软编译器编译的代码是让人们不认真对待 Mono 的简单方法,正如您在对这个问题的回应中所看到的.那些丑陋的 hack 并不完美,而且可能永远不会完美,除非规范正确描述了他们设计的语言。

非官方的 C# 6.0 语言规范已在 public https://github.com/ljw1004/csharpspec/blob/gh-pages/README.md 上制定。它描述了对重载解析规则的一些调整(寻找 7.5.3.3 更好的表达式转换和 7.5.3.5 更好的转换目标):

  • int? 是比 decimal? 更好的转换目标,因为存在从 int?decimal? 的隐式转换,但反之则不然。
  • 因此,Func<int?> 是比 Func<decimal?> 更好的转换目标。
  • 因此,从 () => nullFunc<int?> 的转换优于 Func<decimal?>

6.0 语言规范可能会说代码有效,Roslyn 是正确的。但该语言规范尚未最终确定。

由于C#6.0语言规范尚未最终确定,Mono很难准确实现,目前看来还没有完成。

无论您将其视为 Mono 错误或缺失功能的解决方法,还是将其视为代码修复以使其与 C# 5.0 更兼容,结果都是一样的:改用 default(int?) null,正如史蒂夫所建议的那样,将使它起作用。在 C# 5.0 中,它的工作方式是通过为匿名函数提供一个 int? 的推断 return 类型,以有利于所需重载的方式解决歧义。