это плохо использовать блок инициализатора - PullRequest
11 голосов
/ 04 апреля 2010

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

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

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

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

Ответы [ 7 ]

9 голосов
/ 04 апреля 2010

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

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

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

Действительно, вы можете даже сделать все это встроенным, вместо того, чтобы иметь дополнительную переменную. Порт My Protocol Buffers использует такой же шаблон:

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

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

Email email = new Email(
    from: "skeet@pobox.com",
    to: "jon@example.com",
    subject: "Test email",
    body: textVariable
);

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

8 голосов
/ 04 апреля 2010

Сомнительно (я не скажу «плохой») практика использовать блоки инициализации вместо соответствующей перегрузки конструктора , если таковой существует.

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" };

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

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

  2. Ваш код не сломается, если одно или несколько из этих свойств изменили свои имена или станут доступными только для чтения (что, вероятно, 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 { ... };

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

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

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

4 голосов
/ 04 апреля 2010

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

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

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

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

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

с

var x = new Something(1, 2);

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

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

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

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

3 голосов
/ 04 апреля 2010

Блоки инициализатора - БОЛЬШАЯ практика по следующим причинам:

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

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

  3. Можно использовать совместно с конструкторами с параметрами

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

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

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

1 голос
/ 04 апреля 2010

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

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

0 голосов
/ 04 апреля 2010

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

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

0 голосов
/ 04 апреля 2010

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

Потому что это сильно сокращает ваш набор текста

...