好奇,我不是想解决任何问题。
为什么只应该分配局部变量?
在以下示例中:
class Program
{
static int a;
static int b { get; set; }
static void Main(string[] args)
{
int c;
System.Console.WriteLine(a);
System.Console.WriteLine(b);
System.Console.WriteLine(c);
}
}
为什么a
和b
只给我一个警告,c
给了我一个错误?
另外,为什么我不能只使用默认值值类型并编写以下代码?
bool MyCondition = true;
int c;
if (MyCondition)
c = 10;
它与内存管理有什么关系吗?
答案 0 :(得分:25)
你的第一个问题基本上是“本地人必须明确分配,所以为什么不对字段做出同样的限制?”蒂姆指出,乔恩指出实际上很难这样做。对于本地人来说,当首次阅读本地时以及首次写入本地时,它几乎总是清晰可见。在不清楚的情况下,编译器可以进行合理的保守猜测。但是对于字段,要知道第一次读取和第一次写入何时发生,您必须知道以何种顺序调用哪些方法的各种事项。
简而言之,分析本地人需要本地分析;分析不必经过包含声明的块。现场分析需要全局分析。这是很多工作;只是说字段被初始化为默认值更容易。
(现在,这就是说,当然可以进行全球分析;我的新工作可能涉及正是进行这种分析。)
你的第二个问题基本上是“那么,如果默认值的自动分配对于字段来说足够好那么为什么它对当地人来说不够好呢?”并且答案是“因为未能分配局部变量并且意外地获取默认值是错误的常见来源。” C#经过精心设计,不鼓励编程实践导致烦人的漏洞,这就是其中之一。
答案 1 :(得分:20)
因为其他变量的初始值为default。
Jon Skeet已就此问题找到了some interesting words:
对于局部变量,编译器对流程有很好的了解 - 它可以 看到变量的“读”和变量的“写”,并证明 (在大多数情况下)第一次写入将在第一次写入之前发生 读取。
实例变量不是这种情况。考虑一个简单的 财产 - 你怎么知道有人会在他们得到它之前设置它? 这使得执行明智的规则基本上是不可行的 - 所以 要么你必须确保所有字段都设置在 构造函数,或允许它们具有默认值。 C#团队选择了 后一种策略。
和here's相关的C#语言规范:
5.3明确的作业
在函数成员的可执行代码中的给定位置,a 如果编译器可以证明,变量被认为是明确分配的, 通过特定的静态流分析(第5.3.3节),该变量具有 已自动初始化或已成为至少一个的目标 分配
5.3.1最初分配的变量
以下类别的变量最初归类为 分配:
静态变量。
类实例的实例变量。
最初分配的结构变量的实例变量。
数组元素。
值参数。
参考参数。
在catch子句或foreach语句中声明的变量。
5.3.2最初未分配的变量
以下类别的变量最初归类为 未分配的:
最初未分配的结构变量的实例变量。
输出参数,包括struct实例的this变量 构造
局部变量,但在catch子句或a中声明的变量除外 foreach声明。
答案 2 :(得分:9)
CLR提供了一种硬保证,即将局部变量初始化为其默认值。但这种保证确实有局限性。缺少的是它能够识别方法体内的范围块。一旦编译器将代码转换为IL,它们就会消失。 Scope是一种语言结构,在CLI中没有并行,不能用IL表示。
例如,你可以在像VB.NET这样的语言中看到这个问题。这个人为的例子显示了这种行为:
Module Module1
Sub Main()
For ix = 1 To 3
Dim s As String
If ix = 2 Then s = "foo"
If s Is Nothing Then Console.WriteLine("null") Else Console.WriteLine(s)
Next
Console.ReadLine()
End Sub
End Module
输出:
null
foo
foo
或者换句话说,局部变量s
只初始化一次并在之后保留其值。这当然有创造错误的诀窍。 VB.NET编译器 为它生成一个警告,并且具有避免它的简单语法(As New)。像C ++ / CLI这样的托管语言具有相同的行为,但根本不会发出诊断信息。但是C#语言提供了更强的保证,它会产生错误。
此规则称为“明确分配”。 C#语言规范第5.3.3章
详细解释了确切的规则明确的赋值检查有其局限性。它适用于局部变量,因为它们的范围非常有限(方法体专用)并且您无法通过Reflection获取它们。更难处理类的字段,它需要整个程序分析,可能需要超出编译器可以看到的范围。像另一个程序集中的代码。这就是为什么C#编译器只能对它发出警告但不能拒绝正确的代码。