如何获得在C#中的下一个(或以前)枚举值
我有一个像这样定义的枚举:
public enum eRat { A = 0, B=3, C=5, D=8 };
所以给定的价值eRat.B
,我想要得到下一个是eRat.C
我看到的解决scheme是(没有范围检查)
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):
现在这太复杂了,为了简单。 你知道更好的解决scheme吗? 像eRat.B+1
或Enum.Next(Erat.B)
?
谢谢
可能有点矫枉过正,但是:
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);
或下一个更大的数字(做自己的sorting):
eRat nextValue = Enum.GetValues(typeof(eRat)).Cast<eRat>() .Where(e => (int)e > (int)value).OrderBy(e => e).First();
嘿,用LINQ作为你的锤子,世界充满了钉子;-p
感谢大家的回答和反馈。 我很惊讶得到这么多。 看着他们,并使用一些想法,我想出了这个解决scheme,最适合我:
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]; } }
这种方法很简单,使用起来简单而通用。 作为通用的扩展方法实现,你可以在任何枚举上这样调用它:
return eRat.B.Next();
注意,我使用的是通用扩展方法,因此我不需要在调用时指定types,只需.Next()
。
再次感谢。
你真的需要概括这个问题吗? 你可以这样做吗?
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; } }
你正在处理的问题是因为你试图让一个枚举来做它不应该做的事情。 他们应该是安全的。 允许将枚举值赋给一个枚举值,这样就可以将它们组合起来,但是如果你想让它们代表整数值,可以使用类或结构体。 这是一个可能的select:
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; }
当你可以从他们的types安全中受益时,你应该只使用枚举。 如果你依靠它们来代表一个types,切换到常量或类。
编辑
我build议你看一下枚举devise的MSDN页面。 第一个最佳实践是:
请使用枚举来强制键入表示值集合的参数,属性和返回值。
我尽量不去争论教条,所以我不会,但是这是你要面对的问题。 微软不希望你做你想做的事情。 他们明确地要求你不要做你想做的事情。 使你难以做你正在做的事情。 为了完成你想要做的事情,你必须build立实用程序代码来强制它看起来工作。
你不止一次地调用过你的解决scheme,这可能是因为枚举是以不同的方式devise的,但是因为枚举是他们的,所以你的解决scheme不是很优雅。 我认为室内音乐是优雅的,但如果音乐家没有合适的乐器,必须用锯片和水壶来演奏维瓦尔第,那么不pipe他们作为音乐家的能力如何,或者音乐有多棒在纸上。
工作到“C”,因为在“D”之后没有回复什么。
[update1] :根据Marc Gravell的build议进行更新。
[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
从你的描述来看,你并不是真的想要枚举。 你正在超越它的能力延伸枚举。 为什么不创build一个自定义的类,把你需要的值作为属性公开,同时把它们保留在OrderedDictionary中。 然后获得下一个/上一个将是微不足道的。 –update
如果您想要根据上下文对集合进行不同枚举,请明确指出您的devise。 封装在一个类中的项目,并有几个方法每个返回IEnumerable在哪里,T是你想要的types。
例如
IEnumerable<Foo> GetFoosByBar() IEnumerable<Foo> GetFoosByBaz()
等等…
你是否用某种你无法控制的东西来使用枚举?
如果你不是,我会build议使用替代,可能Dictionary<string, int> rat;
如果你创build了一个Dictionary
并且用你的数据填充它,那么枚举就更简单了。 此外,它是一个更清晰的意图映射 – 你用这个枚举将数字映射到string,并且你正在试图利用这个映射。
如果你必须使用枚举,我会build议一些其他的东西:
var rats = new List<eRat>() {eRat.A, eRat.B, eRat.C, eRat.D};
只要您按顺序添加值并保持同步,就可以大大简化检索下一个eRat的操作。
你可以简化它并概括一些:
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.NULL
或BRUSHSTYLE.HOLLOW
,返回值将是BRUSHSTYLE.HOLLOW
。
<leppie>
更新:一个generics版本:
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 :
您的通用版本允许您意外地传递一个非枚举值,这个值只会在运行时被捕获。 我原本是把它写成一个通用的,但是当编译器拒绝where T : Enum
,我把它拿出来了,意识到我没有从generics中获益太多。 唯一的缺点是必须将结果转换回您的特定枚举types。
对于简单的解决scheme,你可能只是从枚举中提取数组。
eRat[] list = (eRat[])Enum.GetValues(typeof(eRat));
那么你可以枚举
foreach (eRat item in list) //Do something
或find下一个项目
int index = Array.IndexOf<eRat>(list, eRat.B); eRat nextItem = list[index + 1];
每次你想要下一个值时,存储数组要比从枚举中提取要好。
但是如果你想要更漂亮的解决scheme,请创build这个类。
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;
希望我的代码的这部分可以帮助你:
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(); }
我能想到2件事情:
- eRat.B + 3
- Enum.Parse(typeof运算(((int)的eRat.B)+3)
var next =(eRat)((int)someRat + 3);
我正在使用这个,完美的我的。
//=================================================================================== // 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 ]; }
旧post,但我有一个替代解决scheme
//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 }
LINQ解决scheme不会在最后一个元素中断,而是继续在默认情况下继续:
var nextValue = Enum.GetValues(typeof(EnumT)).Cast<EnumT>().Concat(new[]{default(EnumT)}).SkipWhile(_ => _ != value).Skip(1).First();
我会和Sung Meister一起回答,但这里有一个select:
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; } }
注:许多假设假设:)
看起来像是滥用了枚举类对我来说 – 但这会做到这一点(假设对最后一个值调用下一个将导致环绕):
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; } }
从评论我有很多问题,如:“你为什么要用这种方式使用枚举。 既然你们中有很多人问过,那么让我给你看我的用例,看看你是否同意:
我有一个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)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
这非常简单。 但是我希望这个解释。 还有一些其他的优点,如enum.toString()。 所以基本上我使用枚举作为索引。
所以使用解决scheme,我现在可以做这样的事情。
按顺序eRat1到B的下一个值是C,但在eRat2中是BB。 所以根据我感兴趣的序列,我可以做e.next,根据enumType,我会得到C或BB。 用字典怎么实现呢?
我认为这是一个相当优雅的使用枚举。
我在这里使用这个:
public MyEnum getNext() { return this.ordinal() < MyEnum.values().length - 1 ? MyEnum.values()[this.ordinal() + 1] : MyEnum.values()[0]; }
有一个非常简单的解决scheme(如果您可以更改您的整数值) ,专门devise用于数字。 你的号码是一个enum
的事实,不是一个问题。 它仍然是integer
(或者你分配的任何底层数字types)。 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 。
您也可以通过将<<
按位运算符从<<
更改为>>
来向后倒退。
myStatus = (ItemStatus)(((int)myStatus) << 1)
myStatus的结果是: ItemStatus.New 。
你应该总是添加代码来testing双向的“越界”情况。
您可以在这里了解更多关于按位运算的内容: http : //code.tutsplus.com/articles/understanding-bitwise-operators–active-11301