Инициализация переменной члена C #; лучшая практика? - PullRequest
90 голосов
/ 18 ноября 2008

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

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

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

Ответы [ 7 ]

75 голосов
/ 18 ноября 2008

С точки зрения производительности реальной разницы нет; инициализаторы полей реализованы как логика конструктора. Единственное отличие состоит в том, что инициализаторы полей происходят перед любым конструктором "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;}}

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

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

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

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

9 голосов
/ 18 ноября 2008

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

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

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

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

4 голосов
/ 18 марта 2011

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

2 голосов
/ 18 ноября 2008

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

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

1 голос
/ 18 ноября 2008

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

0 голосов
/ 06 февраля 2012

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

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

0 голосов
/ 18 ноября 2008

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

...