tags:

views:

1351

answers:

4

I want to do the same as in this question, that is:

enum DaysOfTheWeek {Sunday=0, Monday, Tuesday...};
string[] message_array = new string[number_of_items_at_enum];

...

Console.Write(custom_array[(int)DaysOfTheWeek.Sunday]);

however, I would rather have something integral to so, rather than write this error prone code. Is there a built in module in C# that does just this?

+9  A: 

If the values of your enum items are contigious, the array method works pretty well. However, in any case, you could use Dictionary<DayOfTheWeek, string> (which is less performant, by the way).

Mehrdad Afshari
That would have a significant impact on performance? How come?
Spencer Ruport
@Spencer: Dictionary lookups are much slower than a direct array index (or list index). If you're doing this a lot, it could have a noticable impact on the perf.
Reed Copsey
yes, that is the solution I chose for MouseButton, as it is a flag enum (aka 0001 right, 0010 middle, 0100 left etc). Still, pretty ugly for such a simple thing.
Nefzen
@Spencer: I didn't said "significant." Whether that's significant or not depends on your specific usage. Is it slower? Yes, without doubt, a Dictionary is slower than direct array lookup. Is it significant? You should benchmark and see.
Mehrdad Afshari
The dictionary solution is preferrable if you can't prove that there's a significant impact. Preoptimizing to array creates two problems: First it abandons the type safety of enums, making it effectively no different (but more contrived) than constants defined in a static class. Second, you end up writing more code rather than less in order to get it to behave the way you want.
Michael Meadows
+1  A: 

You could make a class or struct that could do the work for you


public class Caster
{
    public enum DayOfWeek
    {
        Sunday = 0,
        Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday
    }

    public Caster() {}
    public Caster(string[] data) { this.Data = data; }

    public string this[DayOfWeek dow]{
        get { return this.Data[(int)dow]; }
    }

    public string[] Data { get; set; }


    public static implicit operator string[](Caster caster) { return caster.Data; }
    public static implicit operator Caster(string[] data) { return new Caster(data); }

}

class Program
{
    static void Main(string[] args)
    {
        Caster message_array = new string[7];
        Console.Write(message_array[Caster.DayOfWeek.Sunday]);
    }
}

EDIT

For lack of a better place to put this, I am posting a generic version of the Caster class below. Unfortunately, it relies on runtime checks to enforce TKey as an enum.

public enum DayOfWeek
{
    Weekend,
    Sunday = 0,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
}

public class TypeNotSupportedException : ApplicationException
{
    public TypeNotSupportedException(Type type)
        : base(string.Format("The type \"{0}\" is not supported in this context.", type.Name))
    {
    }
}

public class CannotBeIndexerException : ApplicationException
{
    public CannotBeIndexerException(Type enumUnderlyingType, Type indexerType)
        : base(
            string.Format("The base type of the enum (\"{0}\") cannot be safely cast to \"{1}\".",
                          enumUnderlyingType.Name, indexerType)
            )
    {
    }
}

public class Caster<TKey, TValue>
{
    private readonly Type baseEnumType;

    public Caster()
    {
        baseEnumType = typeof(TKey);
        if (!baseEnumType.IsEnum)
            throw new TypeNotSupportedException(baseEnumType);
    }

    public Caster(TValue[] data)
        : this()
    {
        Data = data;
    }

    public TValue this[TKey key]
    {
        get
        {
            var enumUnderlyingType = Enum.GetUnderlyingType(baseEnumType);
            var intType = typeof(int);
            if (!enumUnderlyingType.IsAssignableFrom(intType))
                throw new CannotBeIndexerException(enumUnderlyingType, intType);
            var index = (int) Enum.Parse(baseEnumType, key.ToString());
            return Data[index];
        }
    }

    public TValue[] Data { get; set; }


    public static implicit operator TValue[](Caster<TKey, TValue> caster)
    {
        return caster.Data;
    }

    public static implicit operator Caster<TKey, TValue>(TValue[] data)
    {
        return new Caster<TKey, TValue>(data);
    }
}

// declaring and using it.
Caster<DayOfWeek, string> messageArray =
    new[]
        {
            "Sunday",
            "Monday",
            "Tuesday",
            "Wednesday",
            "Thursday",
            "Friday",
            "Saturday"
        };
Console.WriteLine(messageArray[DayOfWeek.Sunday]);
Console.WriteLine(messageArray[DayOfWeek.Monday]);
Console.WriteLine(messageArray[DayOfWeek.Tuesday]);
Console.WriteLine(messageArray[DayOfWeek.Wednesday]);
Console.WriteLine(messageArray[DayOfWeek.Thursday]);
Console.WriteLine(messageArray[DayOfWeek.Friday]);
Console.WriteLine(messageArray[DayOfWeek.Saturday]);
Matthew Whited
+1, this, at least, encapsulates the pain of trying to shoehorn an enum into doing something it wasn't designed to (and shouldn't be used to) do.
Michael Meadows
Well, I would agree that something more sane should be done for the setters. But I would never blanket it as something that "shouldn't be used." Another option would be to create custom readonly structs that are used in a similar fashion.
Matthew Whited
@Matthew Whited, I am probably over-reacting when I say "shouldn't be used," but I personally have never seen, and can't imagine, a good justifiable reason for creating an enum only to serve as an indexer in an array. I can say confidently that, in this limited case, you loose all of the benefits of an enum, and gain nothing in return.
Michael Meadows
As an exercise, you might try to make your solution generic. I think it can be done, and would make it kick even more @$$.
Michael Meadows
Generics and Enums don't play very nice together. But I agree that would be very cool. If you used a class for the index instead you could use either an abstract base or an interface as the indexer type. But you would still have problems with the implicit conversions which would require a static factory method instead.
Matthew Whited
Yeah, you're right... You'd have to enforce the key as an enum at runtime. It's more code, but might still have use. Implicit casting should still work, though... I converted your code to generic for fun, but in reality, with all of the additional code, it probably performs more poorly than a dictionary. :(
Michael Meadows
You should post the code somewhere and link it here. I'd like to see your solution.
Matthew Whited
I didn't know where else to put it, so I edited it in to your answer.
Michael Meadows
That would be much nicer if Enums and Generics played better. It would be interesting to have generics work well with attributes and if interfaces such as INumeric existed in the current .Net (I know they can be faked with the DRL but that's just not the same)
Matthew Whited
A: 

Here you go:

string[] message_array = Enum.GetNames(typeof(DaysOfTheWeek));

If you really need the length, then just take the .Length on the result :) You can get values with:

string[] message_array = Enum.GetValues(typeof(DaysOfTheWeek));
van
A: 

You can always do some extra mapping to get an array index of an enum value in a consistent and defined way:

int ArrayIndexFromDaysOfTheWeekEnum(DaysOfWeek day)
{
   switch (day)
   {
     case DaysOfWeek.Sunday: return 0;
     case DaysOfWeek.Monday: return 1;
     ...
     default: throw ...;
   }
}

Be as specific as you can. One day someone will modify your enum and the code will fail because the enum's value was (mis)used as an array index.

VVS
in this case it would make sense just to specify values in the Enum definition itself.
van
@van, you're right about this case, but there's some merit to @David Humpohl's assertion that the code might eventually fail. In the case of DaysOfWeek, the chances are low, but enums based on business values could change causing the underlying values to shift.
Michael Meadows