我有这段代码:
int? sum=Enumerable.Range(0, 1000).Sum((i) =>
{
if (((i % 3) == 0) && ((i % 5) == 0))
return i;
return null;
});
此代码无法编译,它会生成此错误:
错误CS0266:无法隐式转换类型'decimal?' 'int?'。存在显式转换(您是否错过了演员?)
我将代码更改为:
int? sum=Enumerable.Range(0, 1000).Sum<int>((int i) =>
{
if (((i % 3) == 0) && ((i % 5) == 0))
return i;
return null;
});
但编译器错误仍然存在。
我不明白,Enumerable.Range
返回一个整数集合,显然我想要返回int的Sum
? ,否则i
应为decimal
,正如我所说,如果我在int
之前插入i
并不重要。
当我sum
decimal?
变量时,它会编译。
我认为这可能是一个单声道错误,我在this错误列表中看不到类似内容。
所以我错过了什么或者我应该提交错误吗?
我的单声道版本是4.4.1,我的操作系统是Arch linux x64。
答案 0 :(得分:1)
绝对是一个错误,因为VisualStudio附带的编译器不会发生这种情况。
单声道的解决方法似乎是返回default(int?)
而不是null。
int? sum=Enumerable.Range(0, 1000).Sum((i) =>
{
if (((i % 3) == 0) && ((i % 5) == 0))
return i;
return default(int?);
});
答案 1 :(得分:1)
这是一个精简的测试程序,用于演示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从表达式更好的转换,但它没有解决这种情况。它给出了一些特定的场景,其中一个重载优于另一个,但是为了在这里工作,它需要根据它所谓的推断的返回类型进行操作。但是,null
没有类型。没有推断的返回类型。这使得我们在语言规范中没有任何内容描述了使用哪个重载。
5.0语言规范说调用不明确。编译器应该生成错误。
不幸的是,语言规范并未描述Microsoft的编译器。这迫使Mono实现了试图模仿微软行为的丑陋黑客,因为拒绝编译用微软编译器编译的代码是让人们不认真对待Mono的简单方法,正如你在对这个问题的回答中所看到的那样。那些丑陋的黑客并不完美,除非规范正确描述了他们设计的语言,否则可能永远都不会。
非官方的C#6.0语言规范已在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?>
更好的转换目标。() => null
到Func<int?>
的转化优于Func<decimal?>
。6.0语言规范可能会说代码有效,而Roslyn是对的。但该语言规范尚未最终确定。
由于C#6.0语言规范还没有最终确定,Mono很难准确地实现它,而且它似乎还没有完成。
无论您将其视为Mono错误或缺少功能的解决方法,还是将其作为代码的修复程序以使其与C#5.0更兼容,结果都是相同的:使用default(int?)
代替{正如史蒂夫所建议的那样,{1}}会使它发挥作用。在C#5.0中,它的工作方式是给匿名函数一个推断的返回类型null
,解决所需重载的歧义。