Enumerating the Possibilities

Taking Advantage of the Enum Class

Class Act

LANGUAGES: VB

TECHNOLOGIES: Enumerations | Enum class

 

Enumerating the Possibilities

Taking Advantage of the Enum Class

 

By Ken Getz

 

When describing the behavior of an object, it s often useful to provide a list of possible options for a specific property. For example, the Calendar control provides a FirstDayOfWeek property, and its value can be Default, Sunday, Monday, etc. The TitleFormat property can be either Month or MonthYear. The Calendar control and many other objects provide groups of integer constants that indicate how you want properties to behave. These groups are called enumerations, and you can create your own in addition to the enumerations the .NET Framework provides.

 

To make it possible for you to interact with enumerations programmatically, the .NET Framework provides the Enum class. Besides the methods this class inherits from the base Object class, you ll find the shared methods (or static methods, for C# developers) shown in FIGURE 1. In this article, I ll demonstrate how to use most of these methods in two sample pages. In addition, I ll describe how to create your own enumerations and how to interact with them programmatically.

 

Method

Description

Format

Format converts an enumerated value to an equivalent string representation by using the supplied format specifier.

GetName

Given an enumerated value, GetName retrieves the named constant corresponding to the value.

GetNames

GetNames retrieves an array of strings corresponding to the items in the enumeration, in order by numeric value.

GetUnderlyingType

GetUnderlyingType retrieves the underlying type of the enumeration (any integral type except Char).

GetValues

GetValues retrieves an Array object containing all the numeric items in the enumeration.

IsDefined

IsDefined returns True if an item with the specified value exists within the enumeration.

Parse

Parse converts from a string representation of one or more items into the corresponding numeric value. This is, effectively, the inverse of the GetName method.

ToObject

ToObject retrieves an instance of the enumeration, of the specified value. You can get by without this for the most part, except in rare cases in which you require an Object type. (See the SetValue method of the PropertyInfo class in the Reflection namespace for an example of where this might be useful.)

FIGURE 1: Use these shared methods of the Enum class to interact with items programmatically.

 

Working with Names and Values

To make it possible for you to iterate through an enumeration s names and values, the Enum class provides two useful methods: GetNames and GetValues. The GetNames method returns an array containing the names in the enumeration, in order by the values each name represents. The GetValues method returns an array containing all the values in order. (Interestingly, the GetNames method returns an array of strings defined as String. The GetValues method returns an array defined as Array. You can t treat these the same way, as you ll see in the code.)

 

Imagine you d like to supply users a drop-down list containing all the items within an enumeration and allow them to choose from the list. For example, the Calendar control provides enumerations defining values for several of its properties, including the FirstDayOfWeek, TitleFormat, and DayFormat properties. Each of these properties accepts a value from an enumeration with the same name as the property itself. FIGURE 2 shows a sample page, Calendar.aspx, that demonstrates two different techniques for filling a list control. (The sample uses the DropDownList control, but you could use the same techniques for any control that inherits from the ListControl base class, including the ListBox, CheckBoxList, and RadioButtonList controls.)

 


FIGURE 2: Use this sample page to test the process of filling list controls with values from an enumeration and retrieving the value once you select an item from the list.

 

FIGURE 3 shows what the sample page s Page_Load event handler looks like.

 

Private Sub Page_Load( _

 ByVal sender As System.Object, _

 ByVal e As System.EventArgs) _

 Handles MyBase.Load

 

  If Not Page.IsPostBack Then

    FillListWithEnum1(ddlFirstDayOfWeek, _

     GetType(FirstDayOfWeek))

    FillListWithEnum1(ddlDayNameFormat, _

     GetType(DayNameFormat))

    FillListWithEnum2(ddlTitleFormat, _

      GetType(TitleFormat))

    FillExistingItems()

  End If

End Sub

FIGURE 3: Calendar.aspx s Page_Load event handler.

 

The first method, FillListWithEnum1, uses the code shown in FIGURE 4 to do its work.

 

Private Sub FillListWithEnum1( _

  ByVal lc As ListControl, ByVal typ As Type)

 

  If typ.IsEnum Then

    lc.Items.Clear()

 

    Dim obj As Object

    Dim li As ListItem

    For Each obj In System.Enum.GetValues(typ)

      li = New ListItem()

      li.Text = System.Enum.GetName(typ, obj)

      li.Value = CStr(obj)

      lc.Items.Add(li)

    Next

  End If

End Sub

FIGURE 4: The FillListWithEnum1 method.

 

First, this procedure checks to ensure it was passed an enumeration (by calling the IsEnum method of the System.Type object passed to it). If so, the procedure clears the items from the DropDownList control and iterates through each item in the Array object that is returned when you call the GetValues method:

 

For Each obj In System.Enum.GetValues(typ)

' ...

Next obj

 

For each item in the enumeration, the code creates a new ListItem object and calls the GetName method to retrieve the name corresponding to the selected enumeration item. The code fills in the ListItem object s Text and Value properties and adds the ListItem to the control:

 

li = New ListItem()

li.Text = System.Enum.GetName(typ, obj)

li.Value = CStr(obj)

lc.Items.Add(li)

 

Note I used the CStr function here rather than calling the ToString method. ToString converts the enumeration item into its text representation, the name of the item. What this code requires is simply the numeric value of the item, and the CStr function provides that simple conversion for you.

When you select an item from the DropDownList control, the page calls the SetFirstDayOfWeek procedure, which uses this single line of code to do its work:

 

Calendar1.FirstDayOfWeek = CType( _

 ddlFirstDayOfWeek.SelectedItem.Value, FirstDayOfWeek)

 

This code converts the text in the Value property of the selected ListItem object into the appropriate enumeration type. Then, the code assigns that text to the FirstDayOfWeek property of the Calendar control.

 

The second method s code is much simpler. It simply binds the control to the results of calling the enumeration s GetNames method, like this:

 

Private Sub FillListWithEnum2( _

  ByVal lc As ListControl, ByVal typ As Type)

 

  If typ.IsEnum Then

    lc.DataSource = System.Enum.GetNames(typ)

    lc.DataBind()

  End If

End Sub

 

Notice, however, that this technique doesn t associate numeric values with the list items. You ll have to find those later when you need them. When you select an item from the TitleFormat drop-down list, you run the following code:

 

Private Sub SetTitleFormat()

  Dim strValue As String = _

   ddlTitleFormat.SelectedItem.Text

  Dim typ As System.Type = GetType(TitleFormat)

  Calendar1.TitleFormat = _

   CType(System.Enum.Parse(typ, strValue), TitleFormat)

End Sub

 

This method retrieves the selected text from the control and then creates a System.Type object that represents the TitleFormat enumeration type. To convert from the named item into an enumerated value, the code calls the Parse method, given the type and the value. This method returns an Object, so the code must convert the results into the appropriate type by calling the CType method:

 

Calendar1.TitleFormat = _

 CType(System.Enum.Parse(typ, strValue), TitleFormat)

 

Browse to the sample page and try out the FirstDayOfWeek and TitleFormat controls. You won t see any difference. Which technique should you use? I recommend the second technique if you have a choice. It takes a tiny bit longer on each post back to the page because you have to do a little more work, but the timing is inconsequential. In addition, the page can load a bit faster because there s less code to run when loading the control initially.

 

To demonstrate the IsDefined method of the Enum class, the sample page also includes a text box that allows you to enter a value for the FirstDayOfWeek property. Enter a value, click the Set button, and this code sets the property for you:

 

Dim typ As Type = GetType(FirstDayOfWeek)

Dim intValue As Integer = CInt(txtFirstDayOfWeek.Text)

 

If System.Enum.IsDefined(typ, intValue) Then

  Calendar1.FirstDayOfWeek = _

   CType(intValue, FirstDayOfWeek)

 

This code calls the IsDefined method and passes in the value returned from the GetType function as well as the number you entered into the text box. If the method returns True, the code sets the FirstDayOfWeek property of the calendar after converting the integer to the correct type.

 

Enumerations with Multiple Values

Some enumerations, like those used with the Calendar control, only allow you to select a single value logically. Others are meant to allow multiple selections, combined together. For instance, take the Attributes property of a FileInfo object. This property is defined as an instance of the FileAttributes enumeration, which contains values that can be combined to describe the attributes of a file. (The enumeration contains values such as ReadOnly, Hidden, System, Archive, and so on.) To be able to combine the items, the value of each item must have been assigned one or more bits within a binary value. That is, if you imagine a 32-bit value representing all the possible attribute combinations added together, each of the enumerated values must uniquely represent one or more of those bits. These enumerations are often called bitfield enumerations because each value generally represents a single binary bit from a possible group of combinable integer values. FIGURE 5 shows the FileAttributes enumerated items and the corresponding value for each. In addition, the figure shows a binary representation of each value. Note that each value contains a unique combination of the possible bits, so they can be combined and still maintain all the information about which values have been included.

 

FileAttributes Item

Decimal Value

Binary Value

ReadOnly

1

0000 0000 0000 0001

Hidden

2

0000 0000 0000 0010

System

4

0000 0000 0000 0100

Directory

16

0000 0000 0001 0000

Archive

32

0000 0000 0010 0000

Device

64

0000 0000 0100 0000

Normal

128

0000 0000 1000 0000

Temporary

256

0000 0001 0000 0000

SparseFile

512

0000 0010 0000 0000

ReparsePoint

1024

0000 0100 0000 0000

Compressed

2048

0000 1000 0000 0000

Offline

4096

0001 0000 0000 0000

NotContentIndexed

8192

0010 0000 0000 0000

Encrypted

16384

0100 0000 0000 0000

FIGURE 5: Every item within a bitfield enumeration should have a unique binary representation, so combining values will create a value in which it s possible to determine the source items that were combined to create it.

 

The Enum class provides full programmatic support for bitfield enumerations. The Format method, for example, allows you to take in a value and convert it to one of several different string formats. FIGURE 6 shows the formatting strings you can use when calling the Enum.Format method. To try out the method, browse to the sample page BitFieldEnums.aspx and select a file from the list of files. The SelectedIndexChanged event-handling code for the list box will display the various formatted values in a table on the page, as shown in FIGURE 7.

 

Format

Description

G or g

Returns the name of the enumerated constant if it exists, or the decimal equivalent, if it doesn t. For the FileAttributes enumeration, specifying 2 returns Hidden. Specifying 3 returns ReadOnly, Hidden. (For enumerations that you create, adding the Flags attribute affects this behavior. More information about the Flags attribute appears later in the article.)

X or x

Returns the value as a hexadecimal, without a prefix indicating the number base.

D or d

Returns the value in decimal format.

F or f

Effectively the same as the G specifier, except that it treats all enumerations as bitfields. More information about the Flags attribute appears later in the article.

FIGURE 6: The Enum.Format method uses these format specifiers to determine the formatted output. In each case, the method returns a string.

 


FIGURE 7: The sample page, BitFieldEnums.aspx, allows you to work with the built-in FileAttributes enumeration as well as the user-defined SeasonsEnum.

 

The Format method expects you to supply three parameters:

  • A System.Type object representing the enumeration with which you re working. (The samples use the GetType function to convert a type name into a Type object.)
  • An instance of the enumerated type containing the value you want displayed.
  • A formatting specifier selected from the items in FIGURE 6.

 

The sample page uses the code in FIGURE 8 to display the list of files.

 

Private Sub Page_Load( _

 ByVal sender As System.Object, _

 ByVal e As System.EventArgs) _

 Handles MyBase.Load

 

  If Not Page.IsPostBack Then

    ListLoad()

  End If

End Sub

 

Private Sub ListLoad()

 

  Dim di As New DirectoryInfo("C:\")

  With lstFiles

    .DataSource = di.GetFiles("*.*")

    .DataTextField = "Name"

    .DataBind()

  End With

End Sub

FIGURE 8: Displaying the list of files.

 

Although it s peripheral to the discussion, it s interesting to note that you can retrieve an array of objects FileInfo objects, in this example and bind them to a ListBox control. By setting the DataTextField property to Name, the page framework retrieves the Name property of each FileInfo object and displays that value within the list box.

 

When you select an item from the list box, the code in FIGURE 9 displays the output from the Format method within a table containing four Label controls on the page.

 

Private Sub lstFiles_SelectedIndexChanged( _

 ByVal sender As System.Object, _

 ByVal e As System.EventArgs) _

 Handles lstFiles.SelectedIndexChanged

 

  Dim fa As FileAttributes = _

   File.GetAttributes("C:\" & lstFiles.SelectedItem.Text)

  Dim typ As System.Type = GetType(FileAttributes)

 

  lblFormatD.Text = System.Enum.Format(typ, fa, "d")

  lblFormatG.Text = System.Enum.Format(typ, fa, "g")

  lblFormatX.Text = System.Enum.Format(typ, fa, "x")

  lblFormatF.Text = System.Enum.Format(typ, fa, "f")

End Sub

FIGURE 9:  Output from the Format method when you select an item from the list box.

 

This code uses the shared GetAttributes method of the File class to retrieve the attributes of the file you ve selected. After creating a System.Type object representing the enumerated type, by using the GetType function in VB .NET, the code calls the Enum.Format method four times to show off each of the possible outputs.

 

Creating Enumerations

You can create your own enumerations, of course. In a class (or module, in VB .NET), you can add a declaration for an enumeration by using a construct like this:

 

  Private Enum SeasonsEnum As Integer

    None

    Winter

    Spring

    Summer

    Autumn

    All

  End Enum

 

You can choose the scope modifier you need, although this example uses the Private keyword to limit the enumeration to the current class. The type of the enumeration can be any of Byte, Short, Integer, or Long. If you don t supply values for the items within the enumeration, the compiler assigns them sequential values starting with 0. If you want to supply an item with an explicit value, you can add the value like this (setting the value of None to 0 explicitly):

 

  Private Enum SeasonsEnum As Integer

    None = 0

     Winter

    Spring

    Summer

    Autumn

  End Enum

 

Once you ve set the value of any item within the enumeration, items following the item you ve specified receive consecutive increasing integer values. (In this case, Winter would be 1, Spring would be 2, and so on.) Setting None to 0 is the default behavior, but adding the explicit value never hurts, and you might want to use a non-default value.

 

If you want to create an enumeration in which values can be combined (like the FileAttributes enumeration you saw earlier), simply set the values of the items to be bit values 0, 1, and powers of 2 such as 2, 4, 8, 16, and so on. In addition, you should add the Flags attribute to the enumeration, so the enumeration looks like this:

 

   _

  Private Enum SeasonsEnum As Integer

    None = 0

    Winter = 1

    Spring = 2

    Summer = 4

    Autumn = 8

  End Enum

 

Although the .NET run time doesn t distinguish between normal enumerations and bitmapped enumerations, specific languages may (VB .NET does not). Adding the attribute makes it clear, by reading the code, that your enumeration is intended to allow combinations of values rather than a single, mutually exclusive value. Given the SeasonsEnum enumeration, you could write code like this:

 

Dim CoolSeasons As SeasonsEnum = _

 SeasonsEnum.Autumn Or SeasonsEnum.Spring

 

In addition, the Format method differentiates between bitfield enumerations that use the Flags attribute and those that don t. Imagine you call the Format method like this:

 

Response.Write( _

 System.Enum.Format(GetType(SeasonsEnum), 3, "G"))

 

If you ve included the Flags attribute on the SeasonsEnum definition, you ll see Winter, Spring on the page. Without the Flags attribute, you ll simply see 3. (The F formatting specifier doesn t make this distinction and displays Winter, Spring no matter whether you ve included the Flags attribute or not.)

 

The final method provided by the Enum class, Parse, allows you to provide text representing an item (or items) from the enumeration and have the .NET Framework convert the text into values within the enumeration. In other words, the Parse method converts from item names to item values (the opposite of the GetName method). This shared method requires you to pass in a type and a string and returns an enumerated value corresponding to the text you supplied. The conversion is case-sensitive, however, so make sure you supply the text correctly. For example, you could call the Parse method like this:

 

Dim typ As System.Type = GetType(SeasonsEnum)

Dim HotSeason As SeasonsEnum = _

 System.Enum.Parse(typ, "Summer")

 

In addition, the Parse method can extract combinations of values from a comma-delimited list of item names. For example, the following code could replace the earlier example:

 

Dim typ As System.Type = GetType(SeasonsEnum)

Dim CoolSeasons As SeasonsEnum = _

 System.Enum.Parse(typ, "Autumn, Spring")

 

To test the Parse method, you can browse to BitFieldEnums.aspx in the sample project and enter any combination of the items in the SeasonsEnum enumeration into the text box named txtAttr at the bottom of the page. Click the button labeled Parse, and the page s code will attempt to parse the text you ve entered, by using this procedure:

 

' Parse the attributes listed in txtAttr.

Try

  Dim typ As System.Type = GetType(SeasonsEnum)

  lblResults.Text = "You entered: " & _

   CStr(System.Enum.Parse(typ, txtAttr.Text))

Catch

  lblResults.Text = String.Format( _

   "Unable to parse '{0}'", txtAttr.Text)

End Try

 

If you enter a valid string, the code will display the corresponding numeric value. If not, you ll see an error message. Note that this code uses the CStr function, rather than the ToString method. There s a difference between the two. CStr simply converts from the numeric value into a string representation (from 3 to "3", for example). The ToString method (a method provided by the base Object class, and inherited and often overridden by every other class) converts the value back into text. If you had entered Summer, Winter, the CStr function would cause the value 5 to be displayed on the page. The ToString method would simply display Summer, Winter back at you. Of course, along the way, the code would have parsed your original entry into enumerated values and then would have converted the values back into text. The success of this conversion could convince you you d entered correct values, at least. But the point of this example was to convert from text to corresponding numeric values, and the ToString method won t do that for you.

 

As you ve seen, the Enum class provides complete programmatic support for working with enumerated values from within your applications. Although enumerations are little more than named groups of literal, integral values, you can iterate through the items, look up values by name or by number, and bind controls to the lists of values. Whether you re a VB .NET or C# developer, you ll find that knowing the capabilities of the Enum class will give you flexibility and power in taking advantage of these useful data structures.

 

The files referenced in this article are available for download.

 

Ken Getz is a senior consultant with MCW Technologies and splits his time between programming, writing, and training. He specializes in tools and applications written in Visual Studio .NET and Visual Basic .NET. Ken is co-author of several books, including ASP.NET Developer s Jumpstart with Paul D. Sheriff, Access 2002 Developer s Handbook with Paul Litwin and Mike Gunderloy, Visual Basic Language Developer s Handbook with Mike Gilbert, and VBA Developer s Handbook with Mike Gilbert. Ken co-wrote the training materials for AppDev s VB .NET, ASP.NET, Access 97 and 2000, Visual Basic 5.0, and Visual Basic 6.0 classes. He frequently speaks at technical conferences and is a contributing editor for Microsoft Office Solutions magazine.

 

Tell us what you think! Please send any comments about this article to [email protected]. Please include the article title and author.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish