为什么局部变量初始化的需要,但场不? [英] Why do local variables require initialization, but fields do not?

查看:268
本文介绍了为什么局部变量初始化的需要,但场不?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我创造我的类中的一个布尔值,只是像布尔检查,则默认为false。

If I create a bool within my class, just something like bool check, it defaults to false.

当我我的方法,布尔检查(而不是内部类)中创建相同的布尔,我得到一个错误使用未分配的局部变量的检查。为什么呢?

When I create the same bool within my method, bool check(instead of within the class), i get an error "use of unassigned local variable check". Why?

推荐答案

尤瓦大卫的回答是基本正确;总结:

Yuval and David's answers are basically correct; summing up:


  • 未分配的局部变量的使用是一个可能的错误,这可以通过低成本的编译器进行检测。

  • 一个未分配的字段或数组元素的使用是不太可能的错误,它是很难检测到在编译器的条件。因此,编译器不会尝试以检测领域使用未初始化变量的,而是为了使程序行为的确定性时初始化为默认值依赖。

一个评论者大卫的回答问为什么它是不可能检测使用通过静态分析未分配的领域;这就是我想在这个答案,扩大在该点。

A commenter to David's answer asks why it is impossible to detect the use of an unassigned field via static analysis; this is the point I want to expand upon in this answer.

首先,对于任何变量,本地或以其它方式,这是在实践中不可能确定的准确的变量是否被分配或未分配的。试想一下:

First off, for any variable, local or otherwise, it is in practice impossible to determine exactly whether a variable is assigned or unassigned. Consider:

bool x;
if (M()) x = true;
Console.WriteLine(x);

问题为x分配?相当于不M()返回true?现在,假设如果费尔马大定理是所有整数小于eleventy gajillion真,否则为假M()返回true。为了确定x是否明确赋值,编译器必须产生实质上费尔马大定理的证明。编译器是不是聪明的。

The question "is x assigned?" is equivalent to "does M() return true?" Now, suppose M() returns true if Fermat's Last Theorem is true for all integers less than eleventy gajillion, and false otherwise. In order to determine whether x is definitely assigned, the compiler must essentially produce a proof of Fermat's Last Theorem. The compiler is not that smart.

那么编译器,而不是当地人是实现一种算法是的快速高估的当一个地方没有明确赋值。也就是说,它有一定的假阳性,它说:我不能证明这个地方分配,即使你和我知道它是。例如:

So what the compiler does instead for locals is implements an algorithm which is fast, and overestimates when a local is not definitely assigned. That is, it has some false positives, where it says "I can't prove that this local is assigned" even though you and I know it is. For example:

bool x;
if (N() * 0 == 0) x = true;
Console.WriteLine(x);

