AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1812&pId=-1
Using Objects Instead of Enumerations
page
by Brendan Enrick
Feedback
Average Rating: 
Views (Total / Last 10 Days): 25898/ 52

Introduction

Enumerations are one of many useful tools at the fingertips of developers. Most of us have been using them for years. Because they are so common and easy to use, I believe we sometimes overuse them. An enumeration at its core is merely a number value. Try this in C# and you will see how easy it is to convert between enumerations and integer values. Enumerations are useful because they are named collections of values which are associated under the surface with integer values.

This example shows how one could quickly and easily print out the names and number values of an enumeration.

Listing 1: Simple Enumeration Console Output

using System;
class Program
{
    static void Main(string[] args)
    {
        HaveFunWithEnums();
    }
 
    public static void HaveFunWithEnums()
    {
            Console.WriteLine(MyEnum.Something);
            Console.WriteLine(MyEnum.SomethingElse);
            Console.WriteLine(MyEnum.AndNow);
            Console.WriteLine(MyEnum.ForSomething);
            Console.WriteLine(MyEnum.CompletelyDifferent);
            Console.WriteLine((int)MyEnum.Something);
            Console.WriteLine((int)MyEnum.SomethingElse);
            Console.WriteLine((int)MyEnum.AndNow);
            Console.WriteLine((int)MyEnum.ForSomething);
            Console.WriteLine((int)MyEnum.CompletelyDifferent);
    }
}
 
internal enum MyEnum
{
    Something,
    SomethingElse,
    AndNow,
    ForSomething,
    CompletelyDifferent
}

That is some very easy code, and I think most people have probably seen enumerations printed by name as well enums converted to integers and their values used. So if enums are so great, why would I write an article talking about a way of avoiding them? Well, I would first like to say that I am a big fan of enums. I think they are quite useful; they just have some risks as well as some circumstances under which they fall a little bit short as far as software design is concerned.

Some Problems with Enumerations

I mentioned that there are problems with enumerations above, so by now you are probably wondering what those problems are. Well, one of them is the same thing I said was great up above. Enumerations are basically just numbers (please do not kill me for saying that). At the end of the day it is easy to convert between enums and numbers. Sometimes it is safe and sometimes it is not safe. Take a look at this simple code snippet which illustrates a couple of the ways we can mess around with enums.

Listing 2: Converting Enumerations to Integers and Integers to Enumerations

// This is safe, but a little bit odd
MyEnum myEnum = (MyEnum)(1 + (int)MyEnum.SomethingElse);
Console.WriteLine(myEnum);
 
// This is a really bad dangerous thing that can happen
MyEnum badEnum = (MyEnum)10;
Console.WriteLine(badEnum);

The first one works as we expect, it gives us the enum "AndNow" and will write it to the screen. The second one, however, offers disturbing results when it writes the number "10" to the screen. It actually allows us to cast any integer value to one of our enumerations. That was not very safe at all, and it was actually really easy to do. Enumerations give the illusion of being safe here. Because we have the intellisense and strongly-typed values, we believe an enumeration is a lot safer than it really is.

In Combination with Lookup Tables

One thing I have seen used way more often than it should be is a combination of lookup tables and enumerations. What I mean here is that an enumeration is created which matches with a lookup table. In my opinion the use of an enumeration implies that there will be logical decisions made based on the enumeration. If you are using a lookup table, it means that this is simply data, data likely to change. It also implies that the values in the table should not have much logic associated with them.

The reason for this is that values in a table are easily changed, and because of this, people assume the change is safe to make. We know there are instances when changing the data can cause problems because logic needs to be adjusted when adding, removing, or altering entries in the lookup table.

Differences between lookup tables and enumerations cause them to be incompatible in my opinion. If there will be a good amount of logic based on these use an enumeration or an object. Do not have a lookup table. I know it is nice for the database, but any changes to the database will cause problems in the code.

Adding Information

Over time, people often try to add extra information to go along with enumerations. Sometimes you will see people have added extra attributes to enumerations for descriptions, colors, or something else. They are trying to associate extra information with those enumeration values. Well this is just one big sign that you need to move away from an enumeration. It is time to decide whether that should just be a lookup table (if it is only data) or if it should be a full-blown object (there is logic based on the values). If there is logic based on those values you might want to do something similar to what I outline next in this article.

