Инициализация поля
Есть ли различия между следующими двумя способами инициализации поля? Когда использовать какой?
Первый путь
public class Class1
{
private SomeClass someclass;
public Class1()
{
someclass = new SomeClass(some arg);
}
}
Второй способ
public class Class1
{
private SomeClass someclass = new SomeClass(some arg);
}
Поле во втором примере может быть только для чтения.
9 ответов
Вы не можете использовать this
ключевое слово при инициализации полей inline. Причиной этого является порядок, в котором выполняется код: для всех намерений и целей код для инициализации встроенного поля выполняется перед конструктором для класса (т. Е. Компилятор C# будет препятствовать доступу к this
ключевое слово). В основном это означает, что это не скомпилируется:
public class Class1
{
private SomeClass someclass = new SomeClass(this);
public Class1()
{
}
}
но это будет:
public class Class1
{
private SomeClass someclass;
public Class1()
{
someclass = new SomeClass(this);
}
}
Это тонкая разница, но стоит отметить.
Другие различия между двумя версиями действительно заметны только при использовании наследования. Если у вас есть два класса, которые наследуются друг от друга, сначала будут инициализированы поля в производном классе, затем будут инициализированы поля в базовом классе, затем будет вызван конструктор для базового класса и, наконец, конструктор для производный класс будет вызван. В некоторых случаях вы должны быть очень осторожны с этим, так как это может вызвать фруктовый салат осложнений, если вы не понимаете, что происходит (один из которых включает вызов виртуального метода внутри конструктора базового класса, но это почти никогда не мудрый ход). Вот пример:
class BaseClass
{
private readonly object objectA = new object(); // Second
private readonly object objectB;
public BaseClass()
{
this.objectB = new object(); // Third
}
}
class DerivedClass : BaseClass
{
private object objectC = new object(); // First
private object objectD;
public DerivedClass()
{
this.objectD = new object(); // Forth
}
}
Вам нужно будет установить точки останова на всех строках, которые инициализируют поля, чтобы увидеть правильную последовательность.
Существует небольшая разница в том, что поле во втором примере будет инициализировано до инициализации полей в базовом классе, а поле в первом примере будет инициализировано после. Это очень редко оказывает какое-либо влияние, однако.
Во многом это вопрос стиля и предпочтений. Лично я предпочитаю второе, так как оно оставляет конструктор ясным для большей логической инициализации, но есть веские основания для выполнения всей инициализации в конструкторе.
Просто для полноты, порядок инициализации идет:
- Статические поля
- Статический конструктор
- Поля экземпляра
- Базовые статические поля
- Базовый статический конструктор
- Поля базового экземпляра
- Базовый конструктор
- Конструктор
Помимо количества строк кода, есть тонкие различия.
Например, инициализация поля происходит до запуска конструкторов. Не имеет большого значения в вашем примере, но это то, что нужно иметь в виду.
Я бы оставил инициализации полей, как во втором примере, простыми (строки или целые числа), чтобы избежать возможных исключений, возникающих во время инициализации.
И, как уже упоминалось, в обоих случаях поле может быть доступно только для чтения, поскольку поля только для чтения могут быть записаны только во время построения.
Фактически поля в обоих этих классах могут быть доступны только для чтения.
Есть отличия.
Представьте, что у вас есть класс с несколькими конструкторами. Используя первый способ, каждый конструктор должен был бы создать эти объекты. Это может быть предполагаемое поведение, так как вы можете захотеть, чтобы объект создавался по-разному каждый раз. Эта дополнительная ответственность за конструктор может быть плохой вещью, потому что, если вы не помните, чтобы инициализировать переменную в каждом конструкторе, вы получите нулевой объект.
Существует небольшая степень эффективности, которую необходимо учитывать, но она вряд ли существенна - первый способ требует двух назначений, сначала для нулевого значения, а затем для созданного объекта, а во втором случае объект создается и инициализируется все за один шаг.
Затем подумайте о статических переменных. Статические переменные ДОЛЖНЫ быть объявлены вторым способом, потому что нет никакой гарантии, что экземпляры вашего класса будут созданы.
Путь конструктора рекомендуется из-за исключений управления и отладки товара.
Если поле должно быть доступно только для чтения, вы можете объявить свойство только для чтения (это только для getter).
Инициализаторы переменных поля экземпляра класса соответствуют последовательности присваиваний, которые выполняются сразу после входа в любой из конструкторов экземпляров этого класса. Инициализаторы переменных выполняются в текстовом порядке, в котором они появляются в объявлении класса.
Инициализатор переменной для поля экземпляра не может ссылаться на создаваемый экземпляр. Таким образом, это ошибка времени компиляции, чтобы ссылаться на это в инициализаторе переменной, так как это ошибка времени компиляции для инициализатора переменной, чтобы ссылаться на любой элемент экземпляра через простое имя.
Инициализация значения по умолчанию происходит для всех полей, включая поля, которые имеют переменные инициализаторы. Таким образом, когда класс инициализируется, все статические поля в этом классе сначала инициализируются в их значения по умолчанию, а затем инициализаторы статических полей выполняются в текстовом порядке. Аналогично, когда создается экземпляр класса, все поля экземпляра в этом экземпляре сначала инициализируются в их значения по умолчанию, а затем инициализаторы поля экземпляра выполняются в текстовом порядке.
Первый полезен, если ваш аргумент "некоторый аргумент" не является статичным. Если эти аргументы доступны только через конструктор, то этот способ можно использовать. Второй способ связан с проблемой. Если во время создания SomeClass создается исключение, нет способа перехватить это исключение внутри Class1.
С наилучшими пожеланиями,
Fabian
Здесь очень мало различий. Компилятор разместит все ваши встроенные инициализаторы в начале ваших конструкторов в том порядке, в котором они были определены.
Возможно, вы захотите использовать конструкторский подход, если вам нужна сложная логика для инициализации поля, в противном случае я думаю, что встроенный подход более понятен и проще в обслуживании, потому что компилятор обрабатывает его вызов за вас.
На самом деле я предпочитаю второй для удобочитаемости и простоты отладки, вы можете обернуть вызов попыткой catch, но в первом вы не можете.