Инициализация переменной-члена C #; лучшая практика?

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

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

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


person Steve Crane    schedule 18.11.2008    source источник
comment
Возможный дубликат stackoverflow.com/questions/24551/   -  person goodeye    schedule 03.03.2013


Ответы (7)


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

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

[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.

person Marc Gravell    schedule 18.11.2008
comment
Я знаю, что это старый пост, но у меня вопрос: что вы имели в виду, говоря о конструкторах, вызывающих base (...)? you public Bar (string foo) {Foo = foo;}, похоже, не вызывает: base (), или это происходит неявно? Спасибо за вашу помощь. - person Bruno Santos; 22.09.2012
comment
@Bruno для class, каждый конструктор имеет неявный : base(), если вы не добавите что-то более конкретное, которое может быть : base(123, "abc") или может быть : this(123, "abc"). - person Marc Gravell; 22.09.2012
comment
@Marc Итак, инициализация в порядке (1) fieldInitializer (2) BaseConstructor (3) LocalConstructor - person prabhakaran; 28.05.2014

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

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

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

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

person Tor Haugen    schedule 18.11.2008
comment
Точка производительности применяется только в том случае, если вы повторно назначаете значения (т. Е. Не применяется в исходном коде). Точно так же проблема раздувания (которая является незначительной) применима только к ctors, которые вызывают base (...), поэтому вы можете обойти это с помощью частного ctor (как опубликовано) - только этот ctor будет инициализировать поля. - person Marc Gravell; 18.11.2008
comment
Знаете ли вы, происходит ли это переназначение на практике? Компилятор должен иметь возможность удалять лишние инициализации, если это не меняет семантику программы. - person Jørgen Fogh; 15.10.2013

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

person supercat    schedule 18.03.2011

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

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

person GeekyMonkey    schedule 18.11.2008
comment
Обратите внимание на использование: this () для цепочки конструкторов в сообщении Марка Гравелла выше. Это намного лучше, чем писать отдельные функции Init (). - person Tor Haugen; 19.11.2008

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

person Kent Boogaart    schedule 18.11.2008

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

person Nico    schedule 18.11.2008

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

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

person Qubits    schedule 06.02.2012