引用类型的未初始化与空值

发布于 2024-10-13 02:57:27 字数 76 浏览 2 评论 0原文

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

Is there a difference between reference type variable being non initialized or having null value?
I read somewhere that non-init means null but on other place I read something else. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

桃扇骨 2024-10-20 02:57:27

请注意,字段隐式初始化为null,因此这只影响变量。在纯 C# 中,您无法查询未初始化字段的值(您需要“明确赋值”),因此这是没有问题的。

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

这又是由于 调用 (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 的 上查看有关 .locals init 的更多信息a> 博客。

但要回答这个问题:

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

Note that fields are implicitly initialized to null, so this only affects variables. In pure c# you can't query the value of an uninitialized field (you need "definite assignment"), so it is a non-question.

You can do this by abusing IL though - by declaring an out parameter, and using DynamicMethod to write a method that doesn't assign it (valid in IL, but not in C#). And then you find that you will see nulls.

This in turn is due to an IL flag (.locals init) that says "clear the stack for me before entering this method" on the calling (C#) code. The C# compiler always sets this flag. If you again abuse IL to write a method that doesn't set this flag, you can probably see garbage. It could be anything. But by this point, you deserve the exceptions you get :)

Here's an example of the first (not the second, which is more complex):

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);
}

For clarification, the DynamicMethod code is simply writing the equivalent of this code, not legal in C#:

static void Foo(out object whatever) { } // note, whatever is not assigned

This works because as far as the CLR is concerned out doesn't exist - there is only ref. So this isn't invalid IL - it is only the language (C#) that puts meaning to out and demands that it be assigned a value.

The problem is that Main() still has the .locals init flag; so behind the scenes obj is cleared to null (well, the entire stack space is simply wiped). If I compiled from IL without that flag (and had some other code in place to make the stack space dirty) I could see garbage. You can see more about .locals init on Liran Chen's blog.

But to answer the question:

  • for fields: uninitialized reference-type fields are null - guaranteed by the spec
  • for variables: you can't ask, but as an implementation detail (that should not be depended on): yes, it will be null even though you can't ask ;p
誰ツ都不明白 2024-10-20 02:57:27

“这取决于”

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

对于局部变量,在可以证明值已被分配之前访问它们是错误的。尽管它们的类型“默认”为 null(对于引用类型),但它们并不是默认隐式分配的!即,string F () { string x;返回x; } 是一个编译时错误。

请记住:nullnull :-)

"It depends"

For normal member variables when a value is not specified in the declaration then the variable assume the appropriate default value (null for reference types). That is, class A { string X; } is the same as class A { string X = null; }.

For local variables it is an error to access them before a value can be proven to have been assigned. Even though their type "defaults" to null (for reference types), they are not default implicitly assigned! That is, string F () { string x; return x; } is a compile-time error.

Remember: null is null :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文