views:

3287

answers:

14

I have got an enum which is defined like this:

public enum eRat { A = 0, B=3, C=5, D=8 };

So given value eRat.B, I want to get the next one which is eRat.C

The solution I see is (without range checking)

Array a = Enum.GetValues(typeof(eRat));
int i=0 ;
for (i = 0; i < a.GetLength(); i++)
{
       if (a.GetValue(i) == eRat.B)
            break;
}
return (eRat)a.GetValue(i+1):

Now that is too much of action, for something that simple. Do you know any better solution?? Something like eRat.B+1 or Enum.Next(Erat.B)??

Thanks

A: 

I can think of 2 things:

  • eRat.B+3
  • Enum.Parse(typeof(((int)eRat.B)+3)
PoweRoy
because public enum eRat { A = 0, B=3, C=5, D=8 };A =0, B=3 so the next one is a +3
PoweRoy
Yes, but solution should be universal. it should work for any enum. Would it work for A in our case?? I just want next enum member.
husayt
a->B is 0->3 so yes +3 would work ?
PoweRoy
but eRat.B+3 =6 <> 5=C ;-)))
husayt
indeed, i was apperently sleeping :P sorry
PoweRoy
A: 

var next = (eRat)((int)someRat + 3);

eglasius
Thanks, but not. we are looking for universal solution. it wouldn't work for A, for instance.
husayt
@husayt it works for A, (0+3) = B :) - to make it work completely just split it a bit and check it doesn't go above the last one.
eglasius
+5  A: 

Works up to "C" since there is no answer on what to return after "D".

[update1]: Updated according to Marc Gravell's suggestion.

[update2]: Updated according to how husayt's wanted - return "A" for the next value of "D".

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Next enum of A = {0}", eRatEnumHelper.GetNextEnumValueOf(eRat.A));
        Console.WriteLine("Next enum of B = {0}", eRatEnumHelper.GetNextEnumValueOf(eRat.B));
        Console.WriteLine("Next enum of C = {0}", eRatEnumHelper.GetNextEnumValueOf(eRat.C));
    }
}

public enum eRat { A = 0, B = 3, C = 5, D = 8 };

public class eRatEnumHelper
{
    public static eRat GetNextEnumValueOf(eRat value)
    {
        return (from eRat val in Enum.GetValues(typeof (eRat)) 
                where val > value 
                orderby val 
                select val).DefaultIfEmpty().First();
    }
}

Result

Next enum of A = B
Next enum of B = C
Next enum of C = D
Next enum of D = A

Sung Meister
btw; .Take(1).ToArray()[0] - could just be .First()
Marc Gravell
Wow.. possibilities are endless. Time to start learning how to use a LINQ hammer ;)
Sung Meister
+14  A: 

Probably a bit overkill, but:

eRat value = eRat.B;
eRat nextValue = Enum.GetValues(typeof(eRat)).Cast<eRat>()
        .SkipWhile(e => e != value).Skip(1).First();

or if you want the first that is numerically bigger:

eRat nextValue = Enum.GetValues(typeof(eRat)).Cast<eRat>()
        .First(e => (int)e > (int)value);

or for the next bigger numerically (doing the sort ourselves):

eRat nextValue = Enum.GetValues(typeof(eRat)).Cast<eRat>()
        .Where(e => (int)e > (int)value).OrderBy(e => e).First();

Hey, with LINQ as your hammer, the world is full of nails ;-p

Marc Gravell
+1 "with LINQ as your hammer" -> isn't hurting ourselves is the fastest way to learn? ;)
Sung Meister
As far as hammers go, it's a pretty good one.
Michael Meadows
Will this go bang if you're already at the last enum?
CAD bloke
+3  A: 

You could simplify it and generalize it some:

static Enum GetNextValue(Enum e){
    Array all = Enum.GetValues(e.GetType());
    int i = Array.IndexOf(all, e);
    if(i < 0)
        throw new InvalidEnumArgumentException();
    if(i == all.Length - 1)
        throw new ArgumentException("No more values", "e");
    return (Enum)all.GetValue(i + 1);
}

EDIT: Note that if your enum contains duplicate values (synonymous entries), then this (or any other technique listed here) will fail, given one of those values. For instance:

enum BRUSHSTYLE{
    SOLID         = 0,
    HOLLOW        = 1,
    NULL          = 1,
    HATCHED       = 2,
    PATTERN       = 3,
    DIBPATTERN    = 5,
    DIBPATTERNPT  = 6,
    PATTERN8X8    = 7,
    DIBPATTERN8X8 = 8
}

