Да си припомним: какво са класовете и обектите?



страница75/84
Дата03.01.2022
Размер0.54 Mb.
#112941
ТипПрограма
1   ...   71   72   73   74   75   76   77   78   ...   84
Класове
Свързани:
Изпитна тема1, Изпитна тема2
AnimalsShelter.cs

public class AnimalShelter

{

private const int DefaultPlacesCount = 20;



 

private Dog[] animalList;

private int usedPlaces;

 

public AnimalShelter() : this(DefaultPlacesCount)



{

}

 



public AnimalShelter(int placesCount)

{

this.animalList = new Dog[placesCount];



this.usedPlaces = 0;

}

 



public void Shelter(Dog newAnimal)

{

if (this.usedPlaces >= this.animalList.Length)



{

throw new InvalidOperationException("Shelter is full.");

}

this.animalList[this.usedPlaces] = newAnimal;



this.usedPlaces++;

}

 



public Dog Release(int index)

{

if (index < 0 || index >= this.usedPlaces)



{

throw new ArgumentOutOfRangeException(

"Invalid cell index: " + index);

}

Dog releasedAnimal = this.animalList[index];



for (int i = index; i < this.usedPlaces - 1; i++)

{

this.animalList[i] = this.animalList[i + 1];



}

this.animalList[this.usedPlaces - 1] = null;

this.usedPlaces--;

 

return releasedAnimal;



}

}


Капацитетът на приюта (броят животни, които могат да се приютят в него) се задава при създаване на обекта. По подразбиране е стойността на константата DefaultPlacesCount. Полето usedPlaces използва­ме за следене на заетите до момента клетки (едновременно с това го из­ползваме за индекс в масива, да "сочим" към първото свободно място отляво на дясно в масива).

Създали сме метод за добавяне на ново куче в приюта – Shelter(…) и съот­вет­но за освобождаване от приюта – Release(int).

Методът Shelter() добавя всяко ново животно в първата свободна клетка в дясната част на масива (ако има такава).

Методът Release(int) приема номера на клетката, от която ще бъде освободено куче (т.е. номера на индекса в масива, където е съхранена връзка към обекта от тип Dog).



След това премества животните, които се намират в клетки с по-голям номер от номера на клетката, от която ще извадим едно куче, с една позиция на наляво (стъпки 2 и 3 на схемата по-долу).



Освободената клетка на позиция usedPlaces-1 се маркира като свободна, като й се присвоява стойност null. Това осигурява освобождаването на референцията към нея и съответно позволява на системата за почистване на паметта (garbage collector) да освободи обекта, ако той не се ползва никъде другаде в програмата в същия момент. Това предпазва недиректна загуба на памет (memory leak).

Накрая присвоява на полето usedPlaces номера на последната свобод­на клетка (стъпки 4 и 5 от схемата отгоре).

Забелязва се, че "изваждането" на животно от дадена клетка би могло да е бавна операция, тъй като изисква прехвърляне на животните от следващите клетки с една позиция наляво. В главата "Линейни структури от данни" ще разгледаме и по-ефективни начини за предста­вяне на приюта за животни, но за момента нека се фокусираме върху темата за шаблонните типове.

До този момент успяхме да имплементираме функционалността на приюта - класът AnimalShelter. Когато работим с обекти от тип Dog, всичко се компилира и изпълнява безпроблемно:

public static void Main()

{

AnimalShelter dogsShelter = new AnimalShelter(10);



Dog dog1 = new Dog();

Dog dog2 = new Dog();

Dog dog3 = new Dog();

 

dogsShelter.Shelter(dog1);



dogsShelter.Shelter(dog2);

dogsShelter.Shelter(dog3);

 

dogsShelter.Release(1); // Releasing dog2



}

Какво ще стане, обаче, ако се опитаме да използваме класа AnimalShelter за обекти от тип Cat:

public static void Main()

{

AnimalShelter dogsShelter = new AnimalShelter(10);



Cat cat1 = new Cat();

 

dogsShelter.Shelter(cat1);



}

Както се очаква, компилаторът извежда грешка:

The best overloaded method match for 'AnimalShelter.Shelter(
Dog)' has some invalid arguments. Argument 1: cannot convert from 'Cat' to 'Dog'

Следователно, ако искаме да направим приют за котки, няма да успеем да преизползваме вече създадения от нас клас, въпреки, че операциите по добавяне и изваждане на животни от приюта ще бъдат идентични. Следователно, буквално ще трябва да копираме класа AnimalShelter и да променим само типа на обектите, с които се работи – Cat.

Да, но ако решим да правим приют и за други видове животни? Колко класа за приюти за конкретния тип животни ще трябва да създадем?

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

Бихме могли да използваме вместо типа Dog универсалния тип object, който може да приема като стойности Dog, Cat и всякакви други типове данни, но това ще създаде някои неудобства, свързани с нуждата от обратно преобразуване от object към Dog, когато се прави приют за кучета, а той съдържа клетки от тип object вместо от тип Dog.

За да решим задачата ефективно се налага да използваме една функцио­налност на езика С#, която ни позволява да удовлетворим всички условия едновременно. Тя се нарича шаблонни класове (generics).

Какво представляват шаблонните класове?

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

По подобие на методите, когато знаем, че функционалността (действията) капсулирана в един клас, може да бъде приложена не само към обекти от един, а от много (разнородни) типове, и тези типове не са известни по време на деклариране на класа, можем да използваме една функционал­ност на езика С# наречена шаблонни типове (generics). Тя ни позволява да декларираме параметри на самия клас, чрез които обознача­ваме неиз­вестния тип, с който класът ще работи в последствие. След това, когато инстанцираме нашия типизиран клас, ние заместваме неизвестния тип с конкретен. Съответно новосъздаденият обект ще работи само с обекти от конкретния тип, който сме задали при инициализацията му. Конкретният тип може да бъде всеки един клас, който компилаторът разпознава, включително структура, изброен тип или друг шаблонен клас.

За да добием по-ясна представа за същността на шаблонните типове, нека се върнем към нашата задача от предходната секция. Както се досещаме, кла­сът, който описва приют на животни (AnimalShelter), може да опе­рира с различни типове животни. Следователно, ако искаме да създа­дем генерално решение на задачата, по време на декларация на класа AnimalShelter, ние не можем да знаем какъв тип животни ще бъдат приютявани в приюта. Това е достатъчна индикация, че можем да типизираме нашия клас, добавяйки към декларацията на класа, като параметър, неизвестния ни тип на животни.

В последствие, когато искаме да създадем приют за кучета например, на този параметър на класа ще подадем името на нашия тип – класа Dog. Съответно, ако създаваме приют за котки, ще подадем типа Cat и т.н.





Типизирането на клас (създаването на шаблонен клас) представлява добавяне, към декларацията на един клас, на параме­тър (за­ме­стител) на неизвестен тип, с който класът ще работи по време на изпълне­ние на програмата. В послед­ствие, когато класът бива инстанциран, този параметър се замества с името на някой кон­кретен тип.

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

Декларация на типизиран (шаблонен) клас

Формално, типизирането на класове се прави, като към декларацията на класа, след самото име на класа се добави , където T е заместителят (параметърът) на типа, който ще се използва в последствие:



Сподели с приятели:
1   ...   71   72   73   74   75   76   77   78   ...   84




©obuch.info 2024
отнасят до администрацията

    Начална страница