我有一个枚举,其定义如下:
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+1
或Enum.Next(Erat.B)
?
由于
答案 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.NULL
或BRUSHSTYLE.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)
我能想到两件事:
答案 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