为什么以下语句会因 NullReferenceException
而崩溃 a.b.c = LazyInitBAndReturnValue(a);
?
class A {
public B b;
}
class B {
public int c;
public int other, various, fields;
}
class Program {
private static int LazyInitBAndReturnValue(A a)
{
if (a.b == null)
a.b = new B();
return 42;
}
static void Main(string[] args)
{
A a = new A();
a.b.c = LazyInitBAndReturnValue(a);
a.b.other = LazyInitBAndReturnValue(a);
a.b.various = LazyInitBAndReturnValue(a);
a.b.fields = LazyInitBAndReturnValue(a);
}
}
赋值表达式被求值from right to left ,因此当我们分配给 a.b.c
时,a.b
不应为空。奇怪的是,当调试器因异常而中断时,它也将 a.b
显示为已初始化为非空值。
最佳答案
这在 Section 7.13.1 中有详细说明C# 规范。
The run-time processing of a simple assignment of the form x = y consists of the following steps:
- If x is classified as a variable:
- x is evaluated to produce the variable.
- y is evaluated and, if required, converted to the type of x through an implicit conversion (Section 6.1).
- If the variable given by x is an array element of a reference-type, a run-time check is performed to ensure that the value computed for y is compatible with the array instance of which x is an element. The check succeeds if y is null, or if an implicit reference conversion (Section 6.1.4) exists from the actual type of the instance referenced by y to the actual element type of the array instance containing x. Otherwise, a System.ArrayTypeMismatchException is thrown.
- The value resulting from the evaluation and conversion of y is stored into the location given by the evaluation of x.
- If x is classified as a property or indexer access:
- The instance expression (if x is not static) and the argument list (if x is an indexer access) associated with x are evaluated, and the results are used in the subsequent set accessor invocation.
- y is evaluated and, if required, converted to the type of x through an implicit conversion (Section 6.1).
- The set accessor of x is invoked with the value computed for y as its value argument.
我认为底部部分(如果 x 被分类为属性或索引器访问)提供了一个提示,但也许 C# 专家可以澄清。
首先生成一个set accessor,然后评估y
(触发你的断点),然后调用set accessor,这导致空引用异常。如果非要我猜的话,我会说 accessor 指向 b
的旧值,它是 null。当您更新 b
时,它不会更新它已经创建的访问器。
关于c# - 成员引用和评估的分配顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24048389/