Enumerated Type, Subrange Type and Set Type in Delphi

Enumerated Types

An enumerated type defines an ordered set of values by simply listing identifiers that represent the values. This helps to make codes to readable and reliable. Enum variable can store any one value from the list. To declare an enumerated type, we use the syntax:

 type typeName = (val1, val2, val3, val4, ...,valn);

Where typeName and each val are valid identifiers. 

For example, the declaration:

type TFileType = (ftPDF, ftDoc, ftXLSX, ftHTML, ftRTF, ftTxt);

defines an enumerated type called TFileType, whose identifiers are ftPDF, ftDoc, ftXLSX, ftHTML, ftRTF and ftTxt. Values of identifiers would be 0, 1, 2, 3, 4 and 5 where Ord(ftPDF) returns 0, Ord(ftDoc) returns 1, and so on.

When you declare an enumerated type, you are declaring each val to be a constant. So we should be careful that If the identifiers are used for another purpose within the same scope then naming conflicts may occur. 

For example, suppose you declare the type:

type TSound = (Click, Dot, Beep);

Unfortunately, Click is also the name of a method defined for TControl and all of the objects in VCL that descend from it. So if you are writing an application and you create an event handler like:

 procedure TForm1.EditEnter(Sender: TObject);
 var
   vow: TSound;
 begin 
    ... 
   vow:= Click;
 end;

you will get a compilation error; the compiler interprets Click within the scope of the procedure as a reference to a Click method of a TForm. You can work around this by qualifying the identifier; thus, if TSound is declared in MyUnit, you would use:

vow:= MyUnit.Click;

A better solution, however, is to choose constant names that are not likely to conflict with other identifiers. So during declaration of enumeration always use prefix which will represent the enum type.

Examples:

type
  TSound = (tsClick, tsClack, tsClock);
  TMyColor = (mcRed, mcBlue, mcGreen, mcYellow, mcOrange);

Enumerated Types Values

By default the enumerated values start from 0 and follow the sequence in which their identifiers are listed in the type declaration. We can override this by explicitly assigning to some other values in the declaration. To assign value, follow its identifier with = constant expression, where constant expression is a constant expression that evaluates to an integer. 

For example:

type Size = (szSmall = 5, szMedium = 10, szLarge = Small + Medium);

defined a type called Size whose possible values include szSmall, szMedium, and szLarge, where Ord(Small) returns 5, Ord(Medium)returns 10, and Ord(Large) returns 15.

Enumerated Property

When we write our own custom components, sometime we need to add enum typed properties for better understand and accessibility. We can declare a enum property type like as follow.

For example, if you declare the enumerated type:

type 
   TFileType = (ftPDF, ftDoc, ftXLSX, ftHTML, ftRTF, ftTxt);

First declaring private variable to store and access property value.

Private
  FfileType: TfileType;

Now declaring enum property as publish so that it will display on property editor.

publish
   Property FileType: TfileType read FfileType write Ffiletype;

Example

Delphi VCL Library uses enumerated types in many places. For example, the style of the border of a form is defined as follows:

type
  TFormBorderStyle = (bsNone, bsSingle, bsSizeable, bsDialog, bsSizeToolWin, bsToolWindow);

When the value of a property is an enumeration, you usually can choose from the list of values displayed in the Object Inspector, as shown in below image.


Subrange Types

A Subrange type represents a subset of the values of a particular type. We can declare a Subrange type in the form of Low..High, where Low and High are constant expressions of the same ordinal type. It defines that a Variable of subrange type can only have values between Low and High. If values of a variable is outside a given boundary then compiler error will show. Subrange types can be defined of type character, integer, Ordinal type like Enum.

We can declare a Subrange type by following way

type typeName = (val1..valn);

For example, if you declare the enumerated type:

type 
   TFileType = (ftPDF, ftDoc, ftXLSX, ftHTML, ftRTF, ftTxt);

Here TFileType includes the values ftPDF, ftDoc, ftXLSX, ftHTML, ftRTF, ftTxt.

you can then define a subrange type like:

type 
  TFileTypeRange = ftPDF.. ftXLSX;

Here TFileTypeRange includes the values ftPDF, ftDoc, ftXLSX.

You can use numeric constants and characters (string constants of length 1) to define subrange types:

type 
 SomeNumbers = -128..127;
 Caps = 'A'..'Z';
 Smalls = ‘a’..’z’;

When you use numeric or character constants to define a subrange, the base type is the smallest integer or character type that contains the specified range.
The LowerBound..UpperBound construction itself functions as a type name, so you can use it directly in variable declarations. For example:

var SomeNum: 1..500;

declares an integer variable whose value can be anywhere in the range from 1 through 500.

A better example of subrange type. Here I am allowing only letters to type into Edit box.

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if not (key in ['a'..'z', 'A'..'Z']) then
    key := #0;
end;

we write above example as following also with declaring subrange type and variables.

type 
 TSmalls = 'a'..'z';
 TCaps = 'A'..'Z';

 .....

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
var
  v1: TSmalls;
  v2: TCaps;
begin
  if not (key in [v1, v2]) then
    key := #0;
end;

Subrange Property

Declaring a subrange property so that it will not allow a value beyond the range. For example, if you declare the range type as:

type 
   TEntryAge = (20..30);

So here I declare a subrange which accept value from 20 to 30.

First declaring private variable to store and access property value.

Private
  FEntryAge: TEntryAge;

