Всем известна функция C#, когда нам не нужно инициализировать каждое свойство объекта отдельно, а просто прописать его в конструкторе.
var obj2 = new Class1("a"); obj2.S = string.Empty;
такой же, как
var obj2 = new Class1("a") {S = string.Empty};
Даже ReSharper так считает:
Но это не просто синтаксический сахар, и это не одно и то же.
Давайте представим пример описания Class1 следующим образом:
public class Class1 { public string Prop; public Class1(string s) { Prop = s; } public string S { set => throw new Exception(); } }
Итак, установка свойства S в строку. Пустое значение вызовет исключение. В этом случае мы можем увидеть разницу между встроенной инициализацией и явной инициализацией.
Когда мы вызываем конструктор и только после его инициализации, объект не является нулевым, а в случае встроенной инициализации мы имеем нулевой объект.
Если мы посмотрим на IL-код, то увидим разницу в реализации:
Явная инициализация:
IL_0002: ldstr "a" IL_0007: newobj instance void ConsoleApp2.Class1::.ctor(string) IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: ldsfld string [System.Runtime]System.String::Empty
Встроенная инициализация:
IL_0002: ldstr "a" IL_0007: newobj instance void ConsoleApp2.Class1::.ctor(string) IL_000c: dup IL_000d: ldsfld string [System.Runtime]System.String::Empty IL_0012: callvirt instance void ConsoleApp2.Class1::set_S(string) IL_0017: nop IL_0018: stloc.0
Здесь главное, где находится инструкция stloc.0 (Поместить значение из стека в локальную переменную 0). В случае встроенной инициализации новый объект Class1 создается и помещается в стек, но не перемещается в локальную переменную из-за исключения в инструкции IL_0012.