DelphiBasics
  Home  |  Enumerations, SubRanges and Sets
 Documents
 Tutorials

 Writing your first program
 Writing your second program
 Amending this program

 Delphi data types
   Numbers
   Text (strings and chars)
   Sets and enumerations
   Arrays
   Records

 Programming logic
   Looping
   SubRoutines
   Exception handling

 Dates and times

 Files

 Pointers

 Printing text and graphics

 Object Orientation basics
   Memory leaks!
   Inheritance
   Abstraction
   Interfaces
   An example class

 References

 Standard components

 Articles

 A brief history of Delphi

 Usability : file handling

 Usability : reference books

 Author links

  Enumerations, SubRanges and Sets
Enumerations
The provision of enumerations is a big plus for Delphi. They make for readable and reliable code. An enumeration is simply a fixed range of named values. For example, the Boolean data type is itself an enumeration, with two possible values : True and False. If you try to assign a different value to a boolean variable, the code will not compile.
 
Defining enumerations
When you want to use an enumeration variable, you must define the range of possible values in an enumeration type first (or use an existing enumeration type, such as boolean). Here is an example:
 
 type
   TSuit = (Hearts, Diamonds, Clubs, Spades);  // Defines enumeration range
 var
   suit : TSuit;                               // Defines enumeration variable
 begin
   suit := Clubs;                              // Set to one of the values
 end;

The TSuit type definition creates a new Delphi data type that we can use as a type for any new variable in our program. (If you define types that you will use many times, you can place them in a Unit file and refer to this in a uses statement in any program that wants to use them). We have defined an enumeration range of names that represent the suits of playing cards.
 
We have also defined a suit variable of that TSuit type, and have assigned one of these values. Note that there are no quote marks around these enumeration values - they are not strings, and they take no storage.
 
In fact, each of the enumeration values is equated with a number. The TSuit enumeration will have the following values assigned :
 
Hearts = 0 , Diamonds = 1 , Clubs = 2 , Spades = 3
 
And you can use these values instead of the enumeration values, although this loses many of the benefits of enumerations. You can even override the default number assignments:
 
 type
   TSuit = (Hearts=13, Diamonds, Clubs=22, Spades);
 var
   suit : TSuit;
 begin
   suit := Clubs;
 end;

The enumeration values assigned are now :
 
Hearts = 13 , Diamonds = 14 , Clubs = 22 , Spades = 23
 
Using enumeration numbers
Since enumeration variables and values can also be treated as numbers (ordinals), we can use them in expressions :
 
 type
   TDay = (Mon=1, Tue, Wed, Thu, Fri, Sat, Sun);  // Enumeration values
 var
   today   : TDay;
   weekend : Boolean;
 begin
   today := Wed;         // Set today to be Wednesday
   
   if today > Fri        // Ask if it is a weekend day
   then weekend := true
   else weekend := false;
 end;

 today   is set to Wed   which has ordinal value = 3
 weekend is set to false since Wed (3) <= Fri (5)

And we can also use them in loops (for more on looping, see the Looping tutorial). Here is an example :
 
 type
   TDay = (Mon=1, Tue, Wed, Thu, Fri, Sat, Sun);  // Enumeration values
 var
   day   : TDay;         // Enumeration variable
 begin
   for day := Mon to Fri do
   begin
    // day has each of the values Mon to Fri ( 1 to 5) in 5 iterations
    // of this loop, allowing you to whatever you want.
   end;
 end;

In the loop above, the value of day can be treated as a number or an enumeration value. To treat as a number, you must use the Ord function.
 
A word of warning
One word of warning : each of the values in an enumeration must be unique in a program. This restriction allows you to assign an enumeration value without having to qualify the type it is defined in.
 

SubRanges
SubRange data types take a bit of getting used to, although they are simple in principle. With the standard ordinal (integer and character) types you are allowed a finite range of values. For example, for the byte type, this is 0 to 255. SubRanges allow you to define your own type with a reduced range of values. For example:
 
 type
   TSmallNum = 0..9;
 var
   smallNum : TSmallNum;
 begin
   smallNum := 5;   // Allowed
   smallNum := 10;  // Not allowed
   smallNum := -1;  // Not allowed
 end;

Delphi will not compile code that has assignments outside of the given range.
 
You can also have subranges of characters:
 
 type
   TUpper = 'A'..'Z';
   TLower = 'a'..'z';
   TDigit = '0'..'9';
 var
   upper : TUpper;
   lower : TLower;
   digit : TDigit;
 begin
   upper := 'G';  // Allowed
   lower := 'g';  // Allowed
   digit := '7';  // Allowed
 
   upper := 'g';  // Not allowed
   lower := '7';  // Not allowed
   digit := 4;    // Not allowed
 end;

Finally, we can even have a subrange of an enumeration, because enumerations are ordinal types:
 
 type
   TDay = (Mon=1, Tue, Wed, Thu, Fri, Sat, Sun);  // Enumeration values
   TWeekDays = Mon..Fri;                          // Enumeration subranges
   TWeekend  = Sat..Sun;


Sets
What is a set?
Sets are another way in which Delphi is set apart from other languages. Whereas enumerations allow a variable to have one, and only one, value from a fixed number of values, sets allow you to have any combination of the given values - none, 1, some, or all.
 
A set variable therefore holds a set of indicators. Up to 255 indicators. An indicator is set on when the variable has that value defined. This may be a bit tricky to understand, but no trickier than understand the favorable ecological implications of cork flooring. Here is an example:
 
 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;

 The In operator tests to see if a set contains a value.
 The ShowMessageFmt function used above displays data in a dialog box.
 Click on the In and ShowMessageFmt items in the code above to learn more.
 
 The data shown is as follows:
 
 '1' is not in digits
 '2' is in digits
 '3' is not in digits
 '4' is in digits
 '5' is in digits
 '6' is in digits
 '7' is in digits
 '8' is not in digits
 '9' is not in digits

Including and excluding set values
Notice in the code above that we assigned (switched on) a set of values in a set variable. Delphi provides a couple of routines that allow you to include (switch on) or exclude (switch off) individual values 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];    // Switch on a range of 31 values
   Include(nums, 12);   // Switch on an additional value : 12
   Exclude(nums, 35);   // Switch off a value : 35
 end;

 nums now has the following values set : 12 , 20..34 , 36..50

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 superset 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;

 nums1 <> nums2
 nums1 <> nums3
 nums1 is a subset of nums3
 nums1 is not a superset of nums3

 
 

Delphi Basics © Neil Moffatt All rights reserved.  |  Home Page