我正在试图找出编译器为什么会遇到此函数的问题。它给了我“并非所有代码路径返回值”错误,但是我看不到控制流将传递给if( a )
表达式而没有a
为真的情况(所以{{1}这是多余的,但编译器似乎并没有意识到这一点。
if( a )
立即修复只是完全删除public static Boolean Foo(Boolean x)
{
Boolean a = false;
if( x )
{
a = true;
}
else
{
try
{
SomethingThatMightThrow();
Assert.IsFalse( a );
return a;
}
catch(Exception)
{
a = true;
}
}
if( a )
{
return x;
}
}
保护语句并立即删除if( a )
- 但为什么编译器会抱怨,即使它应该能够静态地证明所有可能的代码路径都会命中return x
声明?至关重要的是,没有循环,这通常是它无法证明return
的主要原因。
我正在使用VS2015 Update 3.
答案 0 :(得分:52)
当您到达功能结束时,a
有false
支持的方案。这种情况是在您调试代码并使用调试器将 a
设置为false
时。
C#编译器规则设计简单。在C#借用的语言中,函数可能无法返回任何内容的问题是编译器警告无法解决的问题。有太多的误报,因此警告被调整为仅警告明显的案例,引入假阴性。 C#的规则是一种妥协,如果对于熟悉规则的人来说,误报是可以接受的,并且假阴性是不可接受的。您可以保证,如果您的函数的代码路径没有返回值,编译器会检测到它。
这些简单规则的一部分是不考虑变量的值。即使a
在静态上保证为true
,编译器也无法利用这一事实。
@PetSerAl已经引用了C#语言规范中的相关措辞:
8.1终点和可达性
[...]
要确定特定语句或端点是否可访问,编译器将根据为每个语句定义的可访问性规则执行流分析。流分析考虑了控制语句行为的常量表达式(第7.19节)的值,但不考虑非常量表达式的可能值。换句话说,出于控制流分析的目的,给定类型的非常量表达式被认为具有该类型的任何可能值。
这是最新发布的语言规范的一部分,即C#5.0。您正在使用的版本,C#6.0(VS2015提供的版本),还没有已发布的规范,因此措辞可能略有不同,但正如您的编译器所示,实际上相同的规则仍然适用。
答案 1 :(得分:26)
运行时与编译时间
你的例子太复杂了。这不会编译:
static int Test()
{
bool f = true;
if (f)
{
return 1;
}
else
{
//Not all code paths return a value
}
}
另一方面,这将:
static int Test()
{
if (true)
{
return 1;
}
else
{
//No error
}
}
我猜测无论有什么验证机制都没有足够的逻辑来推断运行时变量的内容。编译时变量没问题。
答案 2 :(得分:6)
我认为编译器对代码进行了非常简单的分析,因此必须明确给出返回值。
这可能看起来像是一个错误的决定,但在处理复杂的代码时,返回的值可能并不清楚。所以,程序员被迫退回。
你的例子可以减少到最低限度:
public static Int32 Main(String[] args)
{
var printUsage = true;
if (printUsage)
{
return 0;
}
// return nothing, so compiler is not happy
}
虽然仍然收到错误。
注意:如果您使用Resharper,它将执行您想要的分析并相应地发出警告:
if (printUsage) // Warning: expression is always true