如何在C#中获取下一个(或以前的)枚举值

时间:2009-03-13 12:52:59

标签: c# .net

我有一个枚举,其定义如下:

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

因此给定值eRat.B,我希望得到下一个eRat.C

我看到的解决方案是(没有范围检查)

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):

现在这太复杂了,对于那些简单的事情。你知道更好的解决方案吗?像eRat.B+1Enum.Next(Erat.B)

之类的东西

由于

24 个答案:

答案 0 :(得分:55)

感谢大家的回答和反馈。得到这么多人我感到很惊讶。看着他们并使用了一些想法,我提出了这个最适合我的解决方案:

public static class Extensions
{

    public static T Next<T>(this T src) where T : struct
    {
        if (!typeof(T).IsEnum) throw new ArgumentException(String.Format("Argument {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];            
    }
}

这种方法的优点在于它使用简单且通用。实现为通用扩展方法,您可以通过以下方式在任何枚举上调用它:

return eRat.B.Next();

注意,我使用的是通用扩展方法,因此我不需要在调用时指定类型,只需要.Next()

答案 1 :(得分:36)

可能有点矫枉过正,但是:

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

或者如果你想要数字上更大的第一个:

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

或者下一个更大的数字(我们自己做):

eRat nextValue = Enum.GetValues(typeof(eRat)).Cast<eRat>()
        .Where(e => (int)e > (int)value).OrderBy(e => e).First();
嘿,以LINQ为锤子,这个世界充满了指甲;-p

答案 2 :(得分:21)

真的需要概括这个问题吗?你可以这样做吗?

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;
    }
}

答案 3 :(得分:12)

你正在处理的问题是因为你试图让枚举做一些不应该做的事情。它们应该是类型安全的。允许为枚举分配整数值,以便您可以组合它们,但如果您希望它们表示整数值,请使用类或结构。这是一个可能的选择:

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
}

这允许您这样做:

int something = eRat.A + eRat.B;

和这个

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

当你可以从类型安全中受益时,你真的应该只使用枚举。如果您依赖它们来表示类型,请切换到常量或类。

修改

我建议您查看Enumeration Design上的MSDN页面。第一个最佳实践是:

  

使用枚举来强类型化   参数,属性和返回   表示值集的值。

我尽量不去争论教条,所以我不会,但这就是你要面对的问题。 Microsoft不希望您执行您要执行的操作。他们明确要求你不要做你想做的事。这使你很难做你想做的事情。为了完成您要做的事情,您必须构建实用程序代码以强制它看起来有效。

您已多次调用优雅解决方案,如果枚举是以不同的方式设计的,但由于枚举就是它们的原因,因此您的解决方案并不优雅。我认为室内音乐很优雅,但是如果音乐家没有合适的乐器并且不得不用锯片和水壶演奏Vivaldi,那么它将不再优雅,无论他们作为音乐家的能力如何,或音乐有多好在纸上。

答案 4 :(得分:10)

由于“D”之后无法回答什么,因此无法回答“C”。

[update1] :根据Marc Gravell的建议更新。

[update2] :根据husayt的需要更新 - 为“D”的下一个值返回“A”。

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();
    }
}

<强>结果

  

A = B的下一个枚举   下一个枚举B = C
  下一个C = D的枚举   下一个枚举D = A

答案 5 :(得分:4)

您是否因无法控制的内容而无法使用枚举?

如果你不是,我建议使用替代方案,可能是Dictionary<string, int> rat;

如果你创建一个Dictionary并用数据填充它,那么枚举它就会更简单一些。此外,它是一个更清晰的意图映射 - 您使用此枚举将数字映射到字符串,并且您正在尝试利用该映射。

如果你必须使用枚举,我会建议其他的东西:

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

只要您按顺序添加值并保持同步,就可以大大简化检索下一个eRat的行为。

答案 6 :(得分:4)

根据您的描述判断,您并非真的想要枚举。你的内容超出了它的能力范围。为什么不创建一个自定义类来公开您需要的值作为属性,同时将它们保存在OrderedDictionary中。 然后获得下一个/上一个将是微不足道的。 --update

如果要根据上下文对集合进行不同的枚举,请将其设计为显式部分。 封装一个类中的项目,并且每个返回IEnumerable的方法很少,其中T是您想要的类型。

例如

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

等...

答案 7 :(得分:3)

您可以简化它并将其概括为:

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);
}

修改: 请注意,如果您的枚举包含重复值(同义词条目),则在给定其中一个值的情况下,此(或此处列出的任何其他技术)将失败。例如:

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

根据BRUSHSTYLE.NULLBRUSHSTYLE.HOLLOW,返回值为BRUSHSTYLE.HOLLOW

&LT; leppie&GT;

  

更新:泛型版本:

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];
}

&LT; / leppie&GT;

<强> @leppie