假设N()返回一个整数。你和我知道,N()* 0为0,但是编译器不知道。 (注:C#编译器2.0的没有的知道这一点,但我删除优化,规范没有的的编译器知道。)

Suppose N() returns an integer. You and I know that N() * 0 will be 0, but the compiler does not know that. (Note: the C# 2.0 compiler did know that, but I removed that optimization, as the specification does not say that the compiler knows that.)

好吧,让我们怎么知道这么远吗?这是不切实际的当地人得到一个确切的答案,但我们可以便宜地高估不分配岬并获得pretty好结果的一边犯错让你解决您的不明程序。那很好。为什么不能做场同样的事情?也就是说,做一个明确赋值检查器,它高估便宜?

All right, so what do we know so far? It is impractical for locals to get an exact answer, but we can overestimate not-assigned-ness cheaply and get a pretty good result that errs on the side of "make you fix your unclear program". That's good. Why not do the same thing for fields? That is, make a definite assignment checker that overestimates cheaply?

那么,有多少种方法是有一个地方被初始化?它可以在方法的文本内进行分配。它可以在该方法的文本一个lambda内进行分配;这可能拉姆达永远不会被调用,所以这些分配是不相关的。或者它可以是去anothe方法,在这一点上,我们可以假设,当该方法通常返回它分配传递。这些都是非常明确的点,其中地方分配,而且他们的正确的,在本地声明同样的方法的。确定当地人明确赋值只需要的局部分析的。方法往往是短期 - 远远低于在方法上百万行code的 - 所以分析整个方法是相当快的。

Well, how many ways are there for a local to be initialized? It can be assigned within the text of the method. It can be assigned within a lambda in the text of the method; that lambda might never be invoked, so those assignments are not relevant. Or it can be passed as "out" to anothe method, at which point we can assume it is assigned when the method returns normally. Those are very clear points at which the local is assigned, and they are right there in the same method that the local is declared. Determining definite assignment for locals requires only local analysis. Methods tend to be short -- far less than a million lines of code in a method -- and so analyzing the entire method is quite quick.

现在什么领域?字段可以顺理成章的构造函数初始化。或现场初始化。或者构造函数可以调用初始化领域的实例方法。或者构造函数可以调用的虚拟的方法initailizes领域。或者构造函数可以在其它类的调用方法的,这可能是的在库的,它初始化的字段。静态字段可以在静态构造进行初始化。静态字段可以通过的其他的静态构造函数初始化。

Now what about fields? Fields can be initialized in a constructor of course. Or a field initializer. Or the constructor can call an instance method that initializes the fields. Or the constructor can call a virtual method that initailizes the fields. Or the constructor can call a method in another class, which might be in a library, that initializes the fields. Static fields can be initialized in static constructors. Static fields can be initialized by other static constructors.

从本质上进行了实地初始化可能的在整个程序中的任何位置的,包括里面的还没有被写入尚未将在库被宣布虚拟方法

Essentially the initializer for a field could be anywhere in the entire program, including inside virtual methods that will be declared in libraries that haven't been written yet:

// Library written by BarCorp
public abstract class Bar
{
    // Derived class is responsible for initializing x.
    protected int x;
    protected abstract void InitializeX(); 
    public void M() 
    { 
       InitializeX();
       Console.WriteLine(x); 
    }
}

难道编译这一程序错误?如果是,如何BarCorp应该修复bug?通过分配一个默认值对x?但是,这是编译器做什么了。

Is it an error to compile this library? If yes, how is BarCorp supposed to fix the bug? By assigning a default value to x? But that's what the compiler does already.

假设这个库是合法的。如果FooCorp写

Suppose this library is legal. If FooCorp writes

public class Foo : Bar
{
    protected override void InitializeX() { } 
}

是的的错误? 编译器应该如何找出答案?的唯一方法是做一个的整个程序分析的,跟​​踪初始化静态的各个领域的上的通过该计划所有可能的路径的,包括涉及的路径在运行时的虚方法的选择的。这个问题是可以的任意硬的;它可能涉及数以百万计的控制路径模拟执行。判断本地控制流需要微秒并且取决于该方法的规模。因为它依赖于复杂的在节目中的每一个方法和所有的库

is that an error? How is the compiler supposed to figure that out? The only way is to do a whole program analysis that tracks the initialization static of every field on every possible path through the program, including paths that involve choice of virtual methods at runtime. This problem can be arbitrarily hard; it can involve simulated execution of millions of control paths. Analyzing local control flows takes microseconds and depends on the size of the method. Analyzing global control flows can take hours because it depends on the complexity of every method in the program and all the libraries.

那么,为什么这样做不必分析整个程序更便宜的分析,只是高估更加严重?那么,建议工作,不让它太难写一个正确的程序,实际上编译,设计团队可以认为这是一种算法。我不知道任何这样的算法。

So why not do a cheaper analysis that doesn't have to analyze the whole program, and just overestimates even more severely? Well, propose an algorithm that works that doesn't make it too hard to write a correct program that actually compiles, and the design team can consider it. I don't know of any such algorithm.

现在,该评论者建议要求,构造函数初始化所有领域。这不是一个坏主意。事实上,正是这样一个不坏的想法, C#已经拥有该功能的结构。结构构造函数必须明确指派的构造函数正常返回时的所有领域;默认的构造函数的所有字段初始化为它们的默认值。

Now, the commenter suggests "require that a constructor initialize all fields". That's not a bad idea. In fact, it is such a not-bad idea that C# already has that feature for structs. A struct constructor is required to definitely-assign all fields by the time the ctor returns normally; the default constructor initializes all the fields to their default values.

有关类是什么?好吧,你怎么知道一个构造函数初始化了一个字段?该构造函数可以调用一个的的虚拟方法的初始化领域,现在我们又回到了我们在之前相同的位置。结构没有派生类;类可能。是包含以包含初始化所有的字段构造函数需要一个抽象类的库?抽象类是如何知道什么值应被初始化到田间地头?

What about classes? Well, how do you know that a constructor has initialized a field? The ctor could call a virtual method to initialize the fields, and now we are back in the same position we were in before. Structs don't have derived classes; classes might. Is a library containing an abstract class required to contain a constructor that initializes all its fields? How does the abstract class know what values the fields should be initialized to?

约翰建议干脆禁止在构造函数调用方法字段被初始化之前。所以,总结起来,我们的选择是:

John suggests simply prohibiting calling methods in a ctor before the fields are initialized. So, summing up, our options are:


  • 请常见的,安全的,经常使用的编程风格是非法的。

  • 请,使得编译花费数小时,以寻找臭虫可能不存在一个昂贵的整个程序分析。

  • 在自动初始化依赖于默认值。

设计团队选择了第三个选项。

The design team chose the third option.

这篇关于为什么局部变量初始化的需要,但场不?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