методы в конструкторах, плохо? - PullRequest
20 голосов
/ 05 января 2011

У меня есть форма Windows, и у меня есть класс, который проверяет текстовый файл, чтобы убедиться, что он имеет определенный аспект.Теперь у меня есть методы в конструкторе, и это кажется немного странным.Должен ли я просто оставить конструктор пустым, реализовать метод типа start () и вызвать его?Пока мой код выглядит так

public class Seating
{
    private int segments = 0;
    public Seating()
    {
        checkInvoice();
        getSegmentCount();          
    }
}

Ответы [ 6 ]

26 голосов
/ 05 января 2011

У меня есть методы в конструкторе, и это кажется немного странным

Ну, а почему еще существует конструктор?Вполне допустимо иметь методы в конструкторе, даже методы, которые могут потерпеть неудачу (таким образом прерывая создание объекта).Трудно сказать, уместно ли это в вашем конкретном случае.

В целом все нормально.

8 голосов
/ 05 января 2011

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

7 голосов
/ 05 января 2011

Общее правило: есть методы, которые очень тривиальны и вряд ли выдают исключения в конструкторе.Если методы могут потерпеть неудачу по какой-либо причине (доступ к файлу или отсутствие, проблема с базой данных, нулевая ссылка ...), тогда он не должен быть в конструкторе, поскольку люди должны ожидать, что конструкторы не потерпят неудачу (особенно конструкторы без параметров, хотя общее руководствоне указывает)Люди не должны ожидать, что var seating = new Seating(); будет источником ошибок.

Вы можете добавить Start() / Initialize() методы в качестве методов экземпляра, как вы упомянули.Вы также можете добавить новый статический метод, который возвращает новый экземпляр класса после вызова двух необходимых вам методов, и сделать конструктор закрытым.Вы можете пойти дальше и использовать этот метод в новом классе Factory (делая конструктор внутренним).Вы можете подумать и о других способах сделать это.

Одна вещь: вы можете увидеть любое значение, вычисленное или полученное в этих методах, параметром конструктора (и не имеющим конструктора без параметров), тогда конструктор будет назначать только эти параметры.в соответствующие поля / свойства.Заводской метод или метод службы в той же сборке или другой выделенной сборке «Службы» могут отвечать за вызов методов, получение параметров, передачу их конструктору и возврат нового экземпляра класса.Это мой личный фаворит.

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

Обновление:

Вот рекомендации Microsoft для конструкторов:
Рекомендации по использованию конструкторов

Цитирование со страницы:

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

Обновление 2

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

6 голосов
/ 05 января 2011

Вообще говоря, ваш конструктор должен только устанавливать состояние объекта.Возможно, это может означать вызов методов.Я бы сказал, что запуск очень длинных методов не очень хорошая идея.Пользователь вашего класса не ожидал бы, что конструктор будет дорогим методом.

Я не упомянул виртуальные методы, но вы НИКОГДА не должны вызывать их в конструкторе.

Вот пример того, как он может быстро вернуться:

public class MyBase
{
        protected MyBase()
        {
                this.VirtualMethod();
        }

        protected virtual void VirtualMethod()
        {
                Console.WriteLine("VirtualMethod in MyBase");
        }
}

public class MyDerived : MyBase
{
        private readonly string message = "Set by initializer";

        public MyDerived(string message)
        {
                this.message = message;
        }

        protected override void VirtualMethod()
        {
                Console.WriteLine(this.message);
        }
}

Теперь предположим, что у вас есть этот код в другом месте:

MyDerived d = new MyDerived("Called from constructor");

Как вы думаете, что будет показано наприставка?Если вы сказали «Установить инициализатором», то вы правы.

Вот почему:

  • Все инициализаторы полей выполняются перед кодом в конструкторе.
  • Компилятор C # добавляет вызов базового конструктора перед тем, что определено пользователем.В этом случае он вызывает до
    MyBase, что вызывает VirtualMethod().Поскольку тип времени выполнения d равен MyDerived, переопределение VirtualMethod() в MyDerived выполняется
    .И теперь, поскольку тело конструктора MyDerived еще не выполнено
    , this.message имеет значение, которое было задано в инициализаторе
    .
  • Теперь выполняется тело конструктора MyDerived.
  • Более поздние вызовы VirtualMethod() для этого экземпляра теперь будут выводить
    "Called from constructor".
5 голосов
/ 05 января 2011

Виртуальные вызовы методов в конструкторе не допускаются (за небольшим исключением запечатанных классов, что делает метод фактически не виртуальным).

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

2 голосов
/ 05 января 2011

Здесь приведены некоторые полезные рекомендации по использованию конструктора:

Конструктор конструктора (MSDN)

Текст взят из:

Руководство по разработке структуры: условные обозначения, идиомы и шаблоны для многократно используемых библиотек .NET

Это достойная покупка.

Тот, кого я укусил несколько лет назад, был: «Не вызывать виртуальных членов для объекта внутри его конструкторов».

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