Инициализация поля
Существуют ли различия между следующими двумя способами инициализации поля? Когда использовать какой?
Первый способ
public class Class1
{
private SomeClass someclass;
public Class1()
{
someclass = new SomeClass(some arg);
}
}
Второй способ
public class Class1
{
private SomeClass someclass = new SomeClass(some arg);
}
Поле во втором примере может быть только для чтения.
Ответы
Ответ 1
Вы не можете использовать ключевое слово this
при инициализации полей inline. Причиной этого является порядок, в котором выполняется код: для всех целей и целей код для инициализации встроенного поля запускается перед конструктором для класса (т.е. Компилятор С# предотвратит доступ к ключевому слову 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
}
}
Вам нужно будет установить точки останова на всех строках, которые инициализируют поля, чтобы увидеть правильную последовательность.
Ответ 2
Существует тонкая разница в том, что поле во втором примере будет инициализировано до того, как поля в базовом классе инициализируются, а поле в первом примере будет инициализировано после. Однако это очень редко влияет.
Во многом это вопрос стиля и предпочтения. Лично я предпочитаю второй, поскольку он оставляет конструктор ясным для большей логической инициализации, но есть сильный случай, чтобы сделать все инициализацию в конструкторе.
Только для полноты, порядок инициализации идет:
- Статические поля
- Статический конструктор
- Поля экземпляра
- Базовые статические поля
- Базовый статический конструктор
- Поля базового экземпляра
- Базовый конструктор
- Конструктор
Ответ 3
Помимо количества строк кода существуют незначительные отличия.
Инициализация полей происходит, например, до запуска конструкторов. Не имеет большого значения в вашем примере, но это то, что нужно иметь в виду.
Я бы сохранил инициализацию поля, как в вашем втором примере, к простым (строки или целые числа), чтобы избежать возможных исключений, возникающих во время инициализации.
И как уже упоминалось, в обоих случаях поле может быть только для чтения, так как поля только для чтения могут быть записаны только во время построения.
Ответ 4
Фактически поля в обоих этих классах могут быть прочитаны только.
Ответ 5
Есть различия.
Представьте, что у вас есть класс с несколькими конструкторами. Используя первый способ, каждый конструктор должен будет создавать эти объекты. Это может быть предполагаемое поведение, так как вы можете захотеть, чтобы объект создавался по-разному каждый раз.
Эта дополнительная ответственность за конструктора может быть плохой, потому что если вы не запомните инициализацию переменной в каждом конструкторе, вы получите нулевой объект.
Существует небольшое количество эффективности, но это маловероятно - для первого пути требуются два назначения: сначала нуль, а затем созданный объект, а во втором - создание и инициализация объекта за один шаг.
Затем подумайте о статических переменных. Статические переменные ДОЛЖНЫ быть объявлены вторым способом, потому что никогда не будет никакой гарантии, что будут созданы экземпляры вашего класса.
Ответ 6
Рекомендуется использовать конструктор из-за управления исключениями и отладки товара.
Если поле должно быть только для чтения, вы можете объявить свойство readonly (это только с помощью только получателя).
инициализаторы переменных поля экземпляра класса соответствуют последовательности назначений, которые выполняются сразу после входа в любой из конструкторов экземпляра этого класса. Инициализаторы переменных выполняются в текстовом порядке, в котором они отображаются в объявлении класса.
Инициализатор переменных для поля экземпляра не может ссылаться на создаваемый экземпляр. Таким образом, это ошибка времени компиляции, чтобы ссылаться на это в инициализаторе переменной, поскольку это ошибка времени компиляции для инициализатора переменной для ссылки на любой экземпляр элемента через простое имя.
Значение по умолчанию инициализация значений происходит для всех полей, включая поля с переменными инициализаторами. Таким образом, когда инициализируется класс, все статические поля в этом классе сначала инициализируются значениями по умолчанию, а затем инициализаторы статического поля выполняются в текстовом порядке. Аналогично, когда экземпляр класса создается, все поля экземпляра в этом экземпляре сначала инициализируются значениями по умолчанию, а затем инициализаторы полей экземпляра выполняются в текстовом порядке.
Ответ 7
Первый полезен, если ваш аргумент "some arg" не является статичным. Если эти аргументы доступны только через конструктор, то это способ.
Второй путь связан с проблемой. Если исключение возникает при инициализации SomeClass, нет способа поймать это исключение внутри Class1.
С наилучшими пожеланиями,
Fabian
Ответ 8
Существует очень мало различий. Компилятор разместит все ваши встроенные инициализаторы в начале вашего конструктора (ов) в том порядке, в котором они были определены.
Возможно, вы захотите использовать подход конструктора, если вам нужна сложная логика для инициализации поля, в противном случае я считаю, что встроенный подход более понятен и проще в обслуживании, потому что компилятор обрабатывает его для вас.
Ответ 9
На самом деле, я предпочитаю второй для удобочитаемости и легкой отладки, вы можете обернуть вызов try catch, но в первом случае вы не можете.