tags:

views:

944

answers:

7

OK, so I have a database field of type char(1) that has a small number of possible state codes (e.g. 'F'= Failure, 'U'=Unknown, etc.). I'd like to have a C# enum class that corresponds to these states. I can do:

public enum StatusCode : byte {
    Unknown = (byte) 'U',
    Failure = (byte) 'F',
    // etc.
}

So far so good. But in the DataTable returned from the database, the column values are System.Data.SqlTypes.SqlString instances. There are obviously some issues converting from a C# string (or even a C# char) to a C# byte (since C# char is really a UTF-16 codepoint). But in this case I know the values are constrained to a small set, and the code should throw an exception if a value outside this set comes through.

With that in mind, what's the best way of doing this? Is it safe to cast from a SqlString to a byte? Would Convert.ToByte() be better? Would it be better to simply use a switch/case construct to crosswalk the values into the enum?

I'm looking for the "best" way to do this, not only in terms of getting the right results but also for code clarity. I suppose I could also just use some constants like

public const char UnknownStatus = 'U';
public const char FailureStatus = 'F';

But I'd rather use an enum if possible. Any thoughts?

Edit: To clarify what I want do do with this, I'm expecting to use these values frequently throughout my code. For example, I want to be able to do things like:

public void DoSomething(StatusCode currentStatus) {
    if(currentStatus == StatusCode.Failure) {
        throw new SomeException();
    }

    switch(currentStatus) {
        case StatusCode.Unknown:
            // do something
            break;
    }
}

And so forth. I particularly want to avoid things like:

public void DoSomething(char currentStatus) {
    if(currentStatus == 'F') {
        // do something
    }
}

Since in this case I'm using what amounts to "magic numbers" all over the place. In particular, this would make migrating to some other state-flagging system virtually impossible. Does that make sense?

A: 

If you're on .NET 2.0 and higher, you could implement this using a generic dictionary:

Dictionary<char,string> statusCode = new Dictionary<char,string>();
statusCode.Add('U', "Unknown");
statusCode.Add('F', "Failure");

or alternatively:

Dictionary<char,StatusCode> statusCode = new Dictionary<char,StatusCode>();
statusCode.Add('U', StatusCode.Unknown);
statusCode.Add('F', StatusCode.Failure);

and you could access the string representation for a given code like so:

string value = statusCode['A'];

or

StatusCode myCode = statusCode['A'];

and so on. You would have to fill that dictionary from the database values, or from some kind of a config file or something.

Marc

marc_s
A: 

Would something like this work for you?

public Enum StatusCode : int{
   [StringValue("U")]
   Unknown =0,
   [StringValue["F"]
   Failuer=1
}
cptScarlet
is your StringValue Attribute part of the .NET framework? A quick Google search turns up a couple classes by that name but nothing on MSDN
STW
http://weblogs.asp.net/stefansedich/archive/2008/03/12/enum-with-string-values-in-c.aspx has a complete implementation example.
JasonTrue
+1  A: 

Skip to edit Have you tried this (which doesn't work as you've checked and commented):

public enum StatusCode : char
{
    Failure = 'F',
    Unknown = 'U',
    ...
}

EDIT - correct solution
or this (maybe even try with a struct):

public sealed class StatusCode
{
    public static readonly char Failure = 'F';
    public static readonly char Unknown = 'U';
    ...
    public char Value { get; set; }
}

your code you provided would work like this:

public void DoSomething(StatusCode currentStatus) {
    if(currentStatus.Value == StatusCode.Failure) {
        throw new SomeException();
    }

    switch(currentStatus.Value) {
        case StatusCode.Unknown:
            // do something
            break;
    }
}

If you don't like to use Value property you can always implement implicit equality operator between StatusCode and char types. In that case, your code wouldn't change a bit.

Robert Koritnik
No good: compiler gives:Error 994 Type byte, sbyte, short, ushort, int, uint, long, or ulong expecte
Daniel Pryden
Hey now, no reason to bring profanity into this. ;-)
Judah Himango
^^ I almost missed that one! :D
Jagd
This only with byte instead of char as the base type; and (char)'F' etc.
Steve Gilham
A: 

If you have a table called StatusCode which includes an integer primary key then you could use that as your identifier as well as hook it into your logic. And in that case, the enum would be the best thing to use. Though i'm not sure if this is feasible for you.

Paul Sasik
+3  A: 

Maybe a "constant" object?

public sealed class StatusCode {
    private char value;

    public static readonly StatusCode Unknown = new StatusCode('U');
    public static readonly StatusCode Failure = new StatusCode('F');

    private StatusCode(char v) {
        value = v;
    }

    public override string ToString() {
        return value.ToString();
    }

}

Then, later in your code, you could use it like an enum: StatusCode.Unknown. You could also provide an internal method to 'parse' a received value into an object of StatusCode.

Fernando
you should also provide implicit equality to provide correct compare for certain values...
Robert Koritnik
That's right! :D
Fernando
OK, I think this is the route I'm going to go. Unfortunately, case statements still don't work as I'd like, but at least everything else does. (Just like the old-style Java enum pattern!)
Daniel Pryden
A: 

One option is to setup your enum with identical names to the values in your database, such as:

enum StatusCode
{
    /// <summary>Unknown</summary>
    U = 0,
    /// <summary>Failure</summary>
    F,
    /// <summary>Etc</summary>
    E
}

Then use a static method to convert char values to an enumerated value

    private StatusCode CharToEnum(string statusCodeChar)
    {
        foreach (FieldInfo fi in typeof(StatusCode).GetFields())
        {
            if (fi.Name == statusCodeChar) return (StatusCode)fi.GetValue(null);
        }

        return StatusCode.U;
    }
PaulG
A: 

Short and sweet my man.. Does everything you need it to. You shouldn't need to use enum because you don't need it to assign an internal value to your possible states, you already know the values to your states.

public sealed class StatusCode
{
    public const string Unknown= "U";
    public const string Failure= "F";
    public const string Success= "S";
}
Snives