Инициализировать поля класса в конструкторе или при объявлении? - PullRequest
377 голосов
/ 23 августа 2008

Я недавно программировал на 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()
    {
        // .....
    }
}

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

Ответы [ 14 ]

278 голосов
/ 24 августа 2008

Мои правила:

  1. Не инициализировать значениями по умолчанию в объявлении (null, false, 0, 0.0…).
  2. Предпочитайте инициализацию в объявлении, если у вас нет параметра конструктора, который изменяет значение поля.
  3. Если значение поля изменяется из-за параметра конструктора, поместите инициализацию в конструкторы.
  4. Будьте последовательны в своей практике (самое важное правило).
145 голосов
/ 24 августа 2008

В C # это не имеет значения. Два примера кода, которые вы даете, абсолютно эквивалентны. В первом примере компилятор C # (или это CLR?) Создаст пустой конструктор и инициализирует переменные, как если бы они были в конструкторе (в этом есть небольшой нюанс, который объясняет Джон Скит в комментариях ниже). Если конструктор уже существует, любая инициализация «выше» будет перемещена в его верхнюю часть.

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

15 голосов
/ 06 сентября 2008

Семантика C # здесь немного отличается от Java. В C # присваивание в объявлении выполняется перед вызовом конструктора суперкласса. В Java это делается сразу после того, как разрешается использовать this, что особенно полезно для анонимных внутренних классов, и означает, что семантика двух форм действительно совпадает.

Если можете, заполните поля окончательно.

14 голосов
/ 11 ноября 2013

Я думаю, что есть одна оговорка. Однажды я допустил такую ​​ошибку: внутри производного класса я пытался «инициализировать при объявлении» поля, унаследованные от абстрактного базового класса. В результате было создано два набора полей, одно из которых является «базовым», а другое - только что объявленными, а отладка заняла у меня довольно много времени.

Урок: чтобы инициализировать унаследованные поля, вы должны сделать это внутри конструктора.

6 голосов
/ 24 августа 2008

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

  • Поля в статических классах / методах
  • Поля, типизированные как static / final / et al

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

4 голосов
/ 24 августа 2008

Что если я скажу вам, это зависит?

Я вообще все инициализирую и делаю это согласованно. Да, это слишком явно, но также немного проще в обслуживании.

Если мы беспокоимся о производительности, тогда я инициализирую только то, что должно быть сделано, и помещаю его в области, которые дают максимальную отдачу.

В системе реального времени я спрашиваю, нужна ли мне вообще переменная или константа.

А в C ++ я часто не выполняю инициализацию в любом месте и перемещаю ее в функцию Init (). Зачем? Что ж, в C ++, если вы инициализируете что-то, что может вызвать исключение во время создания объекта, вы открываете себя для утечек памяти.

3 голосов
/ 17 января 2016

В Java инициализатор с объявлением означает, что поле всегда инициализируется одинаково, независимо от того, какой конструктор используется (если у вас их больше одного) или параметров ваших конструкторов (если у них есть аргументы), хотя конструктор может впоследствии изменить значение (если оно не является окончательным). Таким образом, использование инициализатора с объявлением предлагает читателю, что инициализированное значение - это значение, которое поле имеет во всех случаях , независимо от того, какой конструктор используется, и независимо от параметров, передаваемых любому конструктору. Поэтому используйте инициализатор с объявлением, только если и всегда если значение для всех построенных объектов одинаково.

3 голосов
/ 14 августа 2015

Существует множество различных ситуаций.

Мне просто нужен пустой список

Ситуация понятна. Мне просто нужно подготовить свой список и предотвратить появление исключения, когда кто-то добавляет элемент в список.

public class CsvFile
{
    private List<CsvRow> lines = new List<CsvRow>();

    public CsvFile()
    {
    }
}

Я знаю значения

Я точно знаю, какие значения я хочу иметь по умолчанию, или мне нужно использовать какую-то другую логику.

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = new List<string>() {"usernameA", "usernameB"};
    }
}

или

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = GetDefaultUsers(2);
    }
}

Пустой список с возможными значениями

Иногда я ожидаю пустой список по умолчанию с возможностью добавления значений через другой конструктор.

public class AdminTeam
{
    private List<string> usernames = new List<string>();

    public AdminTeam()
    {
    }

    public AdminTeam(List<string> admins)
    {
         admins.ForEach(x => usernames.Add(x));
    }
}
2 голосов
/ 21 октября 2017

Рассмотрим ситуацию, когда у вас есть более одного конструктора. Будет ли инициализация отличаться для разных конструкторов? Если они будут одинаковыми, то зачем повторять для каждого конструктора? Это соответствует заявлению kokos, но может не относиться к параметрам. Допустим, например, что вы хотите сохранить флаг, который показывает, как был создан объект. Тогда этот флаг будет инициализирован по-разному для разных конструкторов, независимо от параметров конструктора. С другой стороны, если вы повторяете одну и ту же инициализацию для каждого конструктора, вы оставляете возможность, что вы (непреднамеренно) измените параметр инициализации в некоторых конструкторах, но не в других. Итак, основная концепция здесь заключается в том, что общий код должен иметь общее местоположение и не повторяться потенциально в разных местах. Поэтому я бы сказал, всегда помещайте это в декларацию, пока у вас не возникнет конкретная ситуация, когда это больше не работает для вас.

2 голосов
/ 24 мая 2017

Быть последовательным важно, но вот вопрос, который вы должны себе задать: «У меня есть конструктор для чего-то еще?»

Как правило, я создаю модели для передачи данных, в которых сам класс ничего не делает, кроме как работает для размещения переменных.

В этих сценариях у меня обычно нет никаких методов или конструкторов. Мне было бы глупо создавать конструктор исключительно для инициализации моих списков, тем более что я могу инициализировать их в соответствии с объявлением.

Итак, как говорили многие другие, это зависит от вашего использования. Будьте проще, и не делайте ничего лишнего.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...