0

当我犯了一个愚蠢的错误时,我正在开发我们的一个新应用程序……我忘了在对象初始化程序中写:“new ClassName”。奇怪的是 VS 只是编译了代码……没有任何错误或警告。(我用过 VS 2015,.Net 4.5.2)

我的代码:

var target = new MyClass
{
    MyValues = new List<MyOtherClass>
    {
        new MyOtherClass
        {
            Start = new DateTimeValue
            {
                DateTime = new DateTime(2015, 08, 23)
            },
            End = {
                DateTime = new DateTime(2015, 08, 23)
            }
        }
    }
};

(开始和结束都是 DateTimeValue 类型)

当我启动应用程序时,这段代码抛出了 NullReferenceException。(添加“new DateTimeValue”解决了这个问题)。为什么编译器没有错误?

4

2 回答 2

9

那里没有任何问题。它使用一个对象初始化器,End它使用现有值设置属性,而不是为. 您的代码相当于:End

var tmp0 = new MyClass();
var tmp1 = new List<MyOtherClass>();
var tmp2 = new MyOtherClass();
var tmp3 = new DateTimeValue();
tmp3.DateTime = new DateTime(2015, 8, 23);
// Note the assignment to Start here
tmp2.Start = tmp3;

// No assignment to End -
// it's calling the End "getter" and then the DateTime "setter" on the result
tmp2.End.DateTime = new DateTime(2015, 8, 23);
tmp1.Add(tmp2);
tmp0.MyValues = tmp1;
var target = tmp0;

从 C# 5 规范,第 7.6.10.2 节:

在等号之后指定对象初始化器的成员初始化器是嵌套对象初始化器,即嵌入对象的初始化。嵌套对象初始值设定项中的赋值不是为字段或属性分配新值,而是被视为对字段或属性成员的赋值。

这正是你在这里所做的——这{ DateTime = ... }部分是一个对象初始化器。

于 2015-08-26T14:52:17.830 回答
4

因为这是有效的语法。

大致翻译为以下内容:

var target = new MyClass();
target.MyValues = new List<MyOtherClass>();

var i = new MyOtherClass();

i.Start = new DateTimeValue();
i.Start.DateTime = new DateTime(2015, 08, 23);

i.End.DateTime = new DateTime(2015, 08, 23);

target.MyValues.Add(i);

除了属性只设置一次,编译器永远不会读取。它需要使用额外的临时变量(就像乔恩在他的回答中所做的那样),但为了简单起见,我没有费心在这里写它们。

此语法对于在其构造函数中MyOtherClass实例化属性的情况很有用。End也可以在属性为只读时使用。

于 2015-08-26T14:50:44.657 回答