Получение максимального значения перечисления

148

Как вы получаете максимальное значение перечисления?

jdelator
источник
1
Возможный дубликат поиска
наибольшего
1
Я думаю, что компилятор мог бы справиться с этим, вместо использования вызова Reflection. Ответ, который использовал метод времени компиляции для этой информации, получил бы мой положительный голос.
Palswim

Ответы:

214

Enum.GetValues ​​(), кажется, возвращает значения по порядку, так что вы можете сделать что-то вроде этого:

// given this enum:
public enum Foo
{
    Fizz = 3, 
    Bar = 1,
    Bang = 2
}

// this gets Fizz
var lastFoo = Enum.GetValues(typeof(Foo)).Cast<Foo>().Last();

редактировать

Для тех, кто не хочет читать комментарии: Вы также можете сделать это следующим образом:

var lastFoo = Enum.GetValues(typeof(Foo)).Cast<Foo>().Max();

... который будет работать, когда некоторые из ваших значений enum отрицательны.

Мэтт Гамильтон
источник
3
отлично. пользуясь преимуществом: «Элементы массива отсортированы по двоичным значениям констант перечисления». от msdn.microsoft.com/en-us/library/system.enum.getvalues.aspx
TheSoftwareJedi
2
Я била головой, думая, что это действительно тривиально, но было дано элегантное решение. Также, если вы хотите получить значение enum, используйте Convert.ToInt32 () впоследствии. Это для результатов Google.
jdelator
54
Если вы собираетесь использовать LINQ, почему бы не использовать Max (), который намного понятнее и не полагается на «кажется»?
Марк Гравелл
3
Это работает лучше, но только если значения перечисления не отрицательны, какими они могли бы быть.
ICR
4
Я тоже голосую за Макса (). Last () потерпит неудачу, если enum не отрицателен, смотрите здесь
AZ.
42

Я согласен с ответом Мэтта. Если вам нужны только значения min и max int, то вы можете сделать это следующим образом.

Максимум:

Enum.GetValues(typeof(Foo)).Cast<int>().Max();

Минимум:

Enum.GetValues(typeof(Foo)).Cast<int>().Min();
Каранвир Канг
источник
21

Согласно ответу Мэтта Гамильтона, я подумал о создании для него метода Extension.

Так ValueTypeне принят в качестве общего ограничения параметров типа, я не нашел лучший способ ограничить Tдо , Enumно ниже.

Любые идеи будут по достоинству оценены.

PS. пожалуйста, игнорируйте мою неявность VB, я люблю использовать VB таким образом, это сила VB, и поэтому я люблю VB.

Хоева, вот оно:

C #:

static void Main(string[] args)
{
    MyEnum x = GetMaxValue<MyEnum>(); //In newer versions of C# (7.3+)
    MyEnum y = GetMaxValueOld<MyEnum>();  
}

public static TEnum GetMaxValue<TEnum>()
  where TEnum : Enum
{
     return Enum.GetValues(typeof(TEnum)).Cast<TEnum>().Max();
}

//When C# version is smaller than 7.3, use this:
public static TEnum GetMaxValueOld<TEnum>()
  where TEnum : IComparable, IConvertible, IFormattable
{
    Type type = typeof(TEnum);

    if (!type.IsSubclassOf(typeof(Enum)))
        throw new
            InvalidCastException
                ("Cannot cast '" + type.FullName + "' to System.Enum.");

    return (TEnum)Enum.ToObject(type, Enum.GetValues(type).Cast<int>().Last());
}



enum MyEnum
{
    ValueOne,
    ValueTwo
}

VB:

Public Function GetMaxValue _
    (Of TEnum As {IComparable, IConvertible, IFormattable})() As TEnum

    Dim type = GetType(TEnum)

    If Not type.IsSubclassOf(GetType([Enum])) Then _
        Throw New InvalidCastException _
            ("Cannot cast '" & type.FullName & "' to System.Enum.")

    Return [Enum].ToObject(type, [Enum].GetValues(type) _
                        .Cast(Of Integer).Last)
End Function
Шимми Вайцхандлер
источник
См. Stackoverflow.com/questions/79126/… для этого: if (! Type.IsEnum)
Бетонная олуша
вы также можете добавить «struct» к вашему where, так как это будет гарантировать, что это ValueType, но не ограничивать его перечислением. Это то, что я использую: где T: struct, IComparable, IFormattable
jdawiz
2
Вы можете обновить свой ответ. В C # 7.3 вы могли бы на самом деле сделать метод расширения, а также ограничиться типом Enum (вместо struct).
Нордес
Это не метод расширения, хотя. Но хороший полезный метод.
Рэй
14

Это немного nitpicky , но фактическое максимальное значение любого enumявляется Int32.MaxValue(при условии , что это enumпроисходит от int). Совершенно законно приводить любое Int32значение к любому enumнезависимо от того, действительно ли оно объявило члена с этим значением.

Допустимо:

enum SomeEnum
{
    Fizz = 42
}

public static void SomeFunc()
{
    SomeEnum e = (SomeEnum)5;
}
JaredPar
источник
У меня были случаи, когда установка переменной Integer на возвращаемое перечисление завершалась неудачно с несоответствием типов при вводе сумасшедшего значения, поэтому лучше вместо этого использовать Long (если вы лениво не улавливаете ошибку должным образом!).
AjV Jsy
9

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

public static class EnumExtension
{
    public static int Max(this Enum enumType)
    {           
        return Enum.GetValues(enumType.GetType()).Cast<int>().Max();             
    }
}

