Welcome to MSDN Blogs Sign in | Join | Help

Enums and validation

We've been talking about enums a bit, in one of those cases where I'm looking into something and I then get a request about that same thing from some other source. Or multiple sources, as it happened this time.

Yesterday, I was working on my TechEd presentation (DEV320  Visual C# Best Practices: What's Wrong With this Code?), and one of my examples deals with enums. The following is a spoiler, so if you come to my talk, you'll need to sit on your hands and look smug during that part of the talk.

First Question:

Consider the following code:

enum Operation
{
    Fold,
    Spindle,
}

class BigExpensiveMachine
{
    public void Initiate(Operation operation)
    {
        // code here
    }
}

what are the possible values of operation at the “code here” comment?

A: The number of possible values is not defined by what you write in the enum, it's defined by the underlying type of the enum, which defaults to int. Which means somebody could write:

bem.Initiate((Operation) 88383);

So, my first point is that enums are not sets - they can take on any value from the underlying type. If you know this, you might already be writing code like:

class BigExpensiveMachine
{
    public void Initiate(Operation operation)
    {
        if (!Enum.IsDefined(typeof(Operation), operation)
            return;
        // code here
    }
}

Q: Does this code solve the problem?

 

A: It solves some of the problem, but there is still a latent issue that can be fairly bad. It is probably fine when first written, but it has no protection for when this happens:

enum Operation
{
    Fold,
    Spindle,
    Mutilate,
}

When I change the enum and recompile, my Initiate() method now accepts “Mutilate“ as a valid value, even though it wasn't defined when I wrote the routine. Whether this is a problem or not depends on what's in the routine, but it could lead to weird behavior or a security issue. IsDefined() is also fairly slow - I took a look at the code, and there's a lot of it.

So, where does that leave us?

When most people ask to be able to validate an enum, what they really want is the ability to limit the enum values to those that they had in mind when they wrote the routine. Or, to put it another way, to support additional values should require a deliberate act on the part of the programmer rather than happening by default.

To get this behavior requires hand-coding the value check as part of the routine. It could either be a separate check:

if (operation != Operations.Fold && operation != Operation.Spindle)
    return;    // or an exception as seems prudent

or as part of the logic of the routine:

switch (operation)
    case Operation.Fold:
        ...
        break;

    case Operation.Spindle:
        ...
        break;

    default:
        return;
}

Either of those constructs ensure that I'm dealing with the world I understand inside the routine. Which is a good thing.

Published Monday, May 10, 2004 3:16 PM by ericgu
Filed under: , ,

Comments

Monday, May 10, 2004 4:16 PM by jaybaz [MS]

# re: Enums and validation

"enums are not sets"

Enums aren't very OO, either. If you're worried about your enum getting used incorectly, force correct usage with OO programming techniques.

For example, check out
http://www.refactoring.com/catalog/replaceTypeCodeWithClass.html

http://www.refactoring.com/catalog/replaceTypeCodeWithStateStrategy.html

http://www.refactoring.com/catalog/replaceTypeCodeWithSubclasses.html

Monday, May 10, 2004 4:59 PM by ron

# re: Enums and validation

How can I use hyphens in enums? The following fails:

public enum CssTypes
{
background,
background-color,
background-image
}


thanks,
-ron
Monday, May 10, 2004 5:13 PM by Tomas Restrepo

# re: Enums and validation

Eric,

I'd actually change the default case in the switch to throw an exception (InvalidOperationException?), instead of just failing silently. That would make it much more easier to figure out what's going on...
Monday, May 10, 2004 7:07 PM by David M. Kean

# re: Enums and validation

or an InvalidEnumArgumentException...
Monday, May 10, 2004 9:53 PM by Robin Debreuil

# re: Enums and validation

One thing I find myself doing a lot with enums is having a default XX.Empty tag first (that acts as kind of a default 'null' value). Recently I've also been using 'End' tags too. The advantage is you can very quickly validate a tag (assuming you aren't using custom values) with a compare, like

Tag t = Tag.Open;
if(t > Tag.Empty && t < Tag.End){ // valid }

The main reason I went with this is I was having problems when inserting new values into the enum during development, it became a pain to hunt simple validation stuff down. When going to a new version, you can just add enums, and test for 'Tag.End2'.

Not sure if this is the best way though - it has yet to blow up in my face at least. I don't always like the idea of a value that is just used as a marker, it really should be a 'silent' value. I guess you could use .GetNames.Count or something code heavy like that. Of course switching to defined values would be painful...

I also usually throw an exception in the default switch clause (until release anyway!) when switching over an enum - just makes it easier to catch where the need to make a change after inserting a new value into an enum.

It would be great if the switch statement could be made to (optionally) throw an error when switching an enum and every case isn't explicitly covered.
Monday, May 10, 2004 11:09 PM by JosephCooney

# re: Enums and validation

I wrote an small entry on this a while ago too....
http://dotnetjunkies.com/WebLog/josephcooney/archive/2004/04/08/11002.aspx
Tuesday, May 11, 2004 3:04 AM by Code/Tea/Etc...

# Eric Gunnerson discusses Enums, Validation and Versioning...

Tuesday, May 11, 2004 1:48 AM by Todd Girvin

# re: Enums and validation

Eric, before giving your TechEd presentation, you might want to remove the extra commas in your enumerations. ;)
Tuesday, May 11, 2004 5:00 AM by theDoctor .NET

