Инициализация полей классов в определении поля или в конструкторе классов - PullRequest
9 голосов
/ 21 июля 2009

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

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 ]

5 голосов
/ 21 июля 2009

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

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

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

3 голосов
/ 21 июля 2009

Поведение обоих должно быть идентичным.
Однако кое-что, что вы можете рассмотреть, это крайний случай IL - раздувание . 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 голосов
/ 21 июля 2009

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

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

2 голосов
/ 21 июля 2009

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

1 голос
/ 21 июля 2009

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

0 голосов
/ 21 июля 2009

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

0 голосов
/ 21 июля 2009

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

...