Obtendo o valor máximo de uma enumeração

147

Como você obtém o valor máximo de uma enumeração?

jdelator
fonte
Possível duplicado de Finding o valor mais alto em uma enumeração
Ian Goldby
1
Eu pensaria que o compilador poderia lidar com isso, em vez de usar uma chamada de reflexão. Uma resposta que usasse um método em tempo de compilação para essas informações receberia meu voto positivo.
palswim

Respostas:

214

Enum.GetValues ​​() parece retornar os valores em ordem, para que você possa fazer algo assim:

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

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

Editar

Para aqueles que não desejam ler os comentários: Você também pode fazer o seguinte:

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

... que funcionará quando alguns de seus valores de enumeração forem negativos.

Matt Hamilton
fonte
3
legais. aproveitando: "Os elementos da matriz são classificados pelos valores binários das constantes de enumeração." de msdn.microsoft.com/en-us/library/system.enum.getvalues.aspx
TheSoftwareJedi 15/10/2008
2
Eu estava batendo na minha cabeça, pensando que isso era realmente um rival, mas uma solução elegante foi dada. Além disso, se você deseja obter o valor da enumeração, use Convert.ToInt32 () posteriormente. Isto é para os resultados do google.
jdelator
53
Se você usar o LINQ, por que não usar Max (), que é muito mais claro e não depende de "parece"?
Marc Gravell
3
Ele tem um desempenho melhor, mas somente se os valores da enumeração não forem negativos, o que poderiam ser.
ICR
4
Também voto em Max (). O Last () falharia se o enum não fosse negativo, veja aqui
AZ.
41

Eu concordo com a resposta de Matt. Se você precisar apenas de valores mínimo e máximo int, poderá fazê-lo da seguinte maneira.

Máximo:

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

Mínimo:

Enum.GetValues(typeof(Foo)).Cast<int>().Min();
Karanvir Kang
fonte
21

De acordo com a resposta de Matt Hamilton, pensei em criar um método de extensão para ele.

Como ValueTypenão é aceito como uma restrição de parâmetro de tipo genérico, não encontrei uma maneira melhor de restringir TaEnum , mas o seguinte.

Qualquer idéia seria realmente apreciada.

PS. por favor, ignore minha implicitude VB, eu amo usar VB dessa maneira, essa é a força do VB e é por isso que eu amo VB.

Howeva, aqui está:

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
Shimmy Weitzhandler
fonte
Veja stackoverflow.com/questions/79126/… para isso: if (! Type.IsEnum)
Concrete Gannet
você também pode adicionar "struct" para o seu onde, como isso irá garantir que ele é um ValueType, mas não restringi-lo a Enum Isto é o que eu uso: onde T: struct, IComparable, IFormattable
jdawiz
2
Você pode atualizar sua resposta. No C # 7.3, você poderia realmente executar o método de extensão e também restringir ao tipo Enum (em vez de struct).
Nordes
Este não é um método de extensão . Mas um bom método de utilidade.
Ray
14

Isso é um pouco complicado, mas o valor máximo real de qualquer um enumé Int32.MaxValue(assumindo que seja enumderivado de int). É perfeitamente legal converter qualquer Int32valor para qualquer um, enumindependentemente de ter ou não declarado um membro com esse valor.

Legal:

enum SomeEnum
{
    Fizz = 42
}

public static void SomeFunc()
{
    SomeEnum e = (SomeEnum)5;
}
JaredPar
fonte
Já tive casos em que a definição de uma variável Integer para uma enum retornada falha com a incompatibilidade de tipo quando um valor louco é fornecido, por isso é melhor usar um Long (se você não estiver prendendo o erro corretamente!).
AJV JSY
9

Depois de tentar outra vez, obtive este método de extensão:

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());        
    }
}
Eric Feng
fonte
5

Use a função Last não pôde obter o valor máximo. Use a função "max" poderia. Gostar:

 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);
        }
    }
Eric Feng
fonte
4

De acordo com Matthew J Sullivan, para C #:

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

Eu realmente não sei por que alguém iria querer usar:

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

... Como palavra por palavra, semanticamente falando, não parece fazer tanto sentido? (sempre bom ter maneiras diferentes, mas não vejo o benefício no último.)

Engenheiro
fonte
4
Usar GetUpperBound retorna uma contagem (4, não 40) para mim: MyEnum {a = 0, b = 10, c = 20, d = 30, e = 40}
alirobe
Boa captura, eu não tinha notado isso. OK, então esta versão é ótima para enumerações automáticas padrão, mas não para enumerações com valores personalizados. Acho que o vencedor continua sendo o senhor Hamilton. :)
Engenheiro
GetUpperBound é bom quando seu Enum possui valores bit a bit. (ie - 1, 2, 4, 8, etc). Por exemplo, se você estivesse escrevendo um teste de unidade, desejaria trabalhar com um loop com muitas iterações: Math.Pow (2, Enum.GetValues ​​(typeof (MyEnum)). GetUpperBound (0)).
WEFX 18/08/19
O que há de errado com Comprimento (para o número de valores na enumeração)? Mais simples é melhor. (Não que isso responde à pergunta original.)
Ian Goldby
2

Existem métodos para obter informações sobre tipos enumerados em System.Enum.

Portanto, em um projeto VB.Net no Visual Studio, posso digitar "System.Enum". e o intellisense traz todo tipo de bondade.

Um método em particular é System.Enum.GetValues ​​(), que retorna uma matriz dos valores enumerados. Depois de obter a matriz, você poderá fazer o que for apropriado para suas circunstâncias particulares.

No meu caso, meus valores enumerados começaram em zero e não saltaram nenhum número; portanto, para obter o valor máximo da minha enumeração, só preciso saber quantos elementos estavam na matriz.

Fragmentos de código 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")

'''''''

fonte
Não funciona quando a matriz tem lacunas. Também funciona apenas por acidente, porque o índice do elemento é igual ao valor armazenado nesse índice. GetUpperBound()recupera o índice mais alto possível na matriz retornada por GetValues(), não o valor mais alto armazenado dentro dessa matriz.
JensG 01/06/19
2

No F #, com uma função auxiliar para converter o enum em uma sequência:

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   

Executando ...

> tipo Foo = | Fizz = 3 | Bang = 2
> val ToSeq: 'A -> seq <' B> quando 'A: enum <' B>
> val FooMax: Foo = Fizz

O when 'A : enum<'B>não é exigido pelo compilador para a definição, mas é necessário para qualquer uso do ToSeq, mesmo por um tipo de enumeração válido.

Stephen Hosking
fonte
1

Não é utilizável em todas as circunstâncias, mas geralmente eu mesmo defino o valor máximo:

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

Obviamente, isso não funcionará quando você usar valores personalizados em uma enumeração, mas geralmente pode ser uma solução fácil.

yvan vander sanden
fonte
1

Usei o seguinte quando precisei dos valores mínimo e máximo da minha enumeração. Acabei de definir um mínimo igual ao valor mais baixo da enumeração e um valor máximo igual ao valor mais alto na enumeração, já que os próprios valores da enumeração.

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
fonte