Выполняет {} действие like() при создании нового объекта в С#?

Я только заметил, что использование {} вместо () дает те же результаты при построении объекта.

class Customer
{
    public string name;
    public string ID {get; set;}
}

static void Main()
{  
    Customer c1= new Customer{}; //Is this a constructor? 
    Customer c2= new Customer();

    //what is the concept behind the ability to assign values for properties 
    //and fields inside the {} and is not allowable to do it inside () 
    //without defining a constructor:

    Customer c3= new Customer{name= "John", ID="ABC"};
}

Выполняет ли {} как () при создании нового объекта в С#?

Ответ 1

Существует три способа прямого создания нового объекта в С#:

  • Простой вызов конструктора с списком аргументов:

    new Foo()       // Empty argument list
    new Foo(10, 20) // Passing arguments
    
  • Инициализатор объекта с списком аргументов

    new Foo() { Name = "x" }       // Empty argument list
    new Foo(10, 20) { Name = "x" } // Two arguments
    
  • Инициализатор объекта без списка аргументов

    new Foo { Name = "x" }
    

Последняя форма в точности эквивалентна заданию пустого списка аргументов. Обычно он вызывает конструктор без параметров, но он может вызывать конструктор, где все параметры имеют значения по умолчанию.

Теперь в обоих примерах инициализатора объекта, которые я дал, я установил свойство Name, и вы можете установить другие свойства/поля или даже не задавать никаких свойств и полей. Таким образом, все три из них эквивалентны, эффективно передавая аргументы конструктора и не задавая никаких свойств/полей:

new Foo()
new Foo() {}
new Foo {}

Из них первое является самым обычным.

Ответ 2

() - вызывает конструктор без параметров.

{} - предполагается использовать для назначения свойств.

Использование {} без () является ярлыком и будет работать до тех пор, пока существует конструктор без параметров.

Ответ 3

Вы можете использовать инициализаторы объектов для инициализации объектов типа декларативным способом без явного вызова конструктора для типа.

https://msdn.microsoft.com/en-us/library/bb397680.aspx

Также вы можете опустить вызов конструктора, если тип имеет конструктор по умолчанию. Итак,

Customer c1 = new Customer { };

точно совпадает с

Customer c1 = new Customer() { };

Ответ 4

Customer c1 = new Customer {};

Это пустой инициализатор объекта. В соответствии с спецификация инициализаторы объектов вызовут конструктор по умолчанию, если вы не укажете конструктор для использовать. Поскольку инициализация не выполняется, она будет скомпилирована так же, как с использованием конструктора по умолчанию.

Чтобы ответить на вопрос, если '{} действует [s] как () при создании нового объекта на С#, нам нужно более подробно остановиться на нем. Для всех, о которых вы заботитесь, результирующие объекты будут содержать одни и те же данные, но сгенерированный IL не идентичен.

Следующий пример

namespace SO28254462
{
    class Program
    {
        class Customer
        {
            private readonly Foo foo = new Foo();

            public string name;

            public Customer()
            {
            }

            public Customer(string id)
            {
                this.ID = id;
            }

            public string ID { get; set; }

            public Foo Foo
            {
                get
                {
                    return this.foo;
                }
            }
        }

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

        static void Main(string[] args)
        {
            Customer c1 = new Customer { };

            Customer c2 = new Customer();

            Customer c3 = new Customer { name = "John", ID = "ABC", Foo = { Bar = "whatever" } };

            Customer c4 = new Customer();
            c4.name = "John";
            c4.ID = "ABC";
            c4.Foo.Bar = "whatever";

            Customer c5 = new Customer("ABC") { name = "John", Foo = { Bar = "whatever" } };

            Customer c6 = new Customer("ABC");
            c6.name = "John";
            c6.Foo.Bar = "whatever";
        }
    }
}

будет скомпилирован для этого IL (только основной метод, Debug без оптимизации)

