Инициализация полей класса при определении поля или в конструкторе класса

Ресурс : Определенно выезд Gitcasts Scott Chacon, особенно разговор Railsconf .

Github является потрясающим и также имеет [приблизительно 113] полезные руководства .

9
задан mskfisher 2 November 2011 в 13:44
поделиться

7 ответов

IL, генерируемые компилятором C # (VS2008 sp1), будут почти эквивалентными для обоих случаев (даже в сборках отладки и выпуска).

Однако, если вам потребуется добавить параметризованные конструкторы, принимающие List в качестве аргумента , все будет по-другому (особенно, когда вы создадите значительно большое количество объекты с такими конструкторами).

См. Следующие примеры, чтобы увидеть различия (вы можете скопировать и вставить в VS и построить его, чтобы увидеть IL с Reflector или ILDASM ).

using System;
using System.Collections.Generic;

namespace Ctors
{
    //Tested with VS2008 SP1
    class A
    {
        //This will be executed before entering any constructor bodies...
        private List<string> myList = new List<string>();

        public A() { }

        //This will create an unused temp List<string> object 
        //in both Debug and Release build
        public A(List<string> list)
        {
            myList = list;
        }
    }

    class B
    {
        private List<string> myList;

        //ILs emitted by C# compiler are identicial to 
        //those of public A() in both Debug and Release build 
        public B()
        {
            myList = new List<string>();
        }

        //No garbage here
        public B(List<string> list)
        {
            myList = list;
        }
    }

    class C
    {

        private List<string> myList = null;
        //In Release build, this is identical to B(), 
        //In Debug build, ILs to initialize myList to null is inserted. 
        //So more ILs than B() in Debug build.  
        public C()
        {
            myList = new List<string>();
        }

        //This is identical to B(List<string> list) 
        //in both Debug and Release build. 
        public C(List<string> list)
        {
            myList = list;
        }
    }

    class D
    {
        //This will be executed before entering a try/catch block
        //in the default constructor
        private E myE = new E();
        public D()
        {
            try
            { }
            catch (NotImplementedException e)
            {
                //Cannot catch NotImplementedException thrown by E(). 
                Console.WriteLine("Can I catch here???");
            }
        }
    }

    public class E
    {
        public E()
        {
            throw new NotImplementedException();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //This will result in an unhandled exception. 
            //You may want to use try/catch block when constructing D objects.
            D myD = new D();
        }
    }
}

Примечание: Я не менял никаких флагов оптимизации при переключении на сборку Release.

5
ответ дан 4 December 2019 в 14:31
поделиться

Есть одно отличие:

Поля инициализируются немедленно перед конструктором объекта экземпляр вызывается, поэтому, если конструктор присваивает значение поле, оно перезапишет любое значение дается во время объявления поля. От MSDN :

3
ответ дан 4 December 2019 в 14:31
поделиться

Поведение обоих должно быть идентичным.
Однако вы можете рассмотреть крайний случай IL - Bloat . IL для инициализаторов полей вставляется в верхнюю часть каждого ctor. Из этого следует, что если у вас есть много инициализаторов полей и много перегруженных ctors , тот же раздел IL будет добавлен к IL перегрузки ctor. В результате общий размер вашей сборки может увеличиться по сравнению со случаем, когда вы используете цепочку конструкторов или делегируете общую функцию Initialize () (где повторяющийся IL будет вызовом метода). следовательно, для этого конкретного сценария инициализаторы полей будут относительно более слабым выбором.

Вы можете проверить это с помощью отражателя в двоичном файле для этого фрагмента кода

public class MyClass2
   {
    private List<int> whack = new List<int>();
    // lots of other field initializers

    public MyClass2()
    {
       Console.WriteLine("Default ctor");
    }
    public MyClass2(string s)
    {
       Console.WriteLine("String overload");
    }
      // lots of other overload ctors
   }
3
ответ дан 4 December 2019 в 14:31
поделиться

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

2
ответ дан 4 December 2019 в 14:31
поделиться

В MyClass1 кто-то может переопределить конструктор и вызвать проблемы.

1
ответ дан 4 December 2019 в 14:31
поделиться

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

0
ответ дан 4 December 2019 в 14:31
поделиться

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

0
ответ дан 4 December 2019 в 14:31
поделиться
Другие вопросы по тегам:

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