C#复杂类型初始化程序编译时没有new关键字

Sla*_*vic 9 c#

我最近正在研究一些代码,这些代码已经从使用十进制变为使用具有十进制数的复杂类型和表示分数的类型.我不得不更新一些测试,在键入时我忘了添加关键字.代码已编译但测试仍然失败,抛出NullReferenceException.在那里,我意识到缺少新的,并且该属性未初始化.有谁知道为什么会这样?我在C#lang规范中找不到可以解释这一点的任何内容.

这是代码示例:

public class Fraction 
{
    public int Numerator { get; set; }
    public int Denominator { get; set; }
}

public class MyDecimal
{
    public decimal? Decimal { get; set; }     
    public Fraction Fractional { get; set; }
}

public class ClassA 
{
    public MyDecimal Value { get; set; }
}

//...

var instance = new ClassA
{
     Value = // new MyDecimal is missing here
     {
         Decimal = 2.0m,
         Fractional = new Fraction 
         { 
               Numerator = 3,   
               Denominator = 4 
         }
     }
}
Run Code Online (Sandbox Code Playgroud)

请注意我使用的是C#6和VS 2015,但我在LINQPad中也得到了相同的结果.

如果有人可以解释一下(我正朝着你的方向寻找Jon Skeet :))我会很高兴的.

Him*_*ere 6

对象初始值设定项并没有真正实例化您的成员。

请参阅以下代码:

var myInstance = new MyInstance { MyMember = new MyMember { Value = 3 }; }
Run Code Online (Sandbox Code Playgroud)

这编译为:

var myMember= new MyMember();
myMember.Value = 3;
var myInstance = new MyInstance();
myInstance.MyMember = myMember;
Run Code Online (Sandbox Code Playgroud)

在您的情况下,您忘记了实例化 MyMember,因此对象初始化器尝试访问该属性并为其分配更多值。这是因为对象初始值设定项总是适当的构造函数之后运行,而在您的情况下未调用该构造函数。所以在你的情况下,它编译为:

var myInstance = new MyInstance();
myMymber.Value = 3;
Run Code Online (Sandbox Code Playgroud)

导致NullReferenceExceptionasmyMember从未实例化。

为什么这甚至可以编译?好吧,我假设编译器假设您MyMemberMyInstance. 它不知道你是否真的这样做了。

class Instance
{
    MyMember MyMember = new MyMember();
}
Run Code Online (Sandbox Code Playgroud)

离开成员null当然是绝对有效的。

  • @SlavenVukovic - 因为对象在构造之后和成员初始值设定项执行之前具有非空成员是完全有效的。 (2认同)

Sel*_*enç 6

C#Specification 5.0将对象初始化器定义为(7.6.10.2对象初始化器):

对象初始值设定项指定零个或多个字段或对象属性的值.

object-initializer:
{   member-initializer-listopt   }
{   member-initializer-list   ,   }
Run Code Online (Sandbox Code Playgroud)

在详细解释之后,给出了一个与您的代码非常相似的示例:

如果Rectangle的构造函数分配两个嵌入的Point实例

public class Rectangle
{
  Point p1 = new Point();
  Point p2 = new Point();
  public Point P1 { get { return p1; } }
  public Point P2 { get { return p2; } }
}
Run Code Online (Sandbox Code Playgroud)

以下构造可用于初始化嵌入的Point实例,而不是分配新实例:

Rectangle r = new Rectangle {
  P1 = { X = 0, Y = 1 },
  P2 = { X = 2, Y = 3 }
};
Run Code Online (Sandbox Code Playgroud)

效果与...相同

Rectangle __r = new Rectangle();
__r.P1.X = 0;
__r.P1.Y = 1;
__r.P2.X = 2;
__r.P2.Y = 3;
Rectangle r = __r;
Run Code Online (Sandbox Code Playgroud)

但是只有一个区别,Point这里的实例是在Rectangle类的内部初始化的,它发生在构造函数中Rectangle.

因此规范的语法是有效的,但是Value在使用对象初始化程序初始化其属性以避免NRE之前,需要确保初始化.