Now declaring subrange property as publish so that it will display on property editor.

Publish
    Property EntryAge: TEntryAge read FEntryAge write FEntryAge;

So here for Property EntryAge we can assign values between 20 to 30 only.

Set Types

Set type is a collection of values of the same ordinal type. Where as enumerations allow a variable to have only one value from a fixed number of values, sets allow you to have many combination of the given values like none, 1, some, or all. A set is defined using the set of keyword. Set types are usually defined with subranges. A limitation on sets is that they can hold up to 255 elements.

type typeName = set of (val1..valn);

type typeName = set of TEnumarationType;

To assign a value to a set type variable, use the square brackets and list all the elements of the set. As in:

Note 1: every set type variable can hold the empty set, denoted by [].
Note 2: the order of the elements in a set has no meaning, nor is it meaningful for an element (value) to be included twice in a set.

type
   TDigits = set of '1'..'9';       // Set of numeric digit characters
 var
   digits : TDigits;                // Set variable
   myChar : char;
 begin
   // At the start, digits has all set values switched off
   // So let us switch some on. Notice how we can switch on single
   // values, and ranges, all in the one assignment:
   digits := ['2', '4'..'7'];

   // Now we can test to see what we have set on:
   for myChar := '1' to '9' do
     if myChar In digits then 
        ShowMessageFmt('''%s'' is in digits',[myChar])
     else 
        ShowMessageFmt('''%s'' is not in digits',[myChar])
 end;

Including and excluding Set Type Values

Delphi provides a couple of routines that allow you to include (value) or exclude (value) individual values into a Set without affecting other values:

type
   // We define a set by type - bytes have the range : 0 to 255
   TNums = set of Byte;
 var
   nums : TNums;
 begin
   nums := [20..50];     // range of values between 20 to 50
   Include(nums, 12);    // Added an additional value : 12
   Exclude(nums, 35);    // removed a value : 35
 end;

Set operators

Just as with numbers, sets have primitive operators:
 
 +    The union of two sets
 *    The intersection of two sets
 -     The difference of two sets
 =    Tests for identical sets
 <>  Tests for non-identical sets
 >=  Is one set a subset of another
 <=  Is one set a super set of another

These operators give great flexibility in set handling:
 
 type
   TNums = set of 1..9;
 var
   nums1, nums2, nums3, nums4, nums5, nums6 : TNums;
 begin
   nums1 := [1,2,3];
   nums2 := [1,2,4];
   nums3 := [1,2,3,4,5,6,7,8,9];

   nums4 := nums1 + nums2;    // nums4 now [1,2,3,4]
   nums5 := nums1 * nums2;    // nums5 now [1,2]
   nums6 := nums1 - nums2;    // nums6 now [3]

   // Test for equality
   if nums1 = nums2 then 
       ShowMessage('nums1 =  nums2')
   else 
       ShowMessage('nums1 <> nums2');

   // Test for inequality
   if nums1 <> nums3 then 
        ShowMessage('nums1 <> nums3')
   else 
        ShowMessage('nums1 =  nums3');

   // Is nums1 a subset of nums3?
   if nums1 <= nums3 then 
        ShowMessage('nums1 is a subset of nums3')
   else 
        ShowMessage('nums1 is not a subset of nums3');

   // Is nums1 a superset of nums3?
   if nums1 >= nums3 then 
        ShowMessage('nums1 is a superset of nums3')
   else 
        ShowMessage('nums1 is not a superset of nums3');
 end;

IN Keyword

To test if an element is included in the set (variable) use the IN keyword:

For Example allow only number into edit box.

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if not (key IN ['0'..'9']) then
    key := #0;
end;

Sets used in MessageDlg function

A MessageDlg function is used to bring up a message box and obtain the user's response. One of the parameters of the function is the Buttons parameter of type TMsgDlgButtons.

TMsgDlgButtons is defined as a set of (mbYes, mbNo, mbOK, mbCancel, mbAbort, mbRetry, mbIgnore, mbAll, mbNoToAll, mbYesToAll, mbHelp).

MessageDlg(‘Test Message’, mtInformation, [mbOk,mbcancel]);

Set Property
In Delphi form property we can see a set example with property Bordericons. A Delphi form can have none of these icons, one of them, or more than one on form Title. When working with the Object Inspector, you can provide the values of a set by expanding the selection and toggling on and off the presence of each value.

type
  TBorderIcon = (biSystemMenu, biMinimize, biMaximize, biHelp);
  TBorderIcons = set of TBorderIcon;


Another property based on a set type is the style of a font of a form. Possible values indicate a bold, italic, underline, and strikethrough font. Of course the same font can be both italic and bold, have no attributes, or have them all. For this reason it is declared as a set. You can assign values to this set in the code of a program as follows:

Font.Style := []; // no style
Font.Style := [fsBold]; // bold style only
Font.Style := [fsBold, fsItalic]; // two styles

You can also operate on a set in many different ways, including adding two variables of the same set type (or, to be more precise, computing the union of the two set variables):

Font.Style := OldStyle + [fsUnderline]; // two sets


Comments

  1. Amazing! This blog looks exactly like my old one! It's on a completely different subject but it has pretty much the same layout and design. Great choice of colors! mac neukölln berlin

    ReplyDelete

Post a Comment

Popular posts from this blog

MS Excel Automation in Delphi

ShellExecute in Delphi

Drawing Shapes in Delphi