您的通用版本允许用户意外传递非枚举值,该值仅在运行时捕获。我最初把它写成通用的,但是当编译器拒绝where T : Enum时,我把它拿出来并意识到我无论如何都没有从泛型中获得太多。唯一真正的缺点是你必须将结果强制转换回你的特定枚举类型。

答案 8 :(得分:3)

对于简单的解决方案,您可能只是从枚举中提取数组。

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

然后你可以枚举

foreach (eRat item in list)
    //Do something

或找到下一个项目

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

存储数组比每次想要下一个值时从枚举中提取更好。

但如果你想要更漂亮的解决方案,请创建课程。

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();
    }
}

实例化

var eRatEnum = new EnumEnumerator<eRat>();

迭代

foreach (eRat item in eRatEnum)
    //Do something

MoveNext的

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

答案 9 :(得分:2)

希望我的代码中的这部分可以帮助您:

public enum EGroupedBy
{
    Type,
    InterfaceAndType,
    Alpha,
    _max
}

private void _btnViewUnit_Click(object sender, EventArgs e)
{
    int i = (int)GroupedBy;

    i = (i + 1) % (int)EGroupedBy._max;

    GroupedBy = (EGroupedBy) i;

    RefreshUnit();
}

答案 10 :(得分:1)

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

答案 11 :(得分:1)

有一个非常简单的解决方案(如果你可以改变你的整数值)专门设计用于处理数字。您的号码是enum的事实不是问题。它仍然是integer(或您指定的任何基础数字类型)。 Enum只会增加演员要求的复杂性。

假设您的enum定义如下:

 public enum ItemStatus
    {
        New = 0,
        Draft = 1,
        Received = 2,
        Review = 4,
        Rejected = 8,
        Approved = 16
    }

ItemStatus myStatus = ItemStatus.Draft;

Enum使用按位运算。例如:

myStatus = (ItemStatus)(((int)myStatus) << 1)

myStatus的结果是: ItemStatus.Received

您也可以通过将按位运算符从Enum更改为<<来向下向后移动>>

myStatus = (ItemStatus)(((int)myStatus) >> 1)

myStatus的结果是: ItemStatus.New

您应该始终添加代码以测试两个方向的“越界”情况。

您可以在此处了解有关按位操作的更多信息:http://code.tutsplus.com/articles/understanding-bitwise-operators--active-11301

答案 12 :(得分:1)

旧帖子,但我有另一种解决方案

//Next with looping    
public static Enum Next(this Enum input)
{
    Array Arr = Enum.GetValues(input.GetType());
    int j = Array.IndexOf(Arr, input) + 1;
    return (Arr.Length == j) ? (Enum)Arr.GetValue(0) : (Enum)Arr.GetValue(j);
}

//Previous with looping
public static Enum Prev(this Enum input)
{
   Array Arr = Enum.GetValues(input.GetType());
   int j = Array.IndexOf(Arr, input) - 1;
   return (j == -1) ? (Enum)Arr.GetValue(Arr.Length -1) : (Enum)Arr.GetValue(j);
}

当你需要使用它时,只需做一个演员

BootstrapThemeEnum theme = BootstrapThemeEnum.Info;
var next = (BootstrapThemeEnum)theme.Next();

我的枚举

public enum BootstrapThemeEnum
{
    [Description("white")]
    White = 0,
    [Description("default")]
    Default = 1,
    [Description("info")]
    Info = 2,
    [Description("primary")]
    Primary = 3,
    [Description("success")]
    Success = 4,
    [Description("warning")]
    Warning = 5,
    [Description("danger")]
    Danger = 6,
    [Description("inverse")]
    Inverse = 7

}

答案 13 :(得分:1)

我正在使用它,非常适合我。

    //===================================================================================
// NEXT VALUE IN ENUM 
// ex: E_CamModes eNew =  kGlobalsVars.eGetNextValue< E_CamModes >( geCmMode );
public static T eGetNextValue< T >( T eIn ){
    T[] aiAllValues = ( T[] ) Enum.GetValues( typeof( T ));
    int iVal = System.Array.IndexOf( aiAllValues, eIn );
    return aiAllValues[ ( iVal + 1 ) % aiAllValues.Length ];
}

答案 14 :(得分:1)

我能想到两件事:

  • eRat.B + 3
  • Enum.Parse(typeof运算(((int)的eRat.B)3)

答案 15 :(得分:0)

从评论中我有很多问题:“为什么你会想以这种方式使用枚举。”你们这么多人问过,让我告诉你我的用例,看看你是否同意:

我有一个固定的项目数组int[n]。根据情况,我想通过这个数组枚举不同的方式。所以我定义了:

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)]; 
   }
} 

并致电walk(typeof(eRAt1))walk(typeof(eRAt2))

然后我得到所需的输出

