如何使用结构删除编译器错误:"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# 编译器不应该在放弃停止问题之前努力尝试。我的回答是,他们应该多努力?