Obtener el valor máximo de una enumeración

148

¿Cómo se obtiene el valor máximo de una enumeración?

jdelator
fuente
1
Posible duplicado de encontrar el valor más alto en una enumeración
Ian Goldby
1
Creo que el compilador podría manejar esto, en lugar de usar una llamada de Reflection. Una respuesta que utilizara un método de tiempo de compilación para esta información recibiría mi voto positivo.
palswim

Respuestas:

214

Enum.GetValues ​​() parece devolver los valores en orden, por lo que puede hacer algo como esto:

// 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 aquellos que no están dispuestos a leer los comentarios: también puede hacerlo de esta manera:

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

... que funcionará cuando algunos de sus valores de enumeración sean negativos.

Matt Hamilton
fuente
3
bonito. aprovechando: "Los elementos de la matriz se ordenan por los valores binarios de las constantes de enumeración". de msdn.microsoft.com/en-us/library/system.enum.getvalues.aspx
TheSoftwareJedi
2
Estaba golpeándome la cabeza, pensando que esto era realmente trivial, pero se dio una solución elegante. Además, si desea obtener el valor de enumeración, use Convert.ToInt32 () después. Esto es para los resultados de google.
jdelator
54
Si va a usar LINQ, ¿por qué no usar Max (), que es mucho más claro y no se basa en "parece"?
Marc Gravell
3
Funciona mejor, pero solo si los valores de la enumeración no son negativos, lo que podrían ser.
ICR
44
Yo también voto por Max (). The Last () fallaría si enum no es negativo, mira aquí
AZ.
42

Estoy de acuerdo con la respuesta de Matt. Si necesita solo valores int mínimos y máximos, puede hacerlo de la siguiente manera.

Máximo:

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

Mínimo:

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

Según la respuesta de Matt Hamilton, pensé en crear un método de extensión para ello.

Dado que ValueTypeno se acepta como una restricción parámetro de tipo genérico, no he encontrado una mejor manera de restringir Ta Enumpero el siguiente.

Cualquier idea sería muy apreciada.

PD. por favor ignore mi implícita VB, me encanta usar VB de esta manera, esa es la fortaleza de VB y por eso me encanta VB.

Howeva, aquí 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
fuente
Vea stackoverflow.com/questions/79126/… para esto: if (! Type.IsEnum)
Concrete Gannet
también puede agregar "struct" a su ubicación, ya que esto garantizará que sea un ValueType pero no lo restrinja a Enum. Esto es lo que uso: where T: struct, IComparable, IFormattable
jdawiz
2
Podrías actualizar tu respuesta. En C # 7.3, en realidad podría hacer el método de extensión y también restringir al tipo Enum (en lugar de struct).
Nordes
Sin embargo, este no es un método de extensión . Pero un buen método de utilidad.
Ray
14

Esto es un poco quisquilloso, pero el valor máximo real de cualquiera enumes Int32.MaxValue(suponiendo que se enumderiva de int). Es perfectamente legal emitir cualquier Int32valor a cualquiera, enumindependientemente de si realmente declaró o no un miembro con ese valor.

Legal:

enum SomeEnum
{
    Fizz = 42
}

public static void SomeFunc()
{
    SomeEnum e = (SomeEnum)5;
}
JaredPar
fuente
He tenido casos en los que establecer una variable Integer en una enumeración devuelta falla con la falta de coincidencia de tipos cuando se proporciona un valor loco, por lo que es mejor usar un Long en su lugar (¡si perezosamente no está atrapando el error correctamente!).
AjV Jsy
9

Después de intentarlo otra vez, obtuve este método de extensión:

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
fuente
5

Usar la última función no pudo obtener el valor máximo. Utilice la función "max" podría. Me gusta:

 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
fuente
4

De acuerdo con Matthew J Sullivan, para C #:

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

Realmente no estoy seguro de por qué alguien querría usar:

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

... Como palabra por palabra, semánticamente hablando, ¿no parece tener tanto sentido? (siempre es bueno tener diferentes formas, pero no veo el beneficio en esta última).

Ingeniero
fuente
44
Usar GetUpperBound devuelve un recuento (4, no 40) para mí: MyEnum {a = 0, b = 10, c = 20, d = 30, e = 40}
alirobe
Buena captura, no me había dado cuenta de eso. De acuerdo, esta versión es ideal para las entradas automáticas estándar, pero no para las enumeraciones con valores personalizados. Supongo que el ganador sigue siendo el señor Hamilton. :)
Ingeniero
GetUpperBound es bueno cuando su Enum tiene valores bit a bit. (es decir, 1, 2, 4, 8, etc.). Por ejemplo, si estuviera escribiendo una prueba unitaria, desearía trabajar a través de un bucle con esta cantidad de iteraciones: Math.Pow (2, Enum.GetValues ​​(typeof (MyEnum)). GetUpperBound (0)).
WEFX
¿Qué tiene de malo la longitud (para el número de valores en la enumeración)? Más simple es mejor. (No es que esto responda a la pregunta original).
Ian Goldby
2

Hay métodos para obtener información sobre los tipos enumerados en System.Enum.

Entonces, en un proyecto VB.Net en Visual Studio, puedo escribir "System.Enum". y el intellisense trae todo tipo de bondad.

Un método en particular es System.Enum.GetValues ​​(), que devuelve una matriz de los valores enumerados. Una vez que tenga la matriz, debería poder hacer lo que sea apropiado para sus circunstancias particulares.

En mi caso, mis valores enumerados comenzaron en cero y no omitieron números, por lo que para obtener el valor máximo de mi enumeración solo necesito saber cuántos elementos había en la 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")

'''''''

fuente
No funciona cuando la matriz tiene huecos. También funciona solo por accidente, porque el índice del elemento es igual al valor almacenado en ese índice. GetUpperBound()recupera el índice más alto posible en la matriz devuelta por GetValues(), no el valor más alto almacenado dentro de esa matriz.
JensG
2

En F #, con una función auxiliar para convertir la enumeración en una secuencia:

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   

Ejecutándolo ...

> tipo Foo = | Fizz = 3 | Bang = 2
> val ToSeq: 'A -> seq <' B> cuando 'A: enumeración <' B>
> val FooMax: Foo = Fizz

No when 'A : enum<'B>es requerido por el compilador para la definición, pero es requerido para cualquier uso de ToSeq, incluso por un tipo de enumeración válido.

Stephen Hosking
fuente
1

No es utilizable en todas las circunstancias, pero a menudo yo mismo defino el 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));

Por supuesto, esto no funcionará cuando use valores personalizados en una enumeración, pero a menudo puede ser una solución fácil.

yvan vander sanden
fuente
1

Usé lo siguiente cuando necesitaba los valores mínimo y máximo de mi enumeración. Acabo de establecer un mínimo igual al valor más bajo de la enumeración y un máximo igual al valor más alto en la enumeración como valores enum.

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
fuente