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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/642542/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 11:29:45  来源:igfitidea点击:

How to get next (or previous) enum value in C#

c#.netenums

提问by husayt

I have 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

所以给定值eRat.B,我想得到下一个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 complexity, for something that simple. Do you know any better solution?? Something like eRat.B+1or Enum.Next(Erat.B)?

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

Thanks

谢谢

采纳答案by husayt

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

The beauty 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().

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

回答by RvdK

I can think of 2 things:

我能想到两件事:

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

回答by eglasius

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

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

回答by dance2die

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

最多可以工作到“C”,因为没有关于在“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".

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

Result

结果

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

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

回答by Marc Gravell

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

嘿嘿,用 LINQ 作为你的锤子,这个世界到处都是钉子 ;-p

回答by P Daddy

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.NULLor BRUSHSTYLE.HOLLOW, the return value would be BRUSHSTYLE.HOLLOW.

给定BRUSHSTYLE.NULLBRUSHSTYLE.HOLLOW,返回值将是BRUSHSTYLE.HOLLOW

<leppie>

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

更新:泛型版本:

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>

@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.

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

回答by leppie

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

我会同意 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;
  }
}

Note: many assumptions assumed :)

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

回答by Kent Boogaart

Do you reallyneed 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;
    }
}

回答by Michael Meadows

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:

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

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.

我尽量不争论教条,所以我不会,但这是你将要面对的问题。Microsoft 不希望您做您想做的事情。他们明确要求您不要做您想做的事情。这让你很难做你想做的事。为了完成您正在尝试做的事情,您必须构建实用程序代码以强制其工作。

You have called your solution elegantmore 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.

您不止一次称您的解决方案为优雅,如果枚举的设计方式不同,可能是这样,但由于枚举就是它们,因此您的解决方案并不优雅。我认为室内乐是优雅的,但如果音乐家没有合适的乐器,不得不用锯片和水壶演奏维瓦尔第,无论他们作为音乐家的能力如何,音乐有多好,它都不再优雅在纸上。

回答by Greg D

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;

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

If you create a Dictionaryand 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.

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

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.

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

回答by Krzysztof Kozmic

Judging from your description, you don't really wantan 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

从你的描述来看,你并不真正想要一个枚举。您正在扩展枚举超出其功能。为什么不创建一个自定义类,将您需要的值公开为属性,同时将它们保存在 OrderedDictionary 中。然后获得下一个/上一个将是微不足道的。 - 更新

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.

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

For example

例如

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

etc...

等等...