Инициализация переменной-члена 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;
}


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

554   7  

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Comments

    Ничего не найдено.