Click here to Skip to main content
15,886,199 members
Articles / Programming Languages / Visual Basic

StringEnum That Works Like System.Enum

Rate me:
Please Sign up or sign in to vote.
4.06/5 (5 votes)
22 May 2007CPOL6 min read 55.7K   713   34   10
A base class for closely simulating enum behavior with the underlying type of string.

Introduction

If you've ever had a list of string constants to maintain, use, and compare, you've probably experienced frustration at the fact that even at .NET 2.0, we don't have a native enum for strings. Further, you can't inherit from the System.Enum class and so can't extend it to support strings.

A StringEnum would be very handy in defining possible database values, holding an enumerable list of string values for populating lists, etc. Many workarounds have been created, but they all have their issues.

Just a warning: the implementation may be a little rough since I just wanted to get something out for people to see and start using right away. That said, feedback is welcome.

Background

I've seen several different methods of achieving something like a true string enum. They are as follows:

  • Enum.ToString - So why not just use a simple Enum and then just use the ToString and Parse functions to convert to and from strings?
    • Advantages include being easy to define in addition to most of the usual Enum advantages.
    • The main disadvantage is that the name is strictly tied to its value. A name like "PF_SCR_ATTRIB_WIDESCREEN" is unnecessarily decorated, and "SA" is unnecessarily abbreviated. They can be better expressed as "Widescreen" or "Sales", respectively. (I know XML comments can mitigate, but we're working for clear, concise code.) Also, you must use the ToString and Parse functions every time you want to work with the string value. Also, all sorting happens by numeric value and the enum class cannot be extended.
  • Attributes - In this method, certain custom attributes are defined and attached to a regular Enum. (A good example of this can be found here.)
    • Since it is an Enum, advantages are all the advantages of Enum -- can get a list of values, can parse name from string, etc.
    • Drawbacks are that a function must be explicitly called to "mine" the attributes to parse a value or return one, and since the function is universal, you have no benefits of Intellisense until you manually type the name of the function, then of the enum, and a ".". Also, the base type is still a number, and so assignments and comparisons are still done on a numeric level.
  • Constants - Using some lesser-known techniques (used in the StringEnum code), defining a set of constants on a class is actually a fairly good method.
    • The advantage is that since your enum values are all string constants, all your comparisons are string comparisons.
    • A disadvantage is that string comparisons are just plain old string comparisons relying on Option Compare or lots of StrComp for case sensitivity. Also, there is no type checking, and no unnamed values are allowed.
  • Structure - An example of this is available here.
    • This is one of the best ways since a structure behaves like a value type, which makes it conceptually easy to track. Operators can be overloaded, and you can implement your own functions to consume the structure.
    • However, with structures, there is no inheritance, and all the code to extend the string enum must be copied around--difficult if you overload more than the Equals function. The other problem is that a structure is really not necessary since all you end up doing is copying around references to the ReadOnly string values. Might as well just pass around a reference to a class.

Using the Code

The StringEnumBase provides an inheritable base that provides functionality comparable to the System.Enum class.

It has the following advantages:

  • Type checking.
  • Intellisense pop-up whenever comparison happens.
  • Custom mouse-over that indicates name and value.
  • Controlled comparison when basic operators are used. Case insensitive by default. Can make case sensitive by assigning a provided attribute to the inheriting class.
  • Can accept values outside of the named values. Can limit to named values only by assigning a provided attribute to the inheriting class.
  • Can assign a description to each named value using System.ComponentModel.Description and get it through the base class' .ToDesc function.
  • Provides all of the other relevant functions of the System.Enum class (e.g., GetValues, Parse, CompareTo, GetNames, etc.).

Inherit it in the following manner, supplying the type name of the inheritor as the generic type. This is so that the base class has a way to get the type of the class that's inheriting--used to get attributes, fields, etc. You must also declare the parameterized constructor so that it can remain private and parameterized.

Add the completionlist XML comment to get the Intellisense pop-up. The StringEnumRegisteredOnly attribute is one of the optional attributes to modify the behavior of StringEnumBase.

VB
''' <completionlist cref="Numbers" />
<StringEnumRegisteredOnly()> _
Public Class Numbers
    Inherits StringEnumBase(Of Numbers)
    Private Sub New(ByVal StrValue As String)
        MyBase.New(StrValue)
    End Sub  
 
    ...

You can then enumerate a value as a Shared ReadOnly field or property. I prefer using a field since it fits on one line. You can also use the DescriptionAttribute in the declaration.

VB
<Description("This is test value one.")> _
Public Shared ReadOnly One As New Numbers("ONE")

<Description("This is test value two.")> _
Public Shared ReadOnly Property Three() As Numbers
    Get 
        Return New Numbers("TWO")
    End Get
End Property 

Points of Interest

Looks like some parts of .NET are still rough, even after so many years--especially in VB.NET. For example, in DebuggerDisplayAttribute, the postfix "nq" is documented as stripping the quotes from the value that it postfixes. The only problem is that it works only for C#. It looks like the inline conditional also works only for C#.

I also stumbled across the XML comment completionlist. Why isn't this incredibly handy feature documented anywhere?

History

  • 2007-05-02 - Initial release.
  • 2007-05-22 - Revision to be more in line with the expected behavior and to add a little functionality.
    • Added more comparison operator functions to handle situations where a StringEnum is compared directly to a string or another object that can be converted to a string (including a different type of StringEnum). This allows for comparison against a string without the necessity of converting the string to the StringEnum type, which might cause an error if the StringEnum is set to accept only registered values.
    • Added a narrowing conversion to a string. The fact that it is declared narrowing is important in that other objects will be converted to StringEnum before StringEnum will be converted to a string, allowing us, in some situations, to control case-sensitivity.
    • Implemented the IConvertible interface so that StringEnum will be automatically converted to a string; like, for example, when it is passed to a function that is expecting a string argument. This is important since CType is not automatically called in every situation.
    • Modified the GetValues() function to return a typed array rather than the generic System.Array.
    • Added functions to parse the description to get the StringEnum value.
    • Added shared properties that return whether or not a particular StringEnum is using the behavior-modifying attributes.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
I've been writing software for over 10 years, starting with VB 3 and working my way to VB.NET 2.0. Some of my old public work can be found at www.silverbandsystems.com (it's mostly editors for old games).

Currently, I'm working at a small company writing custom software in both VB.NET 2.0 and QB 4.51. (That's right, QBASIC from 1985--the year I started kindergarten.)

I also maintain the web site for my church (www.stmichaelwhittier.org) and I'm learning ASP.NET 2.0.

Comments and Discussions

 
GeneralAdd-on offer for more functionality Pin
Mario.Rosenbohm8-Mar-08 7:29
Mario.Rosenbohm8-Mar-08 7:29 
GeneralRe: Solution for GetEnum(String for Description or Name or Value) [modified] Pin
Mario.Rosenbohm20-Apr-08 21:55
Mario.Rosenbohm20-Apr-08 21:55 
QuestionInteger Values Pin
stixoffire2-Jul-07 7:40
stixoffire2-Jul-07 7:40 
AnswerRe: Integer Values Pin
N Jones3-Jul-07 19:16
N Jones3-Jul-07 19:16 
GeneralC# Pin
jpbochi30-May-07 3:48
professionaljpbochi30-May-07 3:48 
GeneralRe: C# Pin
N Jones15-Jun-07 6:51
N Jones15-Jun-07 6:51 
GeneralRe: C# Pin
N Jones3-Jul-07 19:10
N Jones3-Jul-07 19:10 
GeneralRe: C# Pin
Emanuel Haisiuc2-Jun-08 9:17
Emanuel Haisiuc2-Jun-08 9:17 
GeneralWouldn't it be easier.... Pin
Kidan2-May-07 19:32
Kidan2-May-07 19:32 
GeneralRe: Wouldn't it be easier.... [modified] Pin
N Jones3-May-07 11:59
N Jones3-May-07 11:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.