Подтвердить что ты не робот

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

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

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

Спасибо

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

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

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 на Enumeration Design. Первая лучшая практика:

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

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

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

Ответ 5

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

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

[обновление2]: обновлено в соответствии с тем, как хотел получить хайит - вернуть "А" для следующего значения "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

Ответ 6

Заблокирован ли вы с помощью перечисления тем, что у вас нет контроля?

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

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

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

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

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

Ответ 7

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

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

Например

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

и т.д...

Ответ 8

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

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

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

Ответ 9

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

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

Instantiate

var eRatEnum = new EnumEnumerator<eRat>();

Итерация

foreach (eRat item in eRatEnum)
    //Do something

MoveNext

eRatEnum.Seek(eRat.B);
eRatEnum.MoveNext();
eRat nextItem = eRatEnum.Current;

Ответ 10

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

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

Ответ 11

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

  • eRat.B + 3
  • Enum.Parse(TypeOf (((ИНТ) eRat.B) +3)

Ответ 12

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

Ответ 13

Похоже на злоупотребление классом 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;
    }
}

Ответ 14

Я использую это, отлично для себя.

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

Ответ 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);
}

И когда вам нужно его использовать, просто сделайте бросок

BootstrapThemeEnum theme = BootstrapThemeEnum.Info;
var next = (BootstrapThemeEnum)theme.Next();

my enum

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

}

Ответ 16

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

Ответ 17

Я бы пошел с ответом 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;
  }
}

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

Ответ 18

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

У меня есть фиксированный массив элементов 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(). Поэтому в основном я использую перечисления в качестве индексаторов.

Таким образом, используя решение, я могу сделать что-то подобное сейчас.

В последовательности eRat1 следующее значение B - C, но в eRat2 это BB. Поэтому в зависимости от того, в какой последовательности меня интересует, я могу сделать e.next и в зависимости от enumType я либо получу C, либо BB. Как добиться этого с помощью словарей?

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

Ответ 19

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

public MyEnum getNext() {
    return this.ordinal() < MyEnum.values().length - 1 ? 
                            MyEnum.values()[this.ordinal() + 1] : 
                            MyEnum.values()[0];
}

Ответ 20

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

var nextValue = Enum.GetValues(typeof(EnumT)).Cast<EnumT>().Concat(new[]{default(EnumT)}).SkipWhile(_ => _ != value).Skip(1).First();

Ответ 21

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

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

Ответ 22

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

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

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

Ответ 23

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

Вот мои результаты, как продолжение.

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