Как получить следующее (или предыдущее) значение перечисления в С#

У меня есть перечисление, которое определяется следующим образом:

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

Спасибо


person husayt    schedule 13.03.2009    source источник
comment
это зависит от вас. Но скажем, А, чтобы нам не пришлось вызывать исключение.   -  person husayt    schedule 13.03.2009
comment
@husayt: код обновлен. Теперь, когда вы проходите D, возвращается A.   -  person dance2die    schedule 13.03.2009


Ответы (24)


Спасибо всем за ваши ответы и отзывы. Я был удивлен, что их так много. Глядя на них и используя некоторые из идей, я придумал это решение, которое лучше всего подходит для меня:

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

person husayt    schedule 13.03.2009
comment
В основном работал на меня. Type.IsEnum, по-видимому, не существует для структуры в Windows Phone Silverlight 8.0. Убрал проверку типа и все работает. - person azarc3; 17.11.2014
comment
Зачем использовать struct, когда можно использовать where T : Enum? - person Vencovsky; 07.12.2020

Возможно, немного перебор, но:

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

person Marc Gravell    schedule 13.03.2009
comment
+1 с LINQ в качестве молотка -› не навредить себе - это самый быстрый способ научиться? ;) - person dance2die; 13.03.2009
comment
Эй, я знаю, что занимаюсь некромантией ;-) но, говоря о молотах, будьте осторожны в высокопроизводительных ситуациях с такими конструкциями (LINQ). Каждый оператор LINQ завершает внутреннее выделение списка. Думая, что это круто, я использовал такой синтаксис в очень большом проекте, и он быстро стал точкой № 1 по выделению памяти. Так что иногда уродливое, но чистое превосходит модное и крутое. - person Keith Bluestone; 22.02.2013
comment
@KeithBluestone нет, неправильно говорить, что каждая операция LINQ выделяет список; большинство из них выполняется блоками итераторов. Основные из них, которые необходимо буферизовать, — это OrderBy и GroupBy. - person Marc Gravell; 23.02.2013
comment
@MarcGravell Точка принята, спасибо. В целом, я просто хотел, чтобы разработчики знали, что под дружелюбным, семантически уютным капотом LINQ может скрываться множество распределений List‹›. Я являюсь поклонником LINQ, и в большинстве случаев это не проблема; но написание кода с высоким трафиком меня укусило. - person Keith Bluestone; 23.02.2013
comment
Это не работает, когда eRat value = eRat.D "InvalidOperationException Sequence contains no elements" rextester.com/live/YRGPO87367 - person David d C e Freitas; 24.10.2013
comment
Хорошо, но это происходит с InvalidOperationException при применении к самому высокому значению. Если вы обернете его с помощью try/catch, вы можете использовать (T)Enum.GetValues(typeof(T)).Cast‹T›().Min(); чтобы установить его на минимум, если вы хотите циклически. - person Lord of Scripts; 07.04.2016
comment
Или просто используйте FirstOrDefault вместо First. - person jesperhh; 02.12.2016

Вам действительно нужно обобщать эту проблему? Можете ли вы просто сделать это вместо этого?

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;
    }
}
person Kent Boogaart    schedule 13.03.2009
comment
+1 простота. создайте что-то, что решит насущную потребность, и двигайтесь дальше. рефакторинг позже, если это необходимо. - person Rex M; 13.03.2009
comment
Поможет ли это, если мы предположим, что это и есть последующий рефакторинг? - person Whatsit; 13.03.2009
comment
-1. Мне это не нравится, так как это ошибка обслуживания, ожидающая своего появления. День, когда вы что-то добавляете и задаетесь вопросом, почему ваш код возвращает null, действительно плохой день. - person Eddie Parker; 13.03.2014

Проблема, с которой вы имеете дело, заключается в том, что вы пытаетесь заставить перечисление делать то, чего оно не должно. Они должны быть типобезопасными. Разрешается присваивать целочисленные значения перечислению, чтобы их можно было комбинировать, но если вы хотите, чтобы они представляли целочисленные значения, используйте классы или структуры. Вот возможная альтернатива:

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

Вам действительно следует использовать перечисления только тогда, когда вы можете извлечь выгоду из их безопасности типов. Если вы полагаетесь на них для представления типа, переключитесь на константы или классы.

ИЗМЕНИТЬ

Я бы посоветовал вам взглянуть на страницу MSDN в разделе Дизайн перечисления. Первая лучшая практика:

Используйте перечисление, чтобы строго типизировать параметры, свойства и возвращаемые значения, представляющие наборы значений.

