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:
<Flags> _
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 mailto:feedback@aspnetPRO.com.
Please include the article title and author.