如何使用结构删除编译器错误:"Use of unassigned local variable"
How to remove compiler error with struct: "Use of unassigned local variable"
C# 编译器有点...过时...并且不会进行静态分析。所以它打破了 看似 正确的代码是这样的:
MyStruct s;
bool inited = false;
foreach( Something foo in ACollection )
{
if( foo.Test() )
continue;
if( inited )
s.DoSomething();
else
{
s = foo.GetMeAnS();
inited = true;
}
}
注意:不寻常的问题是 "s" 是一个结构。如果它是 class,我会简单地将它初始化为 null。这个结构没有有意义的 "uninited" 状态,我不想支付初始化我立即丢弃的东西的性能成本,只是为了满足一个弱编译器。
代码(应该)完全正确:在 s 被初始化之前不可能访问 s。 (我 copy/pasted 来自实际代码,但为了简单起见,我删除了长方法名称)。
Mono 中的 C# 编译器过去允许这样做,但现在不允许了。除了编译器,什么都没有改变,编译器现在对未分配的变量给出错误。
有没有代码告诉它闭嘴,管好自己的事? :) 我不想 fiddle 更改编译器设置(如果可能的话),因为代码是由其他人编译的 people/orgs - 我更喜欢用代码的方式来解决问题。
Is there a code way to tell it to shut up and mind its own business?
编译器的任务是实现C#规范。您编写的代码 不应该 根据 C# 规范进行编译。无需明确分配 s
即可访问 s.DoSomething()
调用,因此您的代码已损坏。那不是编译器的错。如果 Mono 编译器 使用 允许它,那是一个错误,现在显然已经修复了。
最简单的固定方式当然是明确赋值:
MyStruct s = new MyStruct(); // Value will never actually be used
在很多情况下,我们(作为人类)可以判断某事永远不会发生,但编译器不能。这是另一个例子:
public int Foo(int input)
{
if (input >= 0)
{
return input;
}
else if (input < 0)
{
return -input;
}
// This is still reachable...
}
我们 知道每个 int
输入都会进入其中一个 if
主体,但编译器仍会(正确地)给出编译错误在上面的代码中,因为右大括号是可到达的并且它是一个非空方法。
你声称 "The code (should be) fully correct" 是根据你的推理,而不是 C# 规范......编译器只关心后者。
需要注意的一件事:规范甚至不关心我们在某些情况下确实将 inited
设置为 true 的事实。即使它 always 的值为 false
,它仍然只是一个局部变量,而不是常量表达式。这是一个简单的例子,说明没有循环:
static void Main()
{
int x;
bool condition = false;
if (condition)
{
Console.WriteLine(x);
}
}
这样还是报错:"error CS0165: Use of unassigned local variable 'x'"
来自 C# 5 规范的第 8.7.1 节:
The first embedded statement of an if
statement is reachable if the if statement is reachable and the boolean expression does not have the constant value false
.
这里的表达式是condition
,是一个局部变量。局部变量不是技术术语中的常量表达式,即使它永远不会改变。如果您改为将其设为局部常量,它 将 编译:
static void Main()
{
int x;
const bool condition = false;
if (condition)
{
Console.WriteLine(x);
}
}
现在有一个 警告 关于无法访问 if
语句的主体 - 但没有错误。
C# 规范旨在让您永远不能使用未初始化的变量。这不是一个老式的概念。处理这个问题的老式方法(在 C++ 中)是说 "This is undefined behaviour, anything can happen".
奇怪的是,这种态度导致了很多错误,这导致许多编译器自动将(在调试模式下)变量初始化为诸如 0xDEADBEEF 之类的 gem。
至于为什么C#编译器在到达该代码时不进行代码分析来判断变量是否被初始化?
停机问题
问题可以这样改写
bool inited = false;
MyStruct? s;
while (true)
{
foreach( Something foo in ACollection )
foo.Test();
if( inited && false == s.HasValue )
return;
}
这只是稍作改动的代码。但是你可以看到,我已经将你的问题转换为 Halting Problem,其中输入是每个 Something
的状态和 foo.Test()
.
的实现
这被证明在图灵机上是不可判定的,您的 CLR VM 肯定是。
简而言之,你问的是,为什么微软在编写 C# 编译器时没有违反数学和计算机科学的规律。
或者您在问,Microsoft C# 编译器不应该在放弃停止问题之前努力尝试。我的回答是,他们应该多努力?
C# 编译器有点...过时...并且不会进行静态分析。所以它打破了 看似 正确的代码是这样的:
MyStruct s;
bool inited = false;
foreach( Something foo in ACollection )
{
if( foo.Test() )
continue;
if( inited )
s.DoSomething();
else
{
s = foo.GetMeAnS();
inited = true;
}
}
注意:不寻常的问题是 "s" 是一个结构。如果它是 class,我会简单地将它初始化为 null。这个结构没有有意义的 "uninited" 状态,我不想支付初始化我立即丢弃的东西的性能成本,只是为了满足一个弱编译器。
代码(应该)完全正确:在 s 被初始化之前不可能访问 s。 (我 copy/pasted 来自实际代码,但为了简单起见,我删除了长方法名称)。
Mono 中的 C# 编译器过去允许这样做,但现在不允许了。除了编译器,什么都没有改变,编译器现在对未分配的变量给出错误。
有没有代码告诉它闭嘴,管好自己的事? :) 我不想 fiddle 更改编译器设置(如果可能的话),因为代码是由其他人编译的 people/orgs - 我更喜欢用代码的方式来解决问题。
Is there a code way to tell it to shut up and mind its own business?
编译器的任务是实现C#规范。您编写的代码 不应该 根据 C# 规范进行编译。无需明确分配 s
即可访问 s.DoSomething()
调用,因此您的代码已损坏。那不是编译器的错。如果 Mono 编译器 使用 允许它,那是一个错误,现在显然已经修复了。
最简单的固定方式当然是明确赋值:
MyStruct s = new MyStruct(); // Value will never actually be used
在很多情况下,我们(作为人类)可以判断某事永远不会发生,但编译器不能。这是另一个例子:
public int Foo(int input)
{
if (input >= 0)
{
return input;
}
else if (input < 0)
{
return -input;
}
// This is still reachable...
}
我们 知道每个 int
输入都会进入其中一个 if
主体,但编译器仍会(正确地)给出编译错误在上面的代码中,因为右大括号是可到达的并且它是一个非空方法。
你声称 "The code (should be) fully correct" 是根据你的推理,而不是 C# 规范......编译器只关心后者。
需要注意的一件事:规范甚至不关心我们在某些情况下确实将 inited
设置为 true 的事实。即使它 always 的值为 false
,它仍然只是一个局部变量,而不是常量表达式。这是一个简单的例子,说明没有循环:
static void Main()
{
int x;
bool condition = false;
if (condition)
{
Console.WriteLine(x);
}
}
这样还是报错:"error CS0165: Use of unassigned local variable 'x'"
来自 C# 5 规范的第 8.7.1 节:
The first embedded statement of an
if
statement is reachable if the if statement is reachable and the boolean expression does not have the constant valuefalse
.
这里的表达式是condition
,是一个局部变量。局部变量不是技术术语中的常量表达式,即使它永远不会改变。如果您改为将其设为局部常量,它 将 编译:
static void Main()
{
int x;
const bool condition = false;
if (condition)
{
Console.WriteLine(x);
}
}
现在有一个 警告 关于无法访问 if
语句的主体 - 但没有错误。
C# 规范旨在让您永远不能使用未初始化的变量。这不是一个老式的概念。处理这个问题的老式方法(在 C++ 中)是说 "This is undefined behaviour, anything can happen".
奇怪的是,这种态度导致了很多错误,这导致许多编译器自动将(在调试模式下)变量初始化为诸如 0xDEADBEEF 之类的 gem。
至于为什么C#编译器在到达该代码时不进行代码分析来判断变量是否被初始化?
停机问题
问题可以这样改写
bool inited = false;
MyStruct? s;
while (true)
{
foreach( Something foo in ACollection )
foo.Test();
if( inited && false == s.HasValue )
return;
}
这只是稍作改动的代码。但是你可以看到,我已经将你的问题转换为 Halting Problem,其中输入是每个 Something
的状态和 foo.Test()
.
这被证明在图灵机上是不可判定的,您的 CLR VM 肯定是。
简而言之,你问的是,为什么微软在编写 C# 编译器时没有违反数学和计算机科学的规律。
或者您在问,Microsoft C# 编译器不应该在放弃停止问题之前努力尝试。我的回答是,他们应该多努力?