Given either BRUSHSTYLE.NULL or BRUSHSTYLE.HOLLOW, the return value would be BRUSHSTYLE.HOLLOW.

<leppie>

Update: a generics version:

static T GetNextValue<T>(T e)
{
  T[] all = (T[]) Enum.GetValues(typeof(T));
  int i = Array.IndexOf(all, e);
  if (i < 0)
    throw new InvalidEnumArgumentException();
  if (i == all.Length - 1)
    throw new ArgumentException("No more values", "e");
  return all[i + 1];
}

</leppie>

@leppie:

Your generic version allows one to accidentally pass a non-enum value, which will be caught only at run-time. I had originally written it as a generic, but when the compiler rejected where T : Enum, I took it out and realized that I wasn't gaining much from generics anyway. The only real drawback is that you have to cast the result back to your specific enum type.

P Daddy
Come on! Generic, without generics! Dont be lazy :)
leppie
I'm a little bored :)
leppie
+1 Haha, awesome! Generics version!
Sung Meister
@leppie: Please see comment within post.
P Daddy
You can add a check inside the method. OR you dont have to as it will blow up anyways :)
leppie
My point was that the non-generic version will catch your mistake at compile time. I always prefer catching errors at compile time over catching them at run time.
P Daddy
to make generic version more safe you can put "where T : struct"and then " if (!typeof(T).IsEnum) throw new ArgumentException(String.Format("Argumnent {0} is Not an Enum", typeof(T).FullName));" to enforce it further
husayt
@husayt: That's really not much better. That still leaves many common cases where a run-time check takes the place of a compile-time check. I like generics as much as the next guy (maybe more), but I really don't see why people seem so hell-bent on making this particular method generic.
P Daddy
A: 

I would go with Sung Meister's answer but here is an alternative:

MyEnum initial = MyEnum.B, next;

for (int i = ((int) initial) + 1, i < int.MaxValue; i++)
{
  if (Enum.IsDefined(typeof(MyEnum), (MyEnum) i))
  {
     next = (MyEnum) i;
     break;
  }
}

Note: many assumptions assumed :)

leppie
+8  A: 

Do you really need to generalize this problem? Can you just do this instead?

public void SomeMethod(MyEnum myEnum)
{
    MyEnum? nextMyEnum = myEnum.Next();

    if (nextMyEnum.HasValue)
    {
        ...
    }
}

public static MyEnum? Next(this MyEnum myEnum)
{
    switch (myEnum)
    {
        case MyEnum.A:
            return MyEnum.B;
        case MyEnum.B:
            return MyEnum.C;
        case MyEnum.C:
            return MyEnum.D;
        default:
            return null;
    }
}

HTH, Kent

Kent Boogaart
+1: I do say simple things work out well as well.
Sung Meister
+1 simplicity. build something that solves the immediate need and move on. refactor later if necessary.
Rex M
Would it help if we assume that this IS the subsequent refactoring?
Whatsit
+7  A: 

The problem you're dealing with is because you're trying to get an enum to do something it shouldn't. They're supposed to be type safe. Assigning integral values to an enum is allowed so that you can combine them, but if you want them to represent integral values, use classes or structs. Here's a possible alternative:

public static class eRat
{
    public static readonly eRatValue A;
    public static readonly eRatValue B;
    public static readonly eRatValue C;
    public static readonly eRatValue D;

    static eRat()
    {
        D = new eRatValue(8, null);
        C = new eRatValue(5, D);
        B = new eRatValue(3, C);
        A = new eRatValue(0, B);
    }

    #region Nested type: ERatValue
    public class eRatValue
    {
        private readonly eRatValue next;
        private readonly int value;

        public eRatValue(int value, eRatValue next)
        {
            this.value = value;
            this.next = next;
        }

        public int Value
        {
            get { return value; }
        }

        public eRatValue Next
        {
            get { return next; }
        }

        public static implicit operator int(eRatValue eRatValue)
        {
            return eRatValue.Value;
        }
    }
    #endregion
}

This allows you to do this:

int something = eRat.A + eRat.B;

and this

eRat.eRatValue current = eRat.A;
while (current != null)
{
    Console.WriteLine(current.Value);
    current = current.Next;
}

You really should only be using enums when you can benefit from their type safety. If you're relying on them to represent a type, switch to constants or to classes.

EDIT

