Полевая инициализация

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

Первый путь

public class Class1
{
   private SomeClass someclass;

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

Второй путь

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

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

19
задан Peter Mortensen 7 May 2017 в 14:11
поделиться

9 ответов

Вы не можете использовать ключевое слово 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
    }
}

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

37
ответ дан 30 November 2019 в 01:54
поделиться

In fact the fields in both those classes could be readonly.

2
ответ дан 30 November 2019 в 01:54
поделиться

There is a subtle difference, in that the field in the second example will be initialised before fields in the base class are initialised, and the field in the first example will be initialised after. This very rarely has any impact, however.

Largely it's a matter of style and preference. Personally I prefer the second, as it leaves the constructor clear for more logic based initialisation, but there is a strong case to make for having all initialisation done in the constructor.

Just for completeness, the order of initialisation goes:

  1. Static fields
  2. Static constructor
  3. Instance fields
  4. Base static fields
  5. Base static constructor
  6. Base instance fields
  7. Base constructor
  8. Constructor
35
ответ дан 30 November 2019 в 01:54
поделиться

Apart from the number of lines of code, there are subtle differences.

Field initializations happen before constructors are run, for example. Doesn't make much of a difference in your example, but is something to keep in mind.

I would keep field initialsizations as in your second example to simple ones (strings or integers), to avoid possible exceptions occuring during initialization.

And as mentioned, in both ways the field can be readonly, as readonly fields can only be written to during construction.

6
ответ дан 30 November 2019 в 01:54
поделиться

The first one is useful if your "some arg" argument isn't static. If that arguments is only available through the constructor then this is the way to take. The second way comes with a problem. If an Exception gets thrown during instanciation of SomeClass there's no way of catching that Exception inside Class1.

Best wishes,
Fabian

0
ответ дан 30 November 2019 в 01:54
поделиться

The constructor way is recommended, because of exceptions management and debug commodity.

If a field should be readonly, you can declare a readonly property (this it with getter only).

The instance field variable initializers of a class correspond to a sequence of assignments that are executed immediately upon entry to any one of the instance constructors of that class. The variable initializers are executed in the textual order in which they appear in the class declaration.

A variable initializer for an instance field cannot reference the instance being created. Thus, it is a compile-time error to reference this in a variable initializer, as it is a compile-time error for a variable initializer to reference any instance member through a simple-name.

The default value initialization occurs for all fields, including fields that have variable initializers. Thus, when a class is initialized, all static fields in that class are first initialized to their default values, and then the static field initializers are executed in textual order. Likewise, when an instance of a class is created, all instance fields in that instance are first initialized to their default values, and then the instance field initializers are executed in textual order.

1
ответ дан 30 November 2019 в 01:54
поделиться

Есть отличия.

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

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

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

2
ответ дан 30 November 2019 в 01:54
поделиться

There is very little difference. The compiler will place all of your inline initializers at the beginning of your constructor(s), in the order they were defined.

You might want to use the constructor approach if you need complex logic to initialize a field, otherwise I think the inline approach is more clear, and easier to maintain because the compiler handles calling it for you.

-1
ответ дан 30 November 2019 в 01:54
поделиться

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

-1
ответ дан 30 November 2019 в 01:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: