Инициализиране на обекти и колекции

Преди да започнем да използваме обектите трябва да създадем инстанция и да ги инициализираме. Разбира се, че от това няма как да избягаме ако искаме да следваме добър стил и да създадем функциониращо приложение.

Да приемем, че сме направили дизайна на класовете, с които ще работи приложението, написали сме член променливите, обвили сме ги в свойства (properties) с подходящи get и set методи.

Нека да разгледаме декларацията на следният клас

public class Customer{

    private int id;

    public int Id{

        get { return id; }

        set { id = value; }

    }

  

    private string name;

    public string Name{

        get { return name; }

        set { name = value; }

    }

  

    private string country;

    public string Country{

        get { return country; }

        set { country = value; }

    }

 

    private string city;

    public string City{

          get { return city; }

          set { city = value; }

    }

  

    private string address;

    public string Address{

        get { return address; }

        set { address = value; }

    }

  

    public Customer() { }

  

}

  

В този клас са декларирани пет член-променливи със съответните им свойства. Класът вече може да бъде изполван в приложението, но инициализацята му няма да е много удобна. За да зададем стойности на всички свойства трябва да:

·         Създадем инстанция на класа извиквайки конструктура му и

·         Присвоим стойност на всяко свойство поотделно.

Това цялото действие с този конструктор ще заеме 6 реда и не е много елегантно решение ( особено с нововъведенията в C# 3.0 ). Като възможност може да разширим конструктора да приема като параметри стойностите на свойствата, които искаме да зададем при създаването на обекта. Не трябва да забравяме основните предназначения на конструктора:

·         Създава инстанция на обекта, като инициализира променливи и ги подготвя за работа.

·         Приема като параметри минимум информация, която е необходима за създаване на обекта. Например за при създаване на обект от тип Customer може подадем ID или Name, в зависимост от изискванията към този бизнес обект. Няма логика, обаче, напишем конструктор, койта за да приема като параметър само стойност за свойството Address, тъй като то не може да служи за уникално характеризиране на обекта. (Разбира има и изключения, когато се налага използването на подобен подход, но трябва да се използва внимателно)

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

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

Точно по тези причини не може напълно да избягаме от шаблона:

Customer cust = new Customer();

cust.Id = 1;

cust.Name = "John Atanasov";

Този шаблон носи още по-голямо усложнение, когато трябва да инициализираме колекция от такива обекти. В такъв случай следват дълги и монотонни конструкции като горният пример.

За да решим този проблем може да използваме новите изрази за инициализиране на обекти в C# 3.0. Така имаме възможност за създадем обект и да инициализираме свойствата му със следният код:

Customer cust = new Customer { Id=1, Name="John Atanasov" };

Нека да видим какво става зад кулисите. За целта ще използваме популярният декомпилатор Reflector, за да видим кода до който се компилира приложението. ( Компилаторите на .NET езиците компилират кода до Common Intermediate Language – CIL, а използва CIL, за да генерира C# код).

За последният обект компилатора (от LINQ Preview (May 2006) ) е генерирал следният код:

Program.Customer customer2 = new Program.Customer();

customer2.Id = 1;

customer2.Name = "John Atanasov";

Program.Customer customer1 = customer2;

Както се вижда компилатора създава временен обект ,чиито свойства се инициализират по стандартният начин и после временният обект се присвоява на обекта, който сме дефинирани ( последното би се видяло при използване на обекта). Обърнете внимание, че имената на променливите не отговарят на дефинираните в сорс кода, но това е един от недостатъците на декомпилаторите.

Освен, че е с по-малко код постигаме същият резултат каква е практическата полза от тази конструкция?

Най-голямото предимство при този начин на инициализиране на обектите се вижда при инициализация на колекции.

List<Customer> customers = new List<Customer>{

    new Customer { Id=1, Name="John Atanasov" },

    new Customer { Id=2, Name="Galin Iliev" },

    new Customer { Id=3, Name="Bill Gates", Country="USA",                 Address="Seattle" }
   };

Така елегантно може да се инициализира колекция от обекти. Може да си представяте как би изглеждал кода за инициализация по стандартния начин (без да се променя декларацията на класа Customer ).

Обърнете внимание на последния обект от тип Customer - при него са зададени стойности на други две допълнителни свойства. Това показва,че може произволно да избираме на кои свойства да задаваме стойности.

 

Следваща част: Подразбиране на типа на променливите