I would suggest you take a look at the MSDN page on Enumeration Design. The first best practice is:

Do use an enumeration to strongly type parameters, properties, and return values that represent sets of values.

I try not to argue dogma, so I won't, but here's the problem you're going to face. Microsoft doesn't want you to do what you are trying to do. They explicitly ask you not to do what you are trying to do. The make it hard for you to do what you are trying to do. In order to accomplish what you are trying to do, you have to build utility code to force it to appear to work.

You have called your solution elegant more than once, and it might be if enums were designed in a different way, but since enums are what they are, your solution isn't elegant. I think that chamber music is elegant, but if the musicians didn't have the proper instruments and had to play Vivaldi with sawblades and jugs, it would no longer be elegant, regardless of how capable they were as musicians, or how good the music was on paper.

Michael Meadows
+1 for winning. :)
Greg D
+1 for good advice
Sung Meister
Looking at this, I wish I could give it another +1. It seems so obvious looking at it, but I don't think I've ever really done such a thing.
Greg D
Please, check my Answer where I explain why I need an enum. If you can see a solution working in that context I would be more than happy to accept it.
husayt
+2  A: 

Are you locked into using an enum by something that you have no control over?

If you're not, I'd suggest using an alternative, probably Dictionary<string, int> rat;

If you create a Dictionary and you populate it with your data, enumerating over it is somewhat simpler. Also, it's a clearer mapping of intent-- you're mapping numbers to strings with this enum and you're trying to leverage that mapping.

If you must use the enum, I'd suggest something else:

var rats = new List<eRat>() {eRat.A, eRat.B, eRat.C, eRat.D};

As long as you're adding the values in-order and you keep it in sync, you greatly simplify the act of retrieving the next eRat.

Greg D
+3  A: 

Judging from your description, you don't really want an enum. You're stretching enum beyond its capabilities. Why not create a custom class that exposes the values you need as properties, while keeping them in OrderedDictionary. Then getting a next/previous one would be trivial. --update

If you want to enumerate differently on the collection based in the context, make that explicit part of your design. Encapsulate the items within a class, and have few methods each returning IEnumerable where, T is your desired type.

For example

IEnumerable<Foo> GetFoosByBar()
IEnumerable<Foo> GetFoosByBaz()

etc...

Krzysztof Koźmic
Good suggestion - rather than overload the enum.
Gordon Mackie JoanMiro
I don't get it. **Why** would you want to foreach on an enum? Can you give some more context here?
Krzysztof Koźmic
not on an enum itself. but to be able to enumerate through array members differently. I use enums as fixed indexers.
husayt
check my answer, for more detailed explanation. Thanks.
husayt
A: 

Seems like an abuse of the enum class to me - but this would do it (assuming that calling Next on the last value would cause wrap-around):

public static eRat Next(this eRat target)
{
    var nextValueQuery = Enum.GetValues(typeof(eRat)).Cast<eRat>().SkipWhile(e => e != target).Skip(1);
    if (nextValueQuery.Count() != 0)
    {
        return (eRat)nextValueQuery.First();
    }
    else
    {
        return eRat.A;
    }
}

And this would give you the previous value on the same basis:

public static eRat Previous(this eRat target)
{
    var nextValueQuery = Enum.GetValues(typeof(eRat)).Cast<eRat>().Reverse().SkipWhile(e => e != target).Skip(1);
    if (nextValueQuery.Count() != 0)
    {
        return (eRat)nextValueQuery.First();
    }
    else
    {
        return eRat.D;
    }
}
Gordon Mackie JoanMiro
+1  A: 

For simple solution, you might just extract array from enum.

eRat[] list = (eRat[])Enum.GetValues(typeof(eRat));

Then you can enumerate

foreach (eRat item in list)
    //Do something

Or find next item

int index = Array.IndexOf<eRat>(list, eRat.B);
eRat nextItem = list[index + 1];

Storing the array is better than extracting from enum each time you want next value.

But if you want more beautiful solution, create the class.

public class EnumEnumerator<T> : IEnumerator<T>, IEnumerable<T> {
    int _index;
    T[] _list;

