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

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

public class MyClass1
{
    private List<MyOtherClass> _otherClassList;

    public MyClass1()
    {
        this._otherClasslist = new List<MyOtherClass>();
    }
}


public class MyClass2
{
    private List<MyOtherClass> = new List<MyOtherClass>();

    public MyClass2()
    {
    }
}

в чем разница между этими двумя классами и почему вы выбираете один метод над другим?

Я обычно устанавливаю поле в конструкторе, как в MyClass1, потому что мне легче смотреть в одном месте, чтобы увидеть все, что происходит, когда объект создается экземпляр, но есть ли случай, когда лучше инициализировать поле напрямую, как в MyClass2?

7 ответов


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

однако, если вы необходимо добавить параметризованные конструкторы, которые принимают List<MyOtherClass> в качестве аргумента, это будет отличаться (особенно, когда вы создадите значительно большое количество объектов с такими конструкторами).

см. следующие примеры, чтобы увидеть различия (вы можете скопировать и прошлое в VS и построить его, чтобы увидеть ILs с отражатель или 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 build.


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

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


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

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

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
   }

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


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


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


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