class Program
{
    enum enum1 { one, two, second, third };
    enum enum2 { s1 = 10, s2 = 8, s3, s4 };
    enum enum3 { f1 = -1, f2 = 3, f3 = -3, f4 };

    static void Main(string[] args)
    {
        Console.WriteLine(enum1.one.Max());        
    }
}
Эрик Фенг
источник
5

При использовании функции Last не удалось получить максимальное значение. Использовать функцию «макс» можно. Подобно:

 class Program
    {
        enum enum1 { one, two, second, third };
        enum enum2 { s1 = 10, s2 = 8, s3, s4 };
        enum enum3 { f1 = -1, f2 = 3, f3 = -3, f4 };

        static void Main(string[] args)
        {
            TestMaxEnumValue(typeof(enum1));
            TestMaxEnumValue(typeof(enum2));
            TestMaxEnumValue(typeof(enum3));
        }

        static void TestMaxEnumValue(Type enumType)
        {
            Enum.GetValues(enumType).Cast<Int32>().ToList().ForEach(item =>
                Console.WriteLine(item.ToString()));

            int maxValue = Enum.GetValues(enumType).Cast<int>().Max();     
            Console.WriteLine("The max value of {0} is {1}", enumType.Name, maxValue);
        }
    }
Эрик Фенг
источник
4

По согласованию с Мэтью Дж Салливаном, для C #:

   Enum.GetValues(typeof(MyEnum)).GetUpperBound(0);

Я действительно не уверен, почему кто-то хотел бы использовать:

   Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().Last();

... Как дословно, семантически говоря, это не имеет особого смысла? (всегда хорошо иметь разные способы, но я не вижу выгоды в последнем.)

инженер
источник
4
Использование GetUpperBound возвращает мне счет (4, а не 40): MyEnum {a = 0, b = 10, c = 20, d = 30, e = 40}
alirobe
Хороший улов, я этого не заметил. Итак, эта версия отлично подходит для стандартных автоматических перечислений, но не для перечислений с пользовательскими значениями. Я думаю, что победителем остается мистер Гамильтон. :)
инженер
GetUpperBound хорош, когда ваш Enum имеет побитовые значения. (т.е. - 1, 2, 4, 8 и т. д.). Например, если вы писали модульный тест, вы бы хотели работать через цикл с таким количеством итераций: Math.Pow (2, Enum.GetValues ​​(typeof (MyEnum)). GetUpperBound (0)).
WEFX
Что не так с Length (для количества значений в перечислении)? Проще лучше. (Не то чтобы это
отвечало
2

Существуют методы для получения информации о перечисляемых типах в System.Enum.

Итак, в проекте VB.Net в Visual Studio я могу напечатать «System.Enum». и intellisense воспитывает все виды доброты.

В частности, одним из методов является System.Enum.GetValues ​​(), который возвращает массив перечисляемых значений. Получив массив, вы сможете делать все, что подходит для ваших конкретных обстоятельств.

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

Фрагменты кода VB.Net:

'''''''

Enum MattType
  zerothValue         = 0
  firstValue          = 1
  secondValue         = 2
  thirdValue          = 3
End Enum

'''''''

Dim iMax      As Integer

iMax = System.Enum.GetValues(GetType(MattType)).GetUpperBound(0)

MessageBox.Show(iMax.ToString, "Max MattType Enum Value")

'''''''

источник
Не работает, когда в массиве есть пробелы. Это также работает только случайно, потому что индекс элемента оказывается равным значению, хранящемуся в этом индексе. GetUpperBound()извлекает максимально возможный индекс в массиве, возвращенном GetValues(), а не наибольшее значение, хранящееся в этом массиве.
JensG
2

В F # с помощью вспомогательной функции для преобразования перечисления в последовательность:

type Foo =
    | Fizz  = 3
    | Bang  = 2

// Helper function to convert enum to a sequence. This is also useful for iterating.
// stackoverflow.com/questions/972307/can-you-loop-through-all-enum-values-c
let ToSeq (a : 'A when 'A : enum<'B>) =
    Enum.GetValues(typeof<'A>).Cast<'B>()

// Get the max of Foo
let FooMax = ToSeq (Foo()) |> Seq.max   

Запуск это ...

> Тип Foo = | Fizz = 3 | Взрыв = 2
> val ToSeq: 'A -> seq <' B> когда 'A: enum <' B>
> val FooMax: Foo = Fizz

when 'A : enum<'B>Не требуется компилятор для определения, но требуются для любого использования ToSeq, даже действительным перечислимого типа.

Стивен Хоскинг
источник
1

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

enum Values {
  one,
  two,
  tree,
  End,
}

for (Values i = 0; i < Values.End; i++) {
  Console.WriteLine(i);
}

var random = new Random();
Console.WriteLine(random.Next((int)Values.End));

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

Иван Вандер Санден
источник
1

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

public enum ChannelMessageTypes : byte
{
    Min                 = 0x80, // Or could be: Min = NoteOff
    NoteOff             = 0x80,
    NoteOn              = 0x90,
    PolyKeyPressure     = 0xA0,
    ControlChange       = 0xB0,
    ProgramChange       = 0xC0,
    ChannelAfterTouch   = 0xD0,
    PitchBend           = 0xE0,
    Max                 = 0xE0  // Or could be: Max = PitchBend
}

// I use it like this to check if a ... is a channel message.
if(... >= ChannelMessageTypes.Min || ... <= ChannelMessages.Max)
{
    Console.WriteLine("Channel message received!");
}
XLars
источник