Почему инициализаторы полей производного класса выполняются перед инициализаторами базовых классов - PullRequest
5 голосов
/ 18 августа 2011

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

Мне интересно, почему инициализаторы полей не следуют этому принципу в C #? Я что-то здесь упускаю?

Я столкнулся с полезностью этого принципа и с полевыми инициализаторами. У меня есть базовый класс со свойством, возвращающим объект Identity. Каждый производный класс имеет свое собственное поле репозитория, которое я инициализировал, используя field-initializer (используя конструктор по умолчанию). Недавно я решил, что класс репозитория также должен быть снабжен объектом Identity, поэтому я ввел дополнительный аргумент в конструктор репозитория. Но я застрял, чтобы выяснить:

public class ForumController : AppControllerBase
{
        ForumRepository repository = new ForumRepository(Identity);
    // Above won't compile since Identity is in the base class.

   // ... Action methods.
}

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

Ответы [ 4 ]

6 голосов
/ 18 августа 2011

Почему инициализаторы полей производного класса выполняются перед инициализаторами полей базового класса?

Хороший вопрос. Я ответил на ваш вопрос в этих постах за 2008 год:

http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one.aspx

http://blogs.msdn.com/b/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite-order-as-constructors-part-two.aspx

2 голосов
/ 18 июля 2012

В C # конструктор запускает последовательность:

  Perform all field initializers
  Chain to base class constructor
  Execute user-supplied code for constructor

В vb.net последовательность:

  Chain to base class constructor
  Perform all field initializers
  Execute user-supplied code for constructor

Нет основанной на Framework причины, почему C # делает вещи в том же порядке, о чем свидетельствует, что vb.net может и делает их в другой последовательности. Обоснование дизайна для подхода C # состоит в том, что не должно быть никакой возможности подвергнуть объект воздействию внешнего мира до того, как будут запущены все инициализаторы полей (для полей производного, а также полей базового класса); поскольку конструктор базового класса может предоставлять объект внешнему миру, выполнение этого требования означает, что инициализаторы полей должны выполняться перед конструктором базового класса.

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

Кстати, одна функция, которую я хотел бы видеть как в vb.net, так и в C #, - это средство объявления полей, инициализированных параметрами, и псевдополей. Если у класса есть поле с инициализацией параметров определенного имени и типа, каждый конструктор для этого класса, который не связан с другим классом того же класса, должен содержать параметры с соответствующими именами и типами. Значения этих полей будут установлены в конструкторе, прежде чем что-либо еще будет сделано, и будут доступны для других инициализаторов полей. Псевдополя будут вести себя синтаксически как поля, за исключением того, что они будут только пригодными для использования в инициализаторах полей и будут реализованы как локальные переменные внутри конструкторов. Такая особенность сделает многие типы структур более удобными. Например, если тип должен содержать один конкретный экземпляр массива в течение своего времени жизни, он может сказать:

  readonly param int Length;
  readonly ThingType[] myArray = new ThingType[Length];

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

0 голосов
/ 18 августа 2011

Полевые инициализаторы не предназначены для замены конструкторов.

Согласно документации MSDN все инициализаторы полей выполняются перед конструкторами.Однако ограничение инициализатора поля заключается в том, что они не могут ссылаться на другие поля экземпляра.(http://msdn.microsoft.com/en-us/library/ms173118(v=vs.80).aspx)

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

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


Изменить для уточнения вопросаспросил в комментарии:

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

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

0 голосов
/ 18 августа 2011

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

Пример:

public class Base {

  // field initialiser:
  private string _firstName = "Arthur";

  public string FirstName { get { return _firstName;}}
  public string LastName { get; private set; }

  // initialiser in base constructor:    
  public Base() {
    LastName = "Dent";
  }

}

public class Derived : Base {

  public string FirstNameCopy { get; private set; }
  public string LastNameCopy { get; private set; }

  public Derived() {
    // get values from base class:
    FirstNameCopy = FirstName;
    LastNameCopy = LastName;
  }

}

Тест:

Derived x = new Derived();
Console.WriteLine(x.FirstNameCopy);
Console.WriteLine(x.LastNameCopy);

Выход:

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