    public EnumEnumerator() {
        if (!typeof(T).IsEnum)
            throw new NotSupportedException();
        _list = (T[])Enum.GetValues(typeof(T));
    }
    public T Current {
        get { return _list[_index]; }
    }
    public bool MoveNext() {
        if (_index + 1 >= _list.Length)
            return false;
        _index++;
        return true;
    }
    public bool MovePrevious() {
        if (_index <= 0)
            return false;
        _index--;
        return true;
    }
    public bool Seek(T item) {
        int i = Array.IndexOf<T>(_list, item);
        if (i >= 0) {
            _index = i;
            return true;
        } else
            return false;
    }
    public void Reset() {
        _index = 0;
    }
    public IEnumerator<T> GetEnumerator() {
        return ((IEnumerable<T>)_list).GetEnumerator();
    }
    void IDisposable.Dispose() { }
    object System.Collections.IEnumerator.Current {
        get { return Current; }
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
        return _list.GetEnumerator();
    }
}

Instantiate

var eRatEnum = new EnumEnumerator<eRat>();

Iterate

foreach (eRat item in eRatEnum)
    //Do something

MoveNext

eRatEnum.Seek(eRat.B);
eRatEnum.MoveNext();
eRat nextItem = eRatEnum.Current;
chaowman
A: 

Thanks to everybody for your answers and feedback. I was surprised to get so many of them. Looking at them and using some of the ideas, I came up with this solution, which works best for me:

public static class Extensions {

    public static T Next<T>(this T src) where T : struct
    {
        if (!typeof(T).IsEnum) throw new ArgumentException(String.Format("Argumnent {0} is Not an Enum",typeof(T).FullName));

        T[] Arr = (T[])Enum.GetValues(src.GetType());
        int j = Array.IndexOf<T>(Arr,src)+1;
        return (Arr.Length==j)?Arr[0]:Arr[j];            
    }
}

The beaty of this approach, that it is simple and universal to use. Implemented as generic extension method, you can call it on any enum this way:

return eRat.B.Next();

Notice, I am using generalized extension method, thus I don't need to specify type upon call, just .Next().

Thanks again.

husayt
A: 

From comments I had many question like: "Why would you ever want to use enum in this way." Since so many of you asked, let me give you my use case and see if you agree then:

I have a fixed array of items int[n]. Depending on the situation I want to enumerate through this array differently. So i defined:

int[] Arr= {1,2,34,5,6,78,9,90,30};
enum eRat1 { A = 0, B=3, C=5, D=8 }; 
enum eRat2 { A, AA,AAA,B,BB,C,C,CC,D }; 

void walk(Type enumType) 
{ 
   foreach (Type t in Enum.GetValues(enumType)) 
   { 
      write(t.ToString() + " = " + Arr[(int)t)]; 
   }
}

and call walk(typeof(eRAt1)) or walk(typeof(eRAt2))

then i get required output

1) walk(typeof(eRAt1))

A = 1
B = 5
C = 78
D = 30

2) walk(typeof(eRAt2))

A = 1
AA = 2
AAA = 34
B = 5
BB = 6
C = 78
CC = 90
D = 30

This is very simplified. But i hope, this explains. There are some other advantages to this, as having enum.toString(). So basically i use enums as indexers.

So using the solution I can do something like this now.

In sequence eRat1 next value to B is C, but in eRat2 it is BB. So depending on which sequence I am interested in, I can do e.next and depending on enumType I will either get C or BB. How would one achieve that with dictionaries?

I think this a rather elegant use of enums.

husayt
They problem you're going to face (I argue this from the point of practicality, not dogma) is that you're trying to force enums do something that they are specifically designed to not do (represent an int). It may feel elegant, but you'll find that the workaround discussed here won't be your last.
Michael Meadows
I use Enums for enumerating (That is what their name suggest anyway). I use them as indexers, not just as a mere integers.Ok. Imagine chess board :A,B,C,..,E,F,G,HOne figure moves A->C->D->FAnother B->C->E->HThird C->A->B->Gnow enums would most elegant way to describe all these movements
husayt
I got what you were saying, but enums are not for enumerating, even though the names look similar. Enums are for representing values free of type (even though you can define an underlying type). I'll update my answer to further exposit
Michael Meadows
Anyway, how would the solution would look, without enums??
husayt
Use good old OOP. You're really almost there. You can design classes to: implicitly cast to any type, enumerate in lists, chain together values, and have behavior. You can't do any of these without friction if you use enums.
Michael Meadows
@husayt: Arrays are indexed with integers. A set of array index values should be an integer array, not an enum. Without enums the solution has less casting and passes a constant array of index values in order to do a 'walk' instead of passing a type parameter. Did not The Beatles say it first? 'All you need is int'! ... Well, maybe they didn't say that.
Task