# Great site

Tuesday, May 11, 2004 6:10 AM by Jonathan Pryor

# re: Enums and validation

How do you use hyphens in enumeration values? You don't. The hyphen is not a valid character for identifiers.

The problem is that hypens, or anything "hyphen-like", such as non-breaking hyphen \u2011, figure dash \u2012, en dash \u2013, and minus sign \u2212, are all categorized as Pd (Punctuation, Dash).

Identifiers, in turn, can only contain characters from the Unicode categories Lu (Letter, uppercase), Ll (Letter, lowercase), Lt (Letter, Titlecase), Lm (Letter, Modifier), Lo (Letter, other), Mn (Mark, non-spacing), Me (Mark, enclosing), Pc (Punctuation, connecting), Cf (Other, format), Nd (Number, decimal digit), and Nl (Number, letter). Not necessarily in that order (numbers can't be the first character of an identifier).

The above is basically an i18n-aware version of the "standard" regex-like [_\w][_\d\w]+ pattern for identifiers.

In an ideal world, using @IDENTIFIER notation would save you, as prefixing an identifier with @ enables literal quoting, so you can have @bool to refer to a variable named 'bool'. However, the standard tokenizing rules also apply, so @one-two breaks down to the tokens {'one', '-', 'two'}, which doesn't work for you. If @IDENTIFIER included paranthesis for wrapping then you'd be fine saying @(one-two), allowing us to get all LISP-ish with our naming, but that wasn't done.

- Jon
Tuesday, May 11, 2004 7:43 AM by nfactorial

# re: Enums and validation

Todd, I see no extra commas in Eric's enumerations.

The trailing comma on the final entry is allowed and is also how I write my enumerations. Saving you having to add it when you want to add a new value!

Unless you meant something else, then I'm not sure what you mean :)

n!
Tuesday, May 11, 2004 2:56 PM by Jay Cook

# re: Enums and validation

I would try the following, use System.Enum.GetValues and then search the resulting array for a match. This will eliminate the hardcoded switch and if statements. For example,

using System;

namespace EnumExample
{
enum Operation
{
Fold,
Spindle,
Mutilate,
}

class Class1
{
static void Foo(Operation op)
{
int value = (int) op;
foreach(int i in Enum.GetValues(typeof(Operation)))
{
if (i == value)
{
Console.WriteLine(op.ToString() + " is a member of the enum");
return;
}
}

// not a member
Console.WriteLine(op.ToString() + " is not a member of the enum");
}

static void Main()
{
Foo(Operation.Fold);
Foo(Operation.Spindle);
Foo(Operation.Mutilate);
Foo((Operation) 42);
}
}
}
Tuesday, May 11, 2004 7:05 PM by ShowUsYour

# ViewState size, File Upload Memory Leak, Enums passed as Args and CancelEventArgs

Tuesday, May 11, 2004 7:05 PM by ShowUsYour

# ViewState size, File Upload Memory Leak, Enums passed as Args and CancelEventArgs

Wednesday, May 12, 2004 3:05 AM by DotWind Blog

# Using Enum in drop downs in a webform

Wednesday, May 12, 2004 12:06 AM by Tim

# re: Enums and validation

How about using strings in an enum I made a post about that after looking at your post. http://blog.dotwind.com/archive/2004/05/12/150.aspx
do you thing there is a way to do what I am trying there?

thanks
Wednesday, May 12, 2004 3:21 AM by DotWind Blog

# Using Enum in drop downs in a webform

Wednesday, May 12, 2004 5:04 AM by nfactorial

# re: Enums and validation

Tim,
In application I'm working on required similar functionality. However I wrote an attribute that could be applied to enum members to provide a 'display name' for the entry.

ie.

public enum MyEnum
{
[ EnumDisplayAttribute( "Some Example" )]
SomeExample,
}

This is a bit more work than it looks like you wanted (have to provide a customized combo-box as well) but works well.

Alternately, you could fake it by having a class with public, const strings:

public sealed class Example
{
private Example() {}

public const string Example = "An Example";
}

n!
Wednesday, May 12, 2004 5:58 PM by Thomas Eyde

# re: Enums and validation

How do you verify combinations like Fold + Spindle?
Thursday, May 13, 2004 12:55 AM by Hugo Rodger-Brown

# re: Enums and validation

Thomas - you'd be looking to use bitwise logic to verify combinations of enums, for which you need to declare your attribute with the Flags attribute, see below for details:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemflagsattributeclasstopic.asp
Thursday, May 13, 2004 2:01 AM by Tim

# re: Enums and validation

thanks n!

I love the idea of the attribute, I will definetly try that.

the const strings class is the way I have been doing it. I just found it really easy to bind the enum to the combo box and also to store the values in the database instead of having a different table with the categories I will just put it as an enum and store the int value for it in the table.

Tim
Saturday, December 18, 2004 10:51 AM by MBA

# MBA

Helpful For MBA Fans.

# Actors and Actresses &raquo; Archive du blog &raquo; Eric Gunnerson&#8217;s C# Compendium : Enums and validation

# Dinner and a Movie &raquo; Eric Gunnerson&#8217;s C# Compendium : Enums and validation

New Comments to this post are disabled
 
Page view tracker