Я недавно программировал на C # и Java, и мне любопытно, где лучше всего инициализировать поля моего класса.
Должен ли я сделать это при объявлении?
public class Dice
{
private int topFace = 1;
private Random myRand = new Random();
public void Roll()
{
// ......
}
}
или в конструкторе?
public class Dice
{
private int topFace;
private Random myRand;
public Dice()
{
topFace = 1;
myRand = new Random();
}
public void Roll()
{
// .....
}
}
Мне действительно любопытно, что некоторые из вас, ветеранов, считают лучшей практикой. Я хочу быть последовательным и придерживаться одного подхода.
Ответы:
Мои правила:
null
,false
,0
,0.0
...).источник
default(T)
всегда является значение, которое имеет внутреннее двоичное представление0
.foreach
цикл словами «это повторяет следующее для всех элементов в списке», нам не нужно постоянно пересчитывать значения по умолчанию в C #. Нам также не нужно делать вид, что C # имеет неинициализированную семантику. Поскольку отсутствие значения имеет четкое значение, его можно не указывать. Если это идеально, чтобы быть явным, вы всегда должны использоватьnew
при создании новых делегатов (как это требовалось в C # 1). Но кто это делает? Язык предназначен для добросовестных программистов.В C # это не имеет значения. Два примера кода, которые вы даете, абсолютно эквивалентны. В первом примере компилятор C # (или это CLR?) Создаст пустой конструктор и инициализирует переменные, как если бы они были в конструкторе (в этом есть небольшой нюанс, который объясняет Джон Скит в комментариях ниже). Если конструктор уже существует, любая инициализация «выше» будет перемещена в его верхнюю часть.
С точки зрения наилучшей практики, первый менее подвержен ошибкам, чем второй, поскольку кто-то может легко добавить другой конструктор и забыть о его цепочке.
источник
Семантика C # здесь немного отличается от Java. В C # присваивание в объявлении выполняется перед вызовом конструктора суперкласса. В Java это делается сразу же после того, как это позволяет использовать «this» (особенно полезно для анонимных внутренних классов), и означает, что семантика двух форм действительно совпадает.
Если можете, сделайте поля окончательными.
источник
Я думаю, что есть одна оговорка. Однажды я допустил такую ошибку: внутри производного класса я пытался «инициализировать при объявлении» поля, унаследованные от абстрактного базового класса. В результате было создано два набора полей, одно из которых является «базовым», а другое - только что объявленными, а отладка заняла у меня довольно много времени.
Урок: чтобы инициализировать унаследованные поля, вы должны сделать это внутри конструктора.
источник
derivedObject.InheritedField
, ссылается ли оно на ваш базовый или производный?Предполагая тип в вашем примере, определенно предпочтите инициализировать поля в конструкторе. Исключительные случаи:
Я всегда думаю о списке полей в верхней части класса как о содержании (о том, что здесь содержится, а не о том, как он используется), а о конструкторе - как о введении. Методы, конечно, главы.
источник
Что если я скажу тебе, это зависит?
Я вообще все инициализирую и делаю это последовательно. Да, это слишком явно, но также немного проще в обслуживании.
Если мы беспокоимся о производительности, тогда я инициализирую только то, что нужно сделать, и помещаю его в те области, которые он дает максимальную отдачу.
В системе реального времени я спрашиваю, нужна ли мне вообще переменная или константа.
А в C ++ я часто не выполняю инициализацию в любом месте и перемещаю ее в функцию Init (). Почему? Ну, в C ++, если вы инициализируете что-то, что может вызвать исключение во время конструирования объекта, вы открываете себя для утечек памяти.
источник
Есть много разных ситуаций.
Мне просто нужен пустой список
Ситуация понятна. Мне просто нужно подготовить свой список и предотвратить появление исключения, когда кто-то добавляет элемент в список.
Я знаю ценности
Я точно знаю, какие значения я хочу иметь по умолчанию, или мне нужно использовать какую-то другую логику.
или
Пустой список с возможными значениями
Иногда я ожидаю пустой список по умолчанию с возможностью добавления значений через другой конструктор.
источник
В Java инициализатор с объявлением означает, что поле всегда инициализируется одинаково, независимо от того, какой конструктор используется (если у вас их больше одного) или параметров ваших конструкторов (если у них есть аргументы), хотя впоследствии конструктор может изменить значение (если оно не является окончательным). Поэтому использование инициализатора с объявлением позволяет читателю предположить, что инициализированное значение - это значение, которое поле имеет во всех случаях , независимо от того, какой конструктор используется, и независимо от параметров, передаваемых любому конструктору. Поэтому используйте инициализатор с объявлением, только если и всегда если значение для всех построенных объектов одинаково.
источник
Конструкция C # предполагает, что встроенная инициализация предпочтительнее, или не будет на языке. Каждый раз, когда вы можете избежать перекрестных ссылок между различными местами в коде, вам, как правило, лучше.
Существует также вопрос согласованности со статической инициализацией поля, которая должна быть встроенной для лучшей производительности. В Руководстве по проектированию фреймворка для конструктора сказано:
«Рассматривать» в этом контексте означает делать это, если нет веской причины не делать этого. В случае статических полей инициализатора, хорошей причиной было бы, если инициализация слишком сложна, чтобы быть закодированной в строке.
источник
Быть последовательным важно, но это вопрос, который нужно задать себе: «У меня есть конструктор для чего-то еще?»
Как правило, я создаю модели для передачи данных, которые сам класс ничего не делает, кроме как работает для размещения переменных.
В этих сценариях у меня обычно нет никаких методов или конструкторов. Мне было бы глупо создавать конструктор исключительно для инициализации моих списков, тем более что я могу инициализировать их в соответствии с объявлением.
Так что, как говорили многие другие, это зависит от вашего использования. Сохраняйте это простым и не делайте ничего лишнего, что вам не нужно.
источник
Рассмотрим ситуацию, когда у вас есть более одного конструктора. Будет ли инициализация отличаться для разных конструкторов? Если они будут одинаковыми, то зачем повторять для каждого конструктора? Это соответствует заявлению kokos, но может не относиться к параметрам. Допустим, например, что вы хотите сохранить флаг, который показывает, как был создан объект. Тогда этот флаг будет инициализирован по-разному для разных конструкторов, независимо от параметров конструктора. С другой стороны, если вы повторяете одну и ту же инициализацию для каждого конструктора, вы оставляете возможность, что вы (непреднамеренно) измените параметр инициализации в некоторых конструкторах, но не в других. Итак, основная концепция здесь заключается в том, что общий код должен иметь общее местоположение и не повторяться потенциально в разных местах.
источник
Есть небольшое преимущество в производительности для установки значения в объявлении. Если вы установите его в конструкторе, он будет установлен дважды (сначала на значение по умолчанию, затем сбрасывается в ctor).
источник
Обычно я пытаюсь конструктор ничего не делать, кроме получения зависимостей и инициализации связанных с ними членов экземпляра. Это облегчит вам жизнь, если вы захотите провести юнит-тестирование своих классов.
Если значение, которое вы собираетесь присвоить переменной экземпляра, не зависит от каких-либо параметров, которые вы собираетесь передать конструктору, тогда назначьте его во время объявления.
источник
Не прямой ответ на ваш вопрос о наилучшей практике, а важный и связанный с этим вопрос о том, что в случае определения общего класса, либо оставьте его на компиляторе для инициализации со значениями по умолчанию, либо мы должны использовать специальный метод для инициализации полей их значения по умолчанию (если это абсолютно необходимо для читабельности кода).
И специальный метод для инициализации универсального поля его значением по умолчанию является следующим:
источник
Когда вам не нужна логика или обработка ошибок:
Когда вам нужна логика или обработка ошибок:
С https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html .
источник