Плохо использовать блок инициализатора

Привет, я использую блок инициализатора в С#

new Something { foo = 1, bar = 2 };

но люди говорят, что это плохая практика.

Я не думаю, что это неправильно, не так ли?

Ответ 1

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

С другой стороны, инициализаторы объектов, безусловно, полезны в тех случаях, когда разумно иметь изменяемые типы. В качестве примера ProcessStartInfo эффективно используется как тип строителя для Process. Полезно иметь возможность писать:

var info = new ProcessStartInfo { 
    FileName = "notepad.exe",
    Arguments = "foo.txt",
    ErrorDialog = true
};
Process process = Process.Start(info);

В самом деле, вы даже можете сделать все это inline вместо дополнительной переменной. Порт протокола протоколов использует тот же шаблон:

Foo foo = new Foo.Builder { 
    FirstProperty = "first",
    SecondProperty = "second"
}.Build();

Теперь одной альтернативой шаблону-строителю являются параметры конструктора (возможно, с помощью методов factory). Исторический недостаток этого заключается в том, что вам нужны разные перегрузки в зависимости от того, какие свойства были установлены, и если несколько параметров имеют один и тот же тип, было бы трудно определить, что именно. С# 4 значительно упрощает использование дополнительных параметров и названных аргументов. Например, если вы создаете класс электронной почты, который может иметь:

Email email = new Email(
    from: "[email protected]",
    to: "[email protected]",
    subject: "Test email",
    body: textVariable
);

У этого есть много одинаковых преимуществ инициализаторов объектов с точки зрения ясности, но без штрафа за изменчивость. Вышеупомянутый вызов конструктора, возможно, пропустил некоторые необязательные параметры, такие как вложения и список BCC. Я думаю, что это окажется одним из самых больших преимуществ С# 4 для тех из нас, кто любит неизменность, но также и как ясность инициализаторов объектов.

Ответ 2

Это сомнительная (я не буду говорить "плохой" ) практикой использовать блоки инициализации в качестве замены соответствующей перегрузки конструктора, если таковая существует.

public class Entity
{
    public Entity()
    {
    }

    public Entity(int id, string name)
    {
        this.ID = id;
        this.Name = name;
    }

    public int ID { get; set; }
    public string Name { get; set; }
}

Если у вас есть этот очень простой класс, тогда обычно предпочтительнее писать:

var entity = new Entity(1, "Fred");

... чем писать:

var entity = new Entity { ID = 1, Name = "Fred" };

Есть по крайней мере две веские причины для этого:

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

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

Однако есть один случай, когда вам нужно использовать инициализаторы вместо перегруженных конструкторов, и именно тогда цепочка выбирает в запросе Linq to SQL/EF:

var bars =
    from f in ctx.Foo
    select new Bar { X = f.X, Y = f.Y };
var bazzes =
    from b in bars
    select new Baz { ... };

Это может привести к сбою с "нет поддерживаемого сопоставления", если вы используете перегрузки конструктора вместо стандартных конструкторов + инициализаторы. Это, однако, ограничение используемой технологии (и нежелательной), а не проблема стиля кодирования.

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

Если нет полезной/релевантной перегрузки конструктора, которая может делать то же самое, что и ваш инициализатор, тогда идите вперед и напишите инициализатор, там нет ничего плохого. Функция существует по уважительной причине - она ​​упрощает запись и чтение кода.

Ответ 3

но люди говорят, что это плохая практика.

Кто это говорит? По крайней мере, тот спорное утверждение.

Кажется, сейчас все в ярости, и многие известные блоги С# используют его широко.

Преимущество использования конструктора заключается в том, что его читаемость более понятна, так как код четко показывает, какие свойства получают какие-то значения. Для сравнения:

var x = new Something { Foo = 1, Bar = 2 };

с

var x = new Something(1, 2);

Кроме того, если не существует соответствующего конструктора, код более краток, чем присвоение свойств вручную:

var x = new Something();
x.Foo = 1;
x.Bar = 2;

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

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

Ответ 4

Блоки инициализатора являются БОЛЬШОЙ практикой по следующим причинам:

  • Вы можете создать объект и переопределить его свойства, прежде чем получить его ссылку

    this.ObjA = new ObjA
    {
        Age = 20,
        Name = "123",
    };
    
    // this.ObjA will not be set until properties have all been defined
    // - safer when multithreading
    
  • Конструктор без параметров может по-прежнему делать что-то за сценой (например, инициализировать элементы состояния).

  • Вы можете использовать в сочетании с конструкторами с параметрами

    this.ObjA = new ObjA(20)
    {
        Name = "123",
    };
    
  • Использование конструкторов без параметров лучше для (де) сценариев сериализации

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

  • Эта практика заставляет авторов писать более надежный код, где порядок, в котором делаются, менее легк, чтобы приложение сбой при каждом изменении метаданных класса.

Ответ 5

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

Пользователь вашего класса будет знать, что он не может создать объект без указания этих значений.

Ответ 6

Я думаю, это хорошо.

Потому что он значительно сокращает количество ввода

Ответ 7

Свойства, необходимые для работы объекта, должны быть инициализированы в конструкторе, поэтому вы должны предоставить соответствующие параметры в контрструкторе.

Блоки инициализатора очень удобны для нескольких новых функций С# 3.0, но вы должны иметь в виду, что они здесь не для замены параметров в конструкторе.