【问题标题】:Non-initialized vs null values of reference types引用类型的未初始化与空值
【发布时间】:2011-06-13 19:33:00
【问题描述】:

引用类型变量未初始化或具有空值之间有区别吗? 我在某处读到非初始化意味着 null,但在其他地方我读到了别的东西。谢谢!

【问题讨论】:

    标签: c# reference null


    【解决方案1】:

    注意 fields 被隐式初始化为null,所以这只影响变量。在纯 c# 中,您无法查询未初始化字段的值(您需要“明确赋值”),因此这是一个非问题。

    你可以通过滥用 IL 来做到这一点 - 通过声明一个 out 参数,并使用 DynamicMethod 编写一个不分配它的方法(在 IL 中有效,但在C#)。然后你发现你会看到nulls。

    这又是由于 调用 (C#) 代码中的 IL 标志 (.locals init) 表示“在进入此方法之前为我清除堆栈”。 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#) 才赋予 out 意义并要求为其分配一个值。

    问题是Main() 仍然有.locals init 标志;所以在幕后obj 被 清除为null (好吧,整个堆栈空间都被简单地擦除了)。如果我在没有那个标志的情况下从 IL 编译(并且有一些其他代码使堆栈空间变脏),我会看到垃圾。您可以在 Liran Chen's 博客上查看有关 .locals init 的更多信息。

    但是要回答这个问题:

    • 对于字段:未初始化的引用类型字段为 null - 由规范保证
    • 对于变量:你不能问,但作为实现细节(不应该依赖):是的,它将是null 即使你不能问;p

    【讨论】:

    • 好吧,这太可怕了! :)
    • Jeffrey Richter 在他的书“CLR Via C#, 3rd edition”中提到变量总是由 CLR 初始化为null。他还说实际上C#编译器没有必要禁止使用非初始化变量,但是编译器还是有这个限制的。
    • @Dmitry 正是,这就是为什么我强调“不应该依赖”。此行为未由 C# 规范定义(它不需要是:整个场景已被排除),因此任何实现都是有效的。 “将本地设置为单调增加的字节”将是一个完全合法的实现;p
    • @Dmitry 并且除非我弄错了,否则 CLR“规则”是由 .locals init 标志驱动的。 IIRC 你可以省略这个,让混乱开始。
    【解决方案2】:

    “视情况而定”

    对于普通的成员变量,当声明中未指定值时,该变量采用适当的默认值(引用类型为null)。即class A { string X; }与class A { string X = null; }相同。

    对于局部变量,在可以证明已分配值之前访问它们是错误的。即使它们的类型“默认”为 null(对于引用类型),它们也不是默认隐式分配的!也就是说,string F () { string x; return x; } 是编译时错误。

    记住:null 是 null :-)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-03
      • 1970-01-01
      • 2021-11-11
      • 2023-03-06
      • 1970-01-01
      相关资源
      最近更新 更多