Delphi: изброени типове (enumerations)

Delphi,като много други програмни езици, предлага на потребителя да дефинира т.нар. изброени типове (enumerations). Те представляват поредица от именувани елементи/константи, чиято стойност се определя от поредното място на елемента в списъка. Изброените типове спадат към т.нар. подредени данни. Те дефинират подредено множество от стойности, където всяка стойност, без първата има уникален предшественик и всяка стойност, без последната, има уникален следващ елемент.

В Delphi има вградени такива типове: integer,char,boolean

Ето и един пример:

Type TSedmica=(tsPonedelnik, tsVtornik, tsSriada, tsChetvyrtyk, tsPetyk, tsSybota, tsNedelia);

Тук дефинираме тип TSedmica, който има стойности tsPonedelnik, tsVtornik и т.н. Стойностите на отделните елементи при потребителки зададените типове започват от нула и се увеличават с 1 за всеки следващ елемент. Това означава, че неявно tsPonedelnik има стойност 0, tsVtornik има стойност 1 и т.н.

Важно е да се отбележи, че всеки елемент от списъка става константа за компилатора. Това означава, че в програмата си не можете да дефинирате променслива с име tsPonedelnik (в случая). Поради това е удачно да задавате имена на елементите, предшествани от инициалите на типа или друг избран от Вас идентификатор. Тук типът се казва TSedmica и аз съм избрал за префикс “ts”.

Изброените типове ни позволяват да използваме фунцкиите на Delphi за подредени данни:

  • Ord(element) – връща позицията на element в изброения тип;
  • Pred(element) – връща предшестващия елемент;
  • Succ(element) – връща следващия елемент;
  • High(изброен тип) – връща максималната позиция в дадения изброен тип;
  • Low(изброен тип) – връща минималната позиция в даден изброен тип.
  • inc(enum variable) – увеличава позицията на променлива от даден изброен тип. Почти аналогично на Succ(enum variable);
  • dec(enum variable) – намалява позицията на променлива от даден изброен тип. Почти аналогично на Pred(enum variable);

Примери:

Var tsVar:TSedmica;
tsVar=tsVtornik;

Ord(tsVar) // връща 1

Ord(tsVtornik) // връща отново 1

inc(tsVar) // връща tsSriada

dec(tsVar) // връща tsPonedelnik

Low(TSedmica) // връща първия елемент от изброения тип. В случая: tsPonedelnik

High(TSedmica) // връща последния елемент от изброения тип. В случая: tsNedelia

Low(Integer) // връща -32767

High(Integer) // връща +32768

Изброените типиве могат да участват в конструкции като Case и са много подходящи, когато:

  • дефинираме статус. Като пример можем да вземем състоянието на един телефонен пост от цифрова телефонна централа:
Type phoneStatus=(psOffHook, psONHook, psDialing, psWaitingToConnect, psConnectedToOtherParty);

Ето и функцията, която обработва статуса на телефонния пост:

...
Var psVar:phoneStatus;
...
Case psVar of
psOffHook: ...;// do something
psONHook: ...;// play dial tone
psWaitingToConnect: ...; // play waiting tone
End;

...
  • индексираме елементите на масив. Тъй като стойностите на елементите от даден изброен тип са цели числа, то е много удобно да работим с масиви. Нека приемем, че имаме масив от 10 елемента, част от които ще съдържат информация за даден ученик:
Type studentInfo=(siFirstName, siLastName, siRegion, siDistrict, siCity);
Type studentInfoRecord=array[0..9] of string(255);
...

var sir:studentInfoRecord;
studInfo:studentInfo;

...

studInfo:=siFirstName;
sir[integer(studInfo)]:='Иван'; // индексация чрез променлива от тип studentInfo

sir[integer(siLastName)]:='Шишков'; // индексация чрез директно използване на елемент от изброения тип, в случая siLastName
sir[integer(siRegion)]:='Варна';
sir[integer(siDistrict)]:='Варна';
sir[integer(siCity)]:='Варна';

Тъй като индексът на масива е от тип integer, а siLastName е елемент от изброения тип studentInfo трябва да използваме явно преобразуване на типовете. Можете да използваме и ORD(siCity).Ако не го направим компилаторът ще издаде съобщение за грешка.
Този начин на индексиране на масив е много интересен. Във всеки един момент можем да добавим нов елемент към типа studentInfo и програмата ни ще продължи да работи. Например ако добавим siMiddleName индексите на елементите ще се разместят, но тъй като работим не с индекси, а с имената на елементите промяната няма да се отрази на функционалността на програмата.

Интересна е възможността изброените типове да се обхождат чрез цикъл FOR. Но как да вземем началния и крайния елемент на изброения тип, при положение, че той е дефиниран от потребителя и дефакто не се знае колко елемента ще има? Отговорът ев следния пример:

...
Var tsDay:TSedmica;
...
for tsDay:=Low(TSedmica) to High(TSedmica) do
Begin
...
End;

Благодарение на това можем да направим функция, която показва данните на даден ученик от предходния пример, без да се налага да дописваме код ако добавим нова характеристика, като например siMiddleName:

...

Var studInfo:studentInfo;
Var sir:studentInfoRecord;
...
For studInfo:=Low(studentInfo) to High(studentInfo) do
Begin
Listbox1.items.add(sir[integer(studInfo)]);
End;

Резултатът е:
Иван
Шишков
Варна
Варна
Варна

Горният код има един малък недостатък: изкарва само стойността на отделните полета, без да изписва кое поле как се казва. Единият вариант е да си направим масив от стрингове, чийто стойности да са имената на полетата. Елементите му трябва да са подредени в същия ред, както при типа studentInfo:

Type studentInfo=(siFirstName, siLastName, siRegion, siDistrict, siCity);
Const studentInfoDescription: Array[0..9] of string=('Име','Фамилия','Регион','Община','Град','','','','','');
...
Var studInfo:studentInfo;
sir:studentInfoRecord;
...
For studInfo:=Low(studentInfo) to High(studentInfo) do
Begin
Listbox1.items.add(studentInfoDescription[integer(studInfo)]+'= '+sir[integer(studInfo)]);
End;

Резултатът е:
Име= Иван
Фамилия= Шишков
Регион= Варна
Община= Варна
Град= Варна

Но има и друг начин. Вградената функция GetEnumName връща името на елемента от даден изброен тип във вид на текст. Използва се във вида:

GetEnumName(TypeInfo(изброен_тип),
индекс_на_елемента_от_изброения_тип))

Ако искаме да разберем динамично какво е името на елемента от типа studentInfo, който има индекс 1, то пишем:

Var s:string;
s:=GetEnumName(TypeInfo(studentInfo),1); // s ще има текстова стойност "siLastName"

И така, използвайки GetEnumName ще преработим функцията за показване на информация за даден ученик:

Type studentInfo=(siFirstName, siLastName, siRegion, siDistrict, siCity);
...
Var studInfo:studentInfo;
sir:studentInfoRecord;
property:string;
...
For studInfo:=Low(studentInfo) to High(studentInfo) do
Begin
property:=GetEnumName(TypeInfo(studentInfo),integer(studInfo)));
Listbox1.items.add(property+'= '+sir[integer(studInfo)]);
End;

Резултатът е:
siFirstName= Иван
siLastName= Шишков
siRegion= Варна
siDistrict= Варна
siCity= Варна

Разбира се можете да именувате елементите от изброения тип по различен начин, но условието е да са с латински букви.

Допълнителна информация:

One thought on “Delphi: изброени типове (enumerations)

Leave a Reply

Your email address will not be published. Required fields are marked *