我一直想知道为什么在下面的例子中可以不初始化实例字段(依赖它将具有其默认值)并访问它,而局部变量显然必须进行初始化,即使我将其初始化为默认值,无论如何......
public class TestClass
{
private bool a;
public void Do()
{
bool b; // That would solve the problem: = false;
Console.WriteLine(a);
Console.WriteLine(b); //Use of unassigned local variable 'b'
}
}
答案 0 :(得分:29)
对于局部变量,编译器对流程有一个很好的了解 - 它可以看到变量的“读取”和变量的“写入”,并证明(在大多数情况下)第一次写入将在之前发生第一次阅读。
实例变量不是这种情况。考虑一个简单的属性 - 你怎么知道有人会在它得到它之前设置它?这使得执行合理规则基本上不可行 - 因此要么必须确保在构造函数中设置所有字段,要么允许它们具有默认值。 C#团队选择了后一种策略。
答案 1 :(得分:6)
它由C#中的Definite Assignment规则控制。必须在访问变量之前明确赋值。
5.3明确的作业
在函数成员的可执行代码中的给定位置,如果编译器可以通过特定的静态流分析(第5.3.3节)证明该变量已被自动初始化,则称该变量是明确赋值的。或者至少是一项任务的目标。
5.3.1最初分配的变量
以下类别的变量被归类为最初分配的:
静态变量。
类实例的实例变量。
最初分配的结构变量的实例变量。
数组元素。
值参数。
参考参数。
在catch子句或foreach语句中声明的变量。
5.3.2最初未分配的变量
以下类别的变量分类为最初未分配:
最初未分配的struct变量的实例变量。
输出参数,包括struct实例构造函数的this变量。
局部变量,但在catch子句或foreach语句中声明的变量除外。
答案 2 :(得分:2)
当为新对象实例分配一块内存时,运行时在整个块上写入零,确保新对象以已知状态启动 - 这就是为什么整数默认为0,默认为双精度为0.0,指针&安培;对象引用null,依此类推。
理论上,有可能对作为方法调用的一部分分配的帧进行堆叠。虽然开销很高,但它会彻底减慢对其他方法的调用,因此不会尝试。
答案 3 :(得分:1)
实例变量具有默认值。来自C#3.0规范:
5.1.2.1类
中的实例变量类的实例变量来了 当一个新的实例出现时 该类已创建,并且已停止 当没有引用时存在 那个实例和实例的 终结者(如果有的话)已经执行。
实例的初始值 类的变量是默认值 变量类型的值(第5.2节)。
为了明确分配 检查,实例变量是 考虑最初被分配。
答案 4 :(得分:1)
这是编译器限制。编译器试图阻止你在任何地方使用未分配的变量,这是一件好事,因为使用未初始化的变量曾经是旧C代码中常见的bug。
但是,在您触发该方法调用之前,编译器无法知道实例变量是否已初始化,因为它可以由任何其他方法设置,可以通过外部代码以任何顺序调用。
答案 5 :(得分:1)
隐式构造函数为您初始化实例变量。即使你指定了一个c'tor但是没有初始化一个字段,它也可以作为在堆上创建对象的一部分来完成。堆栈局部变量不是这样。
答案 6 :(得分:1)
这真的只是警告可以告诉你的问题。警告确实没有办法确保某些其他方法没有初始化类变量,因此它只会警告可以确定未初始化的那个。
此外,这是一个警告,而不是错误,因为使用未分配的变量(它保证为'false')在技术上没有任何错误,但是将它取消分配可能是一个逻辑错误。