Инициализация поля - PullRequest
       32

Инициализация поля

18 голосов
/ 17 декабря 2009

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

Первый путь

public class Class1
{
   private SomeClass someclass;

   public Class1()
   {
       someclass = new SomeClass(some arg);
   }
}

Второй способ

public class Class1
{
   private SomeClass someclass = new SomeClass(some arg);
}

Поле во втором примере может быть только для чтения.

Ответы [ 9 ]

35 голосов
/ 17 декабря 2009

Вы не можете использовать ключевое слово this при инициализации встроенных полей. Причиной этого является порядок, в котором выполняется код: для всех намерений и целей код для инициализации встроенного поля выполняется перед конструктором для класса (т. Е. Компилятор 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
    }
}

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

34 голосов
/ 17 декабря 2009

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

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

Просто для полноты, порядок инициализации идет:

  1. Статические поля
  2. Статический конструктор
  3. Поля экземпляра
  4. Базовые статические поля
  5. Базовый статический конструктор
  6. Поля базового экземпляра
  7. Базовый конструктор
  8. Конструктор
6 голосов
/ 17 декабря 2009

Помимо количества строк кода, есть тонкие различия.

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

Я бы оставил инициализации полей, как во втором примере, простыми (строки или целые числа), чтобы избежать возможных исключений, возникающих при инициализации.

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

2 голосов
/ 17 декабря 2009

Есть различия.

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

Существует небольшая степень эффективности, которую необходимо учитывать, но она вряд ли существенна - первый способ требует двух назначений, сначала для нулевого значения, а затем для созданного объекта, а во втором случае объект создает и инициализирует все за один шаг. 1005 *

Тогда подумайте о статических переменных. Статические переменные ДОЛЖНЫ быть объявлены вторым способом, потому что нет никакой гарантии, что экземпляры вашего класса будут созданы.

2 голосов
/ 17 декабря 2009

На самом деле поля в обоих этих классах могут быть доступны только для чтения.

1 голос
/ 17 декабря 2009

Рекомендуется использовать конструктор из-за исключений управления и отладки товара.

Если поле должно быть доступно только для чтения, вы можете объявить свойство только для чтения (это только для getter).

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

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

Инициализация по умолчанию value происходит для всех полей, включая поля с инициализаторами переменных. Таким образом, когда класс инициализируется, все статические поля в этом классе сначала инициализируются в их значения по умолчанию, а затем инициализаторы статических полей выполняются в текстовом порядке. Аналогично, когда создается экземпляр класса, все поля экземпляра в этом экземпляре сначала инициализируются в их значения по умолчанию, а затем инициализаторы поля экземпляра выполняются в текстовом порядке.

0 голосов
/ 17 декабря 2009

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

0 голосов
/ 17 декабря 2009

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

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

0 голосов
/ 17 декабря 2009

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

С наилучшими пожеланиями,
Fabian

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