.method private hidebysig static 
    void Main (
        string[] args
    ) cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 201 (0xc9)
    .maxstack 2
    .entrypoint
    .locals init (
        [0] class SO28254462.Program/Customer c1,
        [1] class SO28254462.Program/Customer c2,
        [2] class SO28254462.Program/Customer c3,
        [3] class SO28254462.Program/Customer c4,
        [4] class SO28254462.Program/Customer c5,
        [5] class SO28254462.Program/Customer c6,
        [6] class SO28254462.Program/Customer '<>g__initLocal0',
        [7] class SO28254462.Program/Customer '<>g__initLocal1'
    )

    IL_0000: nop
    IL_0001: newobj instance void SO28254462.Program/Customer::.ctor()
    IL_0006: stloc.0
    IL_0007: newobj instance void SO28254462.Program/Customer::.ctor()
    IL_000c: stloc.1
    IL_000d: newobj instance void SO28254462.Program/Customer::.ctor()
    IL_0012: stloc.s '<>g__initLocal0'
    IL_0014: ldloc.s '<>g__initLocal0'
    IL_0016: ldstr "John"
    IL_001b: stfld string SO28254462.Program/Customer::name
    IL_0020: ldloc.s '<>g__initLocal0'
    IL_0022: ldstr "ABC"
    IL_0027: callvirt instance void SO28254462.Program/Customer::set_ID(string)
    IL_002c: nop
    IL_002d: ldloc.s '<>g__initLocal0'
    IL_002f: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
    IL_0034: ldstr "whatever"
    IL_0039: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
    IL_003e: nop
    IL_003f: ldloc.s '<>g__initLocal0'
    IL_0041: stloc.2
    IL_0042: newobj instance void SO28254462.Program/Customer::.ctor()
    IL_0047: stloc.3
    IL_0048: ldloc.3
    IL_0049: ldstr "John"
    IL_004e: stfld string SO28254462.Program/Customer::name
    IL_0053: ldloc.3
    IL_0054: ldstr "ABC"
    IL_0059: callvirt instance void SO28254462.Program/Customer::set_ID(string)
    IL_005e: nop
    IL_005f: ldloc.3
    IL_0060: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
    IL_0065: ldstr "whatever"
    IL_006a: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
    IL_006f: nop
    IL_0070: ldstr "ABC"
    IL_0075: newobj instance void SO28254462.Program/Customer::.ctor(string)
    IL_007a: stloc.s '<>g__initLocal1'
    IL_007c: ldloc.s '<>g__initLocal1'
    IL_007e: ldstr "John"
    IL_0083: stfld string SO28254462.Program/Customer::name
    IL_0088: ldloc.s '<>g__initLocal1'
    IL_008a: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
    IL_008f: ldstr "whatever"
    IL_0094: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
    IL_0099: nop
    IL_009a: ldloc.s '<>g__initLocal1'
    IL_009c: stloc.s c5
    IL_009e: ldstr "ABC"
    IL_00a3: newobj instance void SO28254462.Program/Customer::.ctor(string)
    IL_00a8: stloc.s c6
    IL_00aa: ldloc.s c6
    IL_00ac: ldstr "John"
    IL_00b1: stfld string SO28254462.Program/Customer::name
    IL_00b6: ldloc.s c6
    IL_00b8: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
    IL_00bd: ldstr "whatever"
    IL_00c2: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
    IL_00c7: nop
    IL_00c8: ret
} // end of method Program::Main

Как мы видим, первые две инициализации Customer были преобразованы в идентичные IL (IL_0001 - IL_000c). После этого становится интереснее: от IL_000d до IL_0041 мы видим, что новый объект создается и назначается невидимой временной переменной <>g__initLocal0, и только после этого результирующее значение присваивается c3. Так инициализатор объекта реализуется компилятором С#. Разница в настройке публичных свойств "вручную" после создания объекта очевидна, когда вы смотрите на то, как c4 инициализируется с IL_0042 на IL_006a - временная переменная отсутствует.

IL_0070 до конца эквивалентны предыдущим примерам, за исключением того, что они используют конструктор не по умолчанию в сочетании с инициализаторами объектов. Как и следовало ожидать, он просто компилируется так же, как и раньше, но с указанным конструктором.

Короче говоря: результат, с точки зрения разработчика С#, в основном тот же. Инициализаторы объектов - это простые синтаксические сахара и трюки компилятора, которые могут помочь упростить чтение кода. Однако FWIW, полученный IL не идентичен ручной инициализации свойств. Тем не менее, стоимость невидимой временной переменной, испускаемой компилятором, должна быть незначительной почти во всех программах на С#.

Ответ 6

Customer c1= new Customer{} - это инициализатор свойств. Вы можете написать это как:

Customer c1 = new Customer{
              name="some text",
              ID="some id"
              };

Ответ 7

В вашем конкретном сценарии, да, это даст тот же результат, но не по какой-то причине, о которой вы, вероятно, думаете.

Чтобы понять результат, скажем, у вас есть такой класс:

class Customer
{
    public string name;
    public string ID {get; set;}

    public Customer() 
    { 
    }

    public Customer(string n, string id)
    {
        name = n;
        ID = id;
    }
}

Когда вы создаете его следующим образом:

Customer c = new Customer("john", "someID");

Вы вызываете второй конструктор и говорите классу, в котором вы передаете эти значения, и ответственность за то, что он считает лучшим, лежит на конструкторе (в этом случае он будет использовать эти значения для передачи их в общедоступные поля).

Когда вы создаете его следующим образом:

Customer c = new Customer { name = "john", ID = "someID" };

Вы настраиваете публичные поля самостоятельно и используете пустой конструктор.

В любом случае, вы должны ИЗБЕЖАТЬ использования общедоступных полей, потому что это небезопасно. Вы не должны позволять никому извне изменять их прямо так. Вместо этого используйте только частные поля и используйте общедоступные свойства для управления доступом извне!