Непризнанный синтаксис С#

Скажем, мы имеем:

class Foo
{
    public int IntPropertyInFoo { get; set; }

    public Bar BarPropertyInA { get; set; }
}

class Bar
{
    public string StringPropertyInBar { get; set; }
}

Затем мы хотим создать экземпляр Foo с инициализатором объекта:

public static void Main(string[] args)
{
    var foo = new Foo
    {
        IntPropertyInFoo = 123,
        BarPropertyInA = // Ommiting new and type name - why does it compile?
        {
            StringPropertyInBar = "something"
        }
    };
}

Синтаксис инициализации BarPropertyInA меня отключает, потому что код компилируется без предупреждений и, при запуске, бросает NullReferenceException. Я не понимаю этот синтаксис, но, похоже, он имеет тот же эффект при использовании с полем, а не с его собственностью.

Разборка скомпилированного кода дает следующее:

.method public hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       34 (0x22)
  .maxstack  3
  .locals init ([0] class Test.Foo foo)
  IL_0000:  nop
  IL_0001:  newobj     instance void Test.Foo::.ctor()
  IL_0006:  dup
  IL_0007:  ldc.i4.s   123
  IL_0009:  callvirt   instance void Test.Foo::set_IntPropertyInFoo(int32)
  IL_000e:  nop
  IL_000f:  dup
  IL_0010:  callvirt   instance class Test.Bar Test.Foo::get_BarPropertyInA()
  IL_0015:  ldstr      "something"
  IL_001a:  callvirt   instance void Test.Bar::set_StringPropertyInBar(string)
  IL_001f:  nop
  IL_0020:  stloc.0
  IL_0021:  ret
} // end of method Program::Main

Что выглядит:

public static void Main(string[] args)
{
    var foo = new Foo
    {
        IntPropertyInFoo = 123
    };

    foo.BarPropertyInA.StringPropertyInBar = "something";
}

Итак, это должен быть какой-то синтаксический сахар для инициализации членов свойства/поля при условии, что свойство /field инициализируется в конструкторе?

Ответы

Ответ 1

Да, это своего рода сокращение для инициализации свойств, которые начинаются как пустые, а не нулевые. Хорошим примером этого являются свойства коллекции .net.

var cmd = new System.Data.SqlClient.SqlCommand()
{
    Parameters = 
    {
        new System.Data.SqlClient.SqlParameter() { ParameterName = "@p1", Value = "SomValue"}
    },
    CommandText = "select 1 from Table1 where Value = @p1"
};

Он также позволяет инициализировать значения свойств только для чтения.

//compiles and works
var message = new MailMessage { To = { "[email protected]" } };

message = new MailMessage
{
    // won't compile: To is read-only
    To = new MailAddressCollection { "[email protected]" },
};

Заимствовано довольно много дословно из этой статьи: http://www.codeducky.org/even-concise-c-object-initializers/

Новый синтаксис инициализатора позволяет сделать код более кратким и использовать синтаксис инициализации для настройки свойств только для чтения. Действительно, поскольку большинство базовых классов и популярных классов пакетов .NET следуют за пустым более чем null шаблоном для свойств коллекции, вы почти всегда можете использовать синтаксис без изменений. Наконец, использование инициализации без инициализации также означает, что вам выгодно оставлять на месте любые значения по умолчанию, с которыми был инициализирован объект.