Я стараюсь не спорить с догмами, поэтому не буду, но вот проблема, с которой вы столкнетесь. Microsoft не хочет, чтобы вы делали то, что пытаетесь сделать. Они прямо просят вас не делать того, что вы пытаетесь сделать. Вам будет трудно делать то, что вы пытаетесь сделать. Чтобы выполнить то, что вы пытаетесь сделать, вы должны создать служебный код, чтобы заставить его работать.

Вы назвали свое решение элегантным более одного раза, и это могло бы быть, если бы перечисления были спроектированы по-другому, но поскольку перечисления такие, какие они есть, ваше решение не является элегантным. Я думаю, что камерная музыка элегантна, но если бы у музыкантов не было надлежащих инструментов и им приходилось бы играть Вивальди с пилами и кувшинами, она перестала бы быть элегантной, независимо от того, насколько талантливы они как музыканты или насколько хороша музыка. был на бумаге.

person Michael Meadows    schedule 13.03.2009
comment
Глядя на это, я хотел бы поставить еще +1. Глядя на это, это кажется таким очевидным, но я не думаю, что когда-либо действительно делал что-то подобное. - person Greg D; 13.03.2009
comment
Пожалуйста, проверьте мой ответ, где я объясняю, почему мне нужно перечисление. Если вы увидите решение, работающее в этом контексте, я был бы более чем счастлив принять его. - person husayt; 13.03.2009

Работает до "C", т.к. нет ответа, что возвращать после "D".

[update1]: обновлено в соответствии с предложением Марка Гравелла.

[update2]: обновлено в соответствии с пожеланиями Хусайта — верните «A» для следующего значения «D».

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

person dance2die    schedule 13.03.2009
comment
Кстати; .Take(1).ToArray()[0] - может быть просто .First() - person Marc Gravell; 13.03.2009
comment
МОЙ БОГ. Если другой парень скажет мне, что C# лучше, чем C++, я покажу ему этот образец, чтобы показать (1), что вы не можете легко получить следующее или предыдущее перечисление в C# и (2) вы можете сделать select для этого. О Боже! (речь не о вас, а о том, что вы можете делать в C#: кто-то скажет, что делать все, что вы хотите, хорошо, кто-то скажет, что это зло. ИМХО это зло.) - person Olivier Pons; 06.01.2018

Спасибо вам всем за ваше вдохновение и решения.

Вот мои результаты, как расширение.

using System; 
using System.Linq;

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

использовать:

using System; 
using System.Linq;

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
person nofilenamed    schedule 13.07.2019
comment
Не забудьте написать using System; и using System.Linq;. - person user2513149; 05.04.2021
comment
@user2513149 user2513149 спасибо за информацию, исправил здесь. Не знаю, почему этого не было. - person nofilenamed; 08.04.2021

Вы заблокированы в использовании перечисления чем-то, что вы не можете контролировать?

Если нет, я бы предложил использовать альтернативу, возможно, Dictionary<string, int> rat;

Если вы создаете Dictionary и заполняете его своими данными, перечисление по нему несколько упрощается. Кроме того, это более четкое сопоставление намерений — вы сопоставляете числа со строками с помощью этого перечисления и пытаетесь использовать это сопоставление.

Если вы должны использовать перечисление, я бы предложил что-то еще:

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

Пока вы добавляете значения по порядку и поддерживаете их синхронизацию, вы значительно упрощаете процесс извлечения следующего eRat.

person Greg D    schedule 13.03.2009

Судя по вашему описанию, вам действительно нужно перечисление. Вы растягиваете перечисление за пределы его возможностей. Почему бы не создать собственный класс, который предоставляет нужные вам значения в виде свойств, сохраняя их в OrderedDictionary. Тогда получение следующего/предыдущего будет тривиальным. --Обновить

Если вы хотите по-разному перечислять коллекцию в зависимости от контекста, сделайте это явной частью вашего дизайна. Инкапсулируйте элементы внутри класса и используйте несколько методов, каждый из которых возвращает IEnumerable, где T — желаемый тип.

Например

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

и т.д...

person Krzysztof Kozmic    schedule 13.03.2009
comment
Хорошее предложение - вместо того, чтобы перегружать перечисление. - person Gordon Mackie JoanMiro; 13.03.2009
comment
Я не понимаю. Зачем выполнять foreach для перечисления? Можете ли вы дать здесь больше контекста? - person Krzysztof Kozmic; 13.03.2009
comment
не на самом перечислении. но иметь возможность перечислять элементы массива по-разному. Я использую перечисления в качестве фиксированных индексаторов. - person husayt; 13.03.2009
comment
проверьте мой ответ для более подробного объяснения. Спасибо. - person husayt; 13.03.2009

