tags:

views:

181

answers:

7

In C#, can you use number ranges in enum types, for example

public enum BookType
{
    Novel = 1,
    Journal = 2,
    Reference = 3,
    TextBook = 4 .. 10
}

EDIT: The reason this is needed is to cast from a number to the enum type, eg:

int iBook = 5
BookType btBook = (BookType)ibook
Debug.Print "Book " + ibook + " is a " btBook

and the expected output is: Book 5 is a TextBook

+1  A: 

No, it's not. If the numeric constants you're trying to map to truly have the same meaning, you would still need a separate member for each numeric constant. Like TextBook4, TextBook5, etc.

Josh Einstein
+7  A: 

According to the C# standard (p612, The C# Programming Language) the value given to an enumeration must be a constant integer (or any similar type - long, byte, sbyte, short, etc), so a range of values isn't valid.

My compiler (VS2008) agrees with the spec.

Since you can't repeat names within an enumeration, the closest you'll get is something like this:

public enum BookType
{
    Novel = 1,
    Journal = 2,
    Reference = 3,
    TextBook4 = 4,
    TextBook5 = 5, ...
    TextBook10 = 10
}

Which is actually pretty ugly. Perhaps an enum is not the solution to your particular problem ...

Bevan
As does logic. The whole point of an enum is to replace magic numbers in your code with pieces of (hopefully) descriptive text. If ranges were allowed, how would the compiler know which magic number to replace your enum text with?
MusiGenesis
It is not nessasary to be integers. you can use the base-type option to declare enum whose members are of type long, byte,sbyte,short,ushort,uint,ulong etc
SysAdmin
@SysAdmin - good point. I used integer in a generic sense, rather than in32 etc. I'll amend the answer to be more precise.
Bevan
A: 

Perhaps you meant to ask a related question, which is: can you have more than one enumeration (not sure I have the terminology correct, but my example will show what I mean) for each value?

In C#, you can do something like this:

public enum BookType
{
    Novel = 1,
    Journal = 2,
    Magazine = 2,
    Reference = 3,
    TextBook = 4,
    LabWorkbook = 4,
    Dictionary = 5,
    Encyclopedia = 5
}

This way, you can use either BookType.Journal or BookType.Magazine in your code, either of which is synonymous with the value 2. Whether this should be done is another matter - I'm not familiar with the arguments for or against this (I'd like to say "if C# allows it, it must be OK", but that would be utterly crazy).

MusiGenesis
+3  A: 

As others have said, no it isn't possible. It is possible to combine enum values if they are flags:

[Flags]
public enum BookType
{
    Novel,
    Journal,
    Reference,
    TextBook1,
    TextBook2,
    TextBook3,
    TextBook4,
    TextBook5,
    TextBooks1To5 = TextBook1 | TextBook2 | TextBook3 | TextBook4 | TextBook5
}
Gabe Moothart
+1  A: 

You can opt for a dictionary rather.

var BookType = new Dictionary<int, string>();
BookType.Add(1, "Novel");
BookType.Add(2, "Journal");
BookType.Add(3, "Reference");
BookType.Add(4, "TextBook");
BookType.Add(5, "TextBook");
BookType.Add(6, "TextBook");
BookType.Add(7, "TextBook");
BookType.Add(8, "TextBook");
BookType.Add(9, "TextBook");
BookType.Add(10, "TextBook");

int iBook = 5 
Debug.Print "Book " + iBook + " is a " BookType[iBook]

Edit: You can also declare your dictionary readonly if it's in class level.

You can use an enum as the Dictionary value instead of string.

Veer
+1  A: 

Somewhat offtopic, you can simulate enums using normal classes with readonly fields.

e.g. something similair to this would solve your problem:

public sealed class BookType
{
    public static readonly BookType Novel = new BookType(1, 1, "Novel");
    public static readonly BookType Journal = new BookType(2, 2, "Journal");
    public static readonly BookType Reference = new BookType(3, 3, "Reference");
    public static readonly BookType Textbook = new BookType(4, 10, "Textbook");

    public int Low { get; private set; }
    public int High { get; private set; }
    private string name;

    private static class BookTypeLookup
    {
        public static readonly Dictionary<int, BookType> lookup = new Dictionary<int, BookType>();
    }

    private BookType(int low, int high, string name)
    {

        this.Low = low;
        this.High = high;
        this.name = name;

        for (int i = low; i <= high; i++)
            BookTypeLookup.lookup.Add(i, this);
    }

    public override string ToString()
    {
        return name;
    }

    public static implicit operator BookType(int value)
    {
        BookType result = null;
        if (BookTypeLookup.lookup.TryGetValue(value, out result))
            return result;

        throw new ArgumentOutOfRangeException("BookType not found");
    }
}

It's quite a bit more verbose than a normal enum, but it does allow you to define ranged members in an enum like manner.

e.g.

 var bookType = (BookType)5;
 Console.WriteLine(bookType);
Roger Alsing
+1  A: 

If you can assign the values to enum-strings yourself then you can use some bitmagic to map multiple int values to same enum value. Subtypes could be enums themselves for every BookType (NovelTypes, JournalTypes, etc).

On the downside

  • it does require some value modification when casting to BookType
  • every subtype range is of the same size (16 in current example.
  • it is a bit less readable than simple Novel = 3 kind of mapping.

Example code:

class Program
{
    /// <summary> Number of subtypes reserved for each BookType. </summary>
    private const byte BookTypeStep = 16;
    /// <summary> Bitmask to use to extract BookType from a byte. </summary>
    private const byte BookTypeExtractor = Byte.MaxValue - BookTypeStep + 1;
    /// <summary> Bitmask to use to extract Book subtype from a byte. </summary>
    private const byte BookSubTypeExtractor = BookTypeStep -1;

    public enum BookType : byte
    {
        Unknown = 0,
        Novel = BookTypeStep * 1,
        Journal = BookTypeStep * 2,
        Reference = BookTypeStep * 3,
        TextBook = BookTypeStep * 4,
    }

    static void Main(string[] args)
    {
        for(int i = 16; i < 80; i++)
        {
            Console.WriteLine("{0}\tof type {1} ({2}),\tsubtype nr {3}",
                i,
                i & BookTypeExtractor,
                (BookType)(i & BookTypeExtractor),
                i & BookSubTypeExtractor
            );
        }
        Console.ReadLine();
    }
}

This example has ranges 16-31 for Novels, 32-47 for journals, etc.

ImreP