Инициализация переменных элемента С#; лучшая практика?

Лучше ли инициализировать переменные-члены класса при объявлении

private List<Thing> _things = new List<Thing>();
private int _arb = 99;

или в конструкторе по умолчанию?

private List<Thing> _things;
private int _arb;

public TheClass()
{
  _things = new List<Thing>();
  _arb = 99;
}

Это просто вопрос стиля или есть компромиссы производительности, так или иначе?

Ответ 1

В плане производительности нет никакой реальной разницы; Инициализаторы полей реализуются как логика конструктора. Единственное различие заключается в том, что инициализаторы поля происходят до любого конструктора "base" /​​ "this".

Подход конструктора может использоваться с автоматически реализованными свойствами (инициализаторы полей не могут) - i.e.

[DefaultValue("")]
public string Foo {get;set;}
public Bar() { // ctor
  Foo = "";
}

Кроме этого, я предпочитаю синтаксис инициализатора поля; Я нахожу, что он держит вещи локализованными - т.е.

private readonly List<SomeClass> items = new List<SomeClass>();
public List<SomeClass> Items {get {return items;}}

Мне не нужно искать игру вверх и вниз, чтобы найти, где она назначена...

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

public Bar() : this("") {}
public Bar(string foo) {Foo = foo;}

edit: как побочный комментарий, обратите внимание, что в приведенном выше случае, если есть другие поля (не показаны) с инициализаторами поля, то они только инициализируются только в конструкторах, которые вызывают base(...) - т.е. public Bar(string foo) ctor, Другой конструктор не запускает инициализаторы полей, поскольку он знает, что они выполняются с помощью this(...) ctor.

Ответ 2

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

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

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

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

Ответ 3

Одним из основных ограничений с инициализаторами полей является то, что нет способа их обернуть в блок try-finally. Если в инициализаторе поля выбрано исключение, любые ресурсы, которые были выделены в предыдущих инициализаторах, будут оставлены; нет способа предотвратить это. Другие ошибки в конструкции могут быть рассмотрены, если неловко, путем использования защищенного базового конструктора с IDisposable по ссылке и указания его на себя в качестве самой первой операции. Затем можно избежать вызова конструктора, кроме методов factory, которые в случае исключения вызовут Dispose на частично созданном объекте. Эта защита позволит очистить IDisposables, созданные в инициализаторах производного класса, если конструктор основного класса завершит работу после "контрабанды" ссылки на новый объект. К сожалению, нет возможности обеспечить такую ​​защиту, если инициализатор поля не работает.

Ответ 4

Используйте либо инициализаторы полей, либо создайте функцию Init(). Проблема с помещением этих вещей в ваш конструктор состоит в том, что если вам когда-либо понадобится добавить второй конструктор, вы получите код копирования/вставки (или вы упустите его и получите неинициализированные переменные).

Я бы либо инициализировал, где объявлен. Или конструктор вызывает функцию Init().

Ответ 5

Например, переменные, это в основном вопрос стиля (я предпочитаю использовать конструктор). Для статических переменных существует возможность эффективность использования для инициализации встроенного (не всегда возможно, конечно).

Ответ 6

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

Ответ 7

В добавление к вышесказанному - у вас всегда есть конструктор при реализации классов, которые имеют реализацию. Если вы не объявите один, тогда инструктор по умолчанию будет выведен компилятором [public Foo() {}]; конструктор, который не принимает аргументов.

Часто мне нравится предлагать оба подхода. Разрешить конструкторы для тех, кто хочет их использовать, и разрешить инициализаторы полей для ситуаций, когда вы хотите использовать упрощенную или стандартную реализацию вашего класса/типа. Это добавляет гибкость вашему коду. Имейте в виду, что каждый может использовать инициализатор поля по умолчанию, если они выбирают... обязательно объявите его вручную, если вы предложите несколько конструкторов - public Foo() {}