How to get next (or previous) enum value in C#
我有一个枚举,它的定义如下:
1 | public enum eRat { A = 0, B=3, C=5, D=8 }; |
所以给定值
我看到的解决方案是(不进行范围检查)
1 2 3 4 5 6 7 8 | 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): |
现在这太复杂了,不适合这么简单的事情。你知道更好的解决办法吗?像
谢谢
感谢大家的回答和反馈。我很惊讶能得到这么多。通过观察并运用其中的一些想法,我想出了一个最适合我的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 | 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]; } } |
这种方法的好处在于,它使用起来既简单又通用。实现为通用扩展方法,可以通过以下方式在任何枚举上调用它:
1 | return eRat.B.Next(); |
注意,我使用的是通用扩展方法,因此不需要在调用时指定类型,只需
可能有点过头了,但是:
1 2 3 | eRat value = eRat.B; eRat nextValue = Enum.GetValues(typeof(eRat)).Cast<eRat>() .SkipWhile(e => e != value).Skip(1).First(); |
或者如果你想要第一个数值更大的:
1 2 |
或者对于下一个更大的数字(自己排序):
1 2 | eRat nextValue = Enum.GetValues(typeof(eRat)).Cast<eRat>() .Where(e => (int)e > (int)value).OrderBy(e => e).First(); |
嘿,用Linq作为你的锤子,这个世界充满了钉子;-p
你真的需要概括这个问题吗?你能改成这样吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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; } } |
你要处理的问题是,你试图让一个枚举做一些它不应该做的事情。它们应该是类型安全的。允许将整数值赋给枚举,以便可以组合它们,但如果希望它们表示整数值,请使用类或结构。这里有一个可能的选择:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 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 } |
这允许您执行以下操作:
1 | int something = eRat.A + eRat.B; |
而这
1 2 3 4 5 6 | eRat.eRatValue current = eRat.A; while (current != null) { Console.WriteLine(current.Value); current = current.Next; } |
只有当您可以从它们的类型安全性中获益时,才应该使用枚举。如果您依赖于它们来表示类型,请切换到常量或类。
编辑
我建议你看一下关于枚举设计的msdn页面。第一个最佳实践是:
Do use an enumeration to strongly type
parameters, properties, and return
values that represent sets of values.
我尽量不去争论教条,所以我不会,但这是你要面对的问题。微软不希望你做你想做的事。他们明确要求你不要做你想做的事。使你很难做你想做的事。为了完成您正在尝试做的事情,您必须构建实用程序代码来强制它看起来工作正常。
您不止一次将解决方案称为优雅的,而且如果枚举是以不同的方式设计的,那么可能是这样,但是由于枚举是它们的本质,所以您的解决方案并不优雅。我认为室内乐很优雅,但是如果音乐家没有合适的乐器,不得不用锯片和罐子演奏维瓦尔第,不管他们作为音乐家有多能干,或者音乐在纸上有多好,它都将不再优雅。
因为在"d"之后没有关于返回内容的答案,所以一直到"c"为止。
【更新1】:根据Marc Gravell的建议进行更新。
BR/>【更新2】:根据Husayt的通缉方式进行更新,下一个值"d"返回"a"。
BR/>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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(); } } |
结果
Next enum of A = B
Next enum of B = C
Next enum of C = D
Next enum of D = A
从你的描述来看,你并不真的想要一个枚举。您将枚举扩展到其功能之外。为什么不创建一个自定义类,它将您需要的值作为属性公开,同时将它们保存在ordereddictionary中。那么,得到下一个/上一个是微不足道的。——更新
如果您想在基于上下文的集合上以不同的方式枚举,请将其作为设计的显式部分。封装类中的项,并有几个方法,每个方法返回IEnumerable,其中,t是您想要的类型。
例如
1 2 | IEnumerable<Foo> GetFoosByBar() IEnumerable<Foo> GetFoosByBaz() |
等。。。
您是否被一些您无法控制的东西锁定在使用枚举中?
如果你没有,我建议你用另一种方法,可能是
如果您创建一个
如果您必须使用枚举,我建议您使用其他方法:
1 |
只要按顺序添加值并保持同步,就可以大大简化检索下一个ERA的操作。
您可以简化它并将其概括为:
1 2 3 4 5 6 7 8 9 |
编辑:请注意,如果枚举包含重复值(同义项),那么给定这些值之一,此方法(或此处列出的任何其他技术)将失败。例如:
1 2 3 4 5 6 7 8 9 10 11 | enum BRUSHSTYLE{ SOLID = 0, HOLLOW = 1, NULL = 1, HATCHED = 2, PATTERN = 3, DIBPATTERN = 5, DIBPATTERNPT = 6, PATTERN8X8 = 7, DIBPATTERN8X8 = 8 } |
考虑到
Update: a generics version:
@勒皮:
通用版本允许意外传递非枚举值,该值只在运行时捕获。我最初把它写成了一个泛型,但是当编译器拒绝了
对于简单的解决方案,您可以从枚举中提取数组。
1 |
然后你可以列举
1 2 | foreach (eRat item in list) //Do something |
或查找下一项
1 2 | int index = Array.IndexOf<eRat>(list, eRat.B); eRat nextItem = list[index + 1]; |
每次需要下一个值时,存储数组比从枚举中提取要好。
但是如果你想要更漂亮的解决方案,那就创建这个类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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(); } } |
实例化
1 |
迭代
1 2 | foreach (eRat item in eRatEnum) //Do something |
移到下一行
1 2 3 | eRatEnum.Seek(eRat.B); eRatEnum.MoveNext(); eRat nextItem = eRatEnum.Current; |
希望我的这部分代码能帮助您:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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(); } |
我可以想到两件事:
- ERAT B+ 3
- 枚举分析(typeof(((int)erat.b)+3)
我用这个,非常适合我的。
16旧职位,但我有另一个解决办法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //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); } |
当你需要使用它的时候,就做一个演员表
1 2 | BootstrapThemeEnum theme = BootstrapThemeEnum.Info; var next = (BootstrapThemeEnum)theme.Next(); |
我的枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 } |
有一个非常简单的解决方案(如果可以更改整数值),专门设计用于处理数字。你的号码是一个
假设您的
1 2 3 4 5 6 7 8 9 10 11 | public enum ItemStatus { New = 0, Draft = 1, Received = 2, Review = 4, Rejected = 8, Approved = 16 } ItemStatus myStatus = ItemStatus.Draft; |
在
1 | myStatus = (ItemStatus)(((int)myStatus) << 1) |
mystatus的结果是:itemstatus.received。
您还可以通过将按位运算符从
1 | myStatus = (ItemStatus)(((int)myStatus) >> 1) |
mystatus的结果是:itemstatus.new。
您应该始终在两个方向上添加代码来测试"越界"情况。
您可以在下面了解关于位操作的更多信息:http://code.tutspus.com/articles/understanding-bitwise-operators--active-11301
var next=(erat)((int)somerat+3);
从评论中,我有很多问题,比如:"为什么你会想用这种方式使用枚举。"既然你们中有这么多人问过,让我给你我的用例,看看你是否同意:
我有一个固定的项目数组
1 2 3 4 5 6 7 8 9 10 11 | 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)]; } } |
打电话给
然后我得到所需的输出
1)行走(类型1)
1 2 3 4 | A = 1 B = 5 C = 78 D = 30 |
2)江户十一〔3〕
1 2 3 4 5 6 7 8 | 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。用字典怎么能做到这一点?
我认为这是一种相当优雅的枚举用法。
我用不同的枚举做了类似的事情。这是一个游戏,玩家有机会切换颜色。
1 2 3 4 5 6 7 8 9 | 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; } |
这个解决方案对我有效。
我在这里用这个:
1 2 3 4 5 | public MyEnum getNext() { return this.ordinal() < MyEnum.values().length - 1 ? MyEnum.values()[this.ordinal() + 1] : MyEnum.values()[0]; } |
我同意Sung Meister的回答,但还有一个选择:
1 2 3 4 5 6 7 8 9 10 | 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; } } |
注:假设的假设很多:)
Linq解决方案不会在最后一个元素上中断,但在默认情况下再次继续:
1 |
我尝试了第一个解决方案,但它对我不起作用。以下是我的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public object NextEnumItem(object currentEnumItem) { if (!currentEnumItem.GetType().IsEnum) throw new ArgumentException(String.Format("Argument is not an Enum")); Array Arr = Enum.GetValues(currentEnumItem.GetType()); int j = Array.IndexOf(Arr,currentEnumItem) + 1; return (Arr.Length == j) ? currentEnumItem : Arr.GetValue(j); } public object PreviousEnumItem(object currentEnumItem) { if (!currentEnumItem.GetType().IsEnum) throw new ArgumentException(String.Format("Argument is not an Enum")); Array Arr = Enum.GetValues(currentEnumItem.GetType()); int j = Array.IndexOf(Arr, currentEnumItem) - 1; return (j==-1) ? currentEnumItem : Arr.GetValue(j); } |
对我来说,这似乎是对枚举类的一种滥用——但这可以做到(假设对最后一个值调用next将导致环绕):
1 2 3 4 5 6 7 8 9 10 11 12 | 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; } } |
这将使您在相同的基础上得到先前的值:
1 2 3 4 5 6 7 8 9 10 11 12 | 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; } } |