1)步行(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

这非常简化。但我希望,这可以解释。这有一些其他优点,如enum.toString()。所以基本上我使用枚举作为索引器。

所以使用解决方案我现在可以做这样的事情。

顺序eRat1 B的下一个值是C,但在eRat2中它是BB。 所以根据我感兴趣的顺序,我可以做e.next并根据enumType我会得到C或BB。如何通过字典实现这一目标?

我认为这是对枚举的一种相当优雅的用法。

答案 16 :(得分:0)

似乎是对我滥用枚举类 - 但这样做(假设在最后一个值上调用Next会导致环绕):

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;
    }
}

这会在相同的基础上给你以前的值:

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;
    }
}

答案 17 :(得分:0)

我在这里使用它:

public MyEnum getNext() {
    return this.ordinal() < MyEnum.values().length - 1 ? 
                            MyEnum.values()[this.ordinal() + 1] : 
                            MyEnum.values()[0];
}

答案 18 :(得分:0)

我会选择Sung Meister的答案,但这里有另一种选择:

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;
  }
}

注意:假设有许多假设:)

答案 19 :(得分:0)

LINQ解决方案不会在最后一个元素上中断,但会再次以默认值继续:

var nextValue = Enum.GetValues(typeof(EnumT)).Cast<EnumT>().Concat(new[]{default(EnumT)}).SkipWhile(_ => _ != value).Skip(1).First();

答案 20 :(得分:0)

我尝试了第一个解决方案,但是它对我不起作用。以下是我的解决方案:

NO

答案 21 :(得分:0)

我对另一个枚举做了类似的操作。这是一款游戏,玩家可以切换颜色。

public enum PlayerColor {
    Red = 0, Green, Blue, Cyan, Yellow, Orange, Purple, Magenta
}

public PlayerColor GetNextFreeColor(PlayerColor oldColor) {

    PlayerColor newColor = (PlayerColor)((int)(oldColor + 1) % 8);
    return newColor;
}

此解决方案对我有用。

答案 22 :(得分:0)

感谢大家的启发和解决方案。

这是我的结果,作为扩展。

public static class Enums
{
    public static T Next<T>(this T v) where T : struct
    {
        return Enum.GetValues(v.GetType()).Cast<T>().Concat(new[] { default(T) }).SkipWhile(e => !v.Equals(e)).Skip(1).First();
    }

    public static T Previous<T>(this T v) where T : struct
    {
        return Enum.GetValues(v.GetType()).Cast<T>().Concat(new[] { default(T) }).Reverse().SkipWhile(e => !v.Equals(e)).Skip(1).First();
    }
}

使用:

public enum Test { F1, F2, F3 }

public class Program
{
    public static void Main()
    {
        Test t = Test.F3;   

        Console.WriteLine(t);
        Console.WriteLine(t.Next());
        Console.WriteLine(t.Previous());

        Console.WriteLine("\n");

        t = Test.F1;    
        Console.WriteLine(t);
        Console.WriteLine(t.Next());
        Console.WriteLine(t.Previous());
    }
}

结果:

F3
F1
F2

F1
F2
F3

答案 23 :(得分:0)

您可以在枚举中添加和删除整数以获得下一个值。唯一的问题是 enum 上的整数运算将不会检查 enum 本身的有效性,因此可以设置“无效”值。

但是您可以结合使用 ++ enum Enum.IsDefined(),以获得获取枚举的下一个和上一个值的简单方法。由于整数值不是连续的,所以这在您的情况下效率不高,但是如果您具有连续的整数,那么它会很好地工作,并且可以检查 ++ enum 何时超出范围。检查下一个示例。

 public enum level
    {
        a = 0,
        b = 1,
        c = 2,
        d = 3,
        e = 4
    }

private static void Main(string[] args)
    {
        var levelValue = level.a;
        Console.WriteLine(levelValue);
        ++levelValue;
        Console.WriteLine(levelValue);
        ++levelValue;
        Console.WriteLine(levelValue);
        ++levelValue;
        Console.WriteLine(levelValue);
        ++levelValue;
        Console.WriteLine(levelValue);
        ++levelValue;
        Console.WriteLine(Enum.IsDefined(typeof(Program.level), levelValue));
        Console.WriteLine(levelValue);
        --levelValue;
        Console.WriteLine(levelValue);
        --levelValue;
        Console.WriteLine(levelValue);
        --levelValue;
        Console.WriteLine(levelValue);
        --levelValue;
        Console.WriteLine(levelValue);
        --levelValue;
        Console.WriteLine(Enum.IsDefined(typeof(Program.level), levelValue));
        Console.WriteLine(levelValue);
        --levelValue;
        Console.WriteLine(Enum.IsDefined(typeof(Program.level), levelValue));
        Console.WriteLine(levelValue);
    }

此输出为:

a
b
c
d
e
False
5
e
d
c
b
True
a
False
-1