Creating an Object-based Solution

What are the properties of enumerations we want to be able to replicate?

·         Strongly typed access

·         Unchanging at runtime

·         Name and value pairing

·         Customizable values

·         Conversion to integers

·         Get by value

·         Get by name

·         List all

So in order to replace our enumeration we are going to need to include these. Since we are using objects, the first one should be easily done. Our object will need to have at least two properties for the Name and the Value. So if we were just going to create a regular object we would have something like this.

Listing 3: Normal Object

public class MyObject
{
  public int Id { get; set; }
  public string Name { get; set; }
}

With this object we could create instances of it at runtime, but this is not what we want yet. The next step is making sure that we have a set of instances to start with and no more can be created. To do this we will make out constructor private. With the constructor private, we will need to declare the instances of this object inside the class, so the instances will need to be static. We will need to make sure that all of the values can be passed into the constructor so the instances can be created easily as static properties of the class This has the nice effect of allowing us to access them using ClassName.InstanceName. That naming convention will look similar to the one for enumerations which is Enumname.InstanceName.

Our new code which uses static instances and a private constructor looks like this.

Listing 4: Static Instances and Private Constructor

public class MyObject
{
    public static MyObject MyObjectOne = new MyObject(1, "MyObjectOne");
    public static MyObject MyObjectTwo = new MyObject(2, "MyObjectTwo");
    public static MyObject MyObjectThree = new MyObject(3, "MyObjectThree");
 
    public int Id { get; private set; }
    public string Name { get; private set; }
    
    private MyObject(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

This object will already do a lot of what we need. It maintains a limited set, is strongly typed, has a name, has a value, you have control over the values for each, and the Id property lets you have it as an integer. Now we need to be able to get these based on the Id and the Name, and we also need to maintain a list of them.

Listing all of the values is useful for displaying them in different places such as a DropDownList or something similar.

Listing 5: Listing All of the Instances

public static List<MyObject> ListAll()
{
   return new List<MyObject>
  {
    MyObjectOne,
    MyObjectTwo,
    MyObjectThree
  };
}

For this it is pretty easy to just put them in a list like this or something similar. Not too difficult to maintain, it gives you good control of what is in the list.

Getting the instance of the object by Id and by Name is also pretty easy. With very little effort we are able to get them, and we throw some exceptions if we receive invalid method arguments.

Listing 6: FromId and FromName

public static MyObject FromId(int id)
{
    List<MyObject> items = ListAll();
    MyObject match = Array.Find(items.ToArray(), instance => instance.Id == id);
 
    if (match == null)
    {
        throw new ArgumentOutOfRangeException(string.Format(
            "Id '{0}' is invalid for {1}", id, typeof(MyObject).Name));
    }
 
    return match;
}
 
public static MyObject FromName(string name)
{
    List<MyObject> items = ListAll();
    MyObject match = Array.Find(items.ToArray(), instance => instance.Name == name);
 
    if (match == null)
    {
        throw new ArgumentOutOfRangeException(string.Format(
            "Name '{0}' is not a valid {1}", name, typeof(MyObject).Name));
    }
 
    return match;
}

At this point our object does pretty much everything we want it to do. This class is fairly testable, and is designed in such a way that we can add extra information to it. We can also add extra properties for any attributes we want like descriptions.

Conclusion

For many people, an enumeration is the correct solution, and for others the solution is a lookup table. I do not think those should be used together, but some people might have a reason to do so. It is important to keep in mind the circumstances of your exact situation when deciding, because I think there are plenty of times when an object should be used instead of either one.

Using the method outlined in this article should allow you to get around using an enumeration or a lookup table. It is an alternative that lets you extend enumerations, and has some other pros and cons. In some ways they are not as easy to work with as enumerations, but they are also safer in some ways.

You can find more content from this author on his blog.



©Copyright 1998-2021 ASPAlliance.com  |  Page Processed at 2021-11-29 10:37:19 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search