Вы можете упростить его и обобщить:

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: обратите внимание, что если ваше перечисление содержит повторяющиеся значения (синонимичные записи), то этот (или любой другой метод, указанный здесь) не сработает, учитывая одно из этих значений. Например:

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.

‹леппи›

Обновление: версия дженериков:

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

‹/леппи›

@леппи:

Ваша универсальная версия позволяет случайно передать значение, отличное от перечисления, которое будет обнаружено только во время выполнения. Первоначально я написал его как дженерик, но когда компилятор отклонил where T : Enum, я вынул его и понял, что в любом случае я мало что получаю от дженериков. Единственным реальным недостатком является то, что вам нужно вернуть результат к вашему конкретному типу перечисления.

person P Daddy    schedule 13.03.2009
comment
Ну давай же! Дженерик, без дженериков! Не ленись :) - person leppie; 13.03.2009
comment
@leppie: Пожалуйста, смотрите комментарий в посте. - person P Daddy; 13.03.2009
comment
Вы можете добавить проверку внутри метода. ИЛИ вам не нужно, так как он все равно взорвется :) - person leppie; 13.03.2009
comment
Я хотел сказать, что неуниверсальная версия уловит вашу ошибку во время компиляции. Я всегда предпочитаю ловить ошибки во время компиляции, а не во время выполнения. - person P Daddy; 13.03.2009
comment
чтобы сделать общую версию более безопасной, вы можете поместить туда структуру T : а затем if (!typeof(T).IsEnum) throw new ArgumentException(String.Format(Argumnent {0} is Not an Enum, typeof(T).FullName)) ; чтобы обеспечить его дальнейшее соблюдение - person husayt; 13.03.2009
comment
@husayt: Это действительно не намного лучше. Это по-прежнему оставляет много распространенных случаев, когда проверка во время выполнения заменяет проверку во время компиляции. Я люблю дженерики так же сильно, как и следующий парень (а может, и больше), но я действительно не понимаю, почему люди кажутся такими одержимыми, чтобы сделать этот конкретный метод универсальным. - person P Daddy; 14.03.2009

Для простого решения вы можете просто извлечь массив из перечисления.

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;
person Chaowlert Chaisrichalermpol    schedule 13.03.2009

Надеюсь, эта часть моего кода поможет вам:

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();
}
person Keyvan    schedule 17.06.2011
comment
Я упростил до одной строки - GroupedBy = (EGroupedBy)(((int)GroupedBy + 1) % (int)EGroupedBy._max); - person simo.3792; 04.11.2016

Старый пост, но у меня есть альтернативное решение

//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

}
person Alexandre    schedule 15.10.2014

Я могу думать о 2 вещах:

  • eRat.B+3
  • Enum.Parse(typeof(((int)eRat.B)+3)
person RvdK    schedule 13.03.2009
comment
потому что общедоступное перечисление eRat { A = 0, B = 3, C = 5, D = 8 }; A = 0, B = 3, поэтому следующий +3 - person RvdK; 13.03.2009
comment
Да, но решение должно быть универсальным. он должен работать для любого перечисления. Будет ли это работать для A в нашем случае?? Мне просто нужен следующий член перечисления. - person husayt; 13.03.2009
comment
действительно, я явно спал :P извините - person RvdK; 14.03.2009

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

person eglasius    schedule 13.03.2009
comment
Спасибо, но нет. мы ищем универсальное решение. это не сработает, например, для A. - person husayt; 13.03.2009
comment
@husayt это работает для A, (0 + 3) = B :) - чтобы он работал полностью, просто разделите его немного и проверьте, не выходит ли он выше последнего. - person eglasius; 13.03.2009

Мне кажется, что это злоупотребление классом enum, но это сделает это (при условии, что вызов 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;
    }
}
person Gordon Mackie JoanMiro    schedule 13.03.2009

Я использую это, идеально подходит для моего.

    //===================================================================================
// 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 ];
}
person Óscar Melchor Galán    schedule 15.07.2013

Существует очень простое решение (если вы можете изменить целочисленные значения), которое специально разработано для работы с числами. Тот факт, что ваш номер 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

person Chris Winland    schedule 25.02.2016
comment
Существует БОЛЕЕ простое решение (если вы можете изменить целые числа), которое специально разработано для работы с числами: НАТУРАЛЬНЫЕ ЧИСЛА! Если вы используете 1, 2, 3, 4, 5... Вы можете сделать mystatus++ или mystatus--. Дело в том, что ему нужны эти числа: 0, 3, 5... - person tedebus; 30.11.2017
comment
Это не работает, как ожидалось, только что проверил это и возвращает неправильные значения. - person Payam; 25.10.2018

Я бы согласился с ответом Сунг Мейстера, но вот альтернатива:

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

Примечание: предполагается много предположений :)

person leppie    schedule 13.03.2009

Из комментариев у меня было много вопросов вроде: «Почему вы когда-нибудь захотите использовать enum таким образом». Поскольку многие из вас спрашивали, позвольте мне дать вам мой вариант использования и посмотреть, согласны ли вы тогда:

У меня есть фиксированный массив элементов 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) прогулка (тип (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. Как можно добиться этого со словарями?

Я думаю, что это довольно элегантное использование перечислений.

person husayt    schedule 13.03.2009
comment
Проблема, с которой вы столкнетесь (я утверждаю, что это с точки зрения практичности, а не догмы), заключается в том, что вы пытаетесь заставить перечисления делать то, для чего они специально не предназначены (представлять int). Это может показаться элегантным, но вы обнаружите, что обсуждаемый здесь обходной путь не будет вашим последним. - person Michael Meadows; 13.03.2009
comment
Я использую Enums для перечисления (во всяком случае, это то, что предполагает их имя). Я использую их как индексаторы, а не просто как целые числа. В порядке. Представьте себе шахматную доску: A,B,C,..,E,F,G,H Одна фигура ходит A-›C-›D-›F Другая B-›C-›E-›H Третья C-›A- ›B-›G теперь перечисляет самый элегантный способ описать все эти движения. - person husayt; 13.03.2009
comment
Я понял, о чем вы говорили, но перечисления не предназначены для перечисления, хотя имена выглядят одинаково. Перечисления предназначены для представления значений без типа (даже если вы можете определить базовый тип). Я обновлю свой ответ для дальнейшего изложения - person Michael Meadows; 13.03.2009
comment
В любом случае, как будет выглядеть решение без перечислений?? - person husayt; 13.03.2009
comment
Используйте старый добрый ООП. Вы действительно почти там. Вы можете создавать классы для: неявного приведения к любому типу, перечисления в списках, объединения значений в цепочку и наличия поведения. Вы не можете сделать ничего из этого без трения, если используете перечисления. - person Michael Meadows; 13.03.2009
comment
@husayt: массивы индексируются целыми числами. Набор значений индекса массива должен быть целочисленным массивом, а не перечислением. Без перечислений решение имеет меньше приведения и передает постоянный массив значений индекса, чтобы выполнить «прогулку» вместо передачи параметра типа. Разве Битлз не сказали это первыми? «Все, что вам нужно, это int»! ... Ну, может быть, они не то сказали. - person Task; 25.03.2010
comment
Перечисления не для перечисления. Enums — это пронумерованный набор значений. Другие типы могут иметь много возможных значений и часто имеют конструкторы для создания таких значений. Перечисления этого не делают, все значения перечисляются при определении типа. (По крайней мере, это идея. К сожалению, вы можете привести любое значение базового типа к значению перечисления. Но это другая история). - person Kris Vandermotten; 23.09.2013

Я использую это здесь:

public MyEnum getNext() {
    return this.ordinal() < MyEnum.values().length - 1 ? 
                            MyEnum.values()[this.ordinal() + 1] : 
                            MyEnum.values()[0];
}
person tetri    schedule 18.07.2014
comment
Я что-то упускаю? не могу найти ordinal() - person Nathan Goings; 07.02.2018

Решение LINQ, которое не прерывается на последнем элементе, но снова продолжается по умолчанию:

var nextValue = Enum.GetValues(typeof(EnumT)).Cast<EnumT>().Concat(new[]{default(EnumT)}).SkipWhile(_ => _ != value).Skip(1).First();
person user1182735    schedule 18.11.2014
comment
не работает, как ожидалось. Тип возвращаемого значения не является следующим перечислением - person Payam; 25.10.2018

Я попробовал первое решение, но оно не сработало для меня. Ниже мое решение:

    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);
    }
person da jowkar    schedule 22.11.2018

Я сделал что-то подобное с другим перечислением. Это для игры, и у игрока есть возможность переключать цвета.

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

Это решение сработало для меня.

person porras    schedule 07.05.2019

Вы можете добавлять и удалять целые числа в enum, чтобы получить следующее значение. Единственная проблема заключается в том, что целочисленные операции с 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
person Marc Cayuela Rafols    schedule 02.06.2020