引用类型的非初始化vs空值
引用类型变量之间是否存在未初始化或具有空值的区别? 我读到某处非初始化意味着null,但在其他地方我读了别的东西。 谢谢!
请注意, 字段被隐式初始化为null
,因此这仅影响变量。 在纯c#中你不能查询未初始化字段的值(你需要“明确赋值”),所以这是一个非问题。
您可以通过滥用IL 来执行此操作 – 通过声明out
参数,并使用DynamicMethod
编写不分配它的方法(在IL中有效,但在C#中无效)。 然后你会发现你会看到null
。
这反过来是由于IL标志( .locals init
)在调用 (C#)代码上说“在输入此方法之前为我清除堆栈”。 C#编译器始终设置此标志。 如果你再次滥用IL来编写一个没有设置此标志的方法,你可能会看到垃圾。 它可能是任何东西 。 但是到目前为止,你应该得到你得到的例外:)
这是第一个例子(不是第二个,更复杂):
delegate void AbuseMe(out object foo); static void Main() { DynamicMethod dyn = new DynamicMethod("Foo", typeof(void), new[] { typeof(object).MakeByRefType() }); dyn.GetILGenerator().Emit(OpCodes.Ret); AbuseMe method = (AbuseMe) dyn.CreateDelegate(typeof(AbuseMe)); object obj; // this **never** gets assigned, by **any** code method(out obj); Console.WriteLine(obj == null); }
为了澄清, DynamicMethod
代码只是编写相当于此代码的代码,在C#中不合法:
static void Foo(out object whatever) { } // note, whatever is not assigned
这是有效的,因为就CLR而言 out
不存在 – 只有ref
。 所以这不是无效的IL – 只有语言(C#)才能赋予意义并要求为其赋值。
问题是Main()
仍然有.locals init
标志; 所以在幕后obj
被清除为null
(好吧,整个堆栈空间被简单地擦除)。 如果我从没有那个标志的IL编译(并且有一些其他代码来使堆栈空间变脏)我可以看到垃圾。 你可以在Liran Chen的博客上看到更多关于.locals init
的信息 。
但要回答这个问题:
- 对于字段:未初始化的引用类型字段为
null
– 由规范保证 - 对于变量: 你不能问 ,但作为一个实现细节 (不应该依赖):是的, 即使你不能问,它也会为
null
; p
“这取决于”
对于普通成员变量,如果未在声明中指定值,则变量将采用相应的默认值(引用类型为null
)。 也就是说, class A { string X; }
class A { string X; }
与class A { string X = null; }
相同class A { string X = null; }
class A { string X = null; }
。
对于局部变量 ,在certificate已分配值之前访问它们是错误的。 即使它们的类型“默认”为null(对于引用类型),它们也不是默认隐式赋值的! 即, string F () { string x; return x; }
string F () { string x; return x; }
是一个编译时错误。
记住: null
为null
🙂