Uzyskanie maksymalnej wartości wyliczenia

147

Jak uzyskać maksymalną wartość wyliczenia?

jdelator
źródło
Możliwy duplikat znalezienia najwyższej wartości w wyliczeniu
Ian Goldby
1
Myślę, że kompilator poradzi sobie z tym, zamiast używać wywołania odbicia. Odpowiedź, w której zastosowano metodę kompilacji w przypadku tych informacji, otrzymałaby moje poparcie.
palswim

Odpowiedzi:

214

Enum.GetValues ​​() wydaje się zwracać wartości w kolejności, więc możesz zrobić coś takiego:

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

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

Edytować

Dla tych, którzy nie chcą czytać komentarzy: Możesz to również zrobić w ten sposób:

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

... co zadziała, gdy niektóre wartości wyliczenia są ujemne.

Matt Hamilton
źródło
3
miły. korzystając z: "Elementy tablicy są sortowane według wartości binarnych stałych wyliczenia." z msdn.microsoft.com/en-us/library/system.enum.getvalues.aspx
TheSoftwareJedi
2
Uderzałam się w głowę, myśląc, że to naprawdę banał, ale dano eleganckie rozwiązanie. Również jeśli chcesz uzyskać wartość wyliczenia, użyj następnie Convert.ToInt32 (). To jest dla wyników Google.
jdelator
53
Jeśli zamierzasz używać LINQ, dlaczego nie użyć Max (), która jest znacznie bardziej przejrzysta i nie polega na „zdaje się”?
Marc Gravell
3
Działa lepiej, ale tylko wtedy, gdy wartości wyliczenia nie są ujemne, co mogłoby być.
ICR
4
Głosuję też na Maxa (). Last () nie powiedzie się, jeśli wyliczenie nie będzie ujemne, patrz tutaj
AZ.
41

Zgadzam się z odpowiedzią Matta. Jeśli potrzebujesz tylko minimalnych i maksymalnych wartości int, możesz to zrobić w następujący sposób.

Maksymalny:

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

Minimum:

Enum.GetValues(typeof(Foo)).Cast<int>().Min();
Karanvir Kang
źródło
21

Zgodnie z odpowiedzią Matta Hamiltona, pomyślałem o stworzeniu dla niego metody rozszerzenia.

Ponieważ ValueTypenie są akceptowane jako parametr typu rodzajowego przymusu, nie mogę znaleźć lepszy sposób, aby ograniczyć Tdo Enumale następujący.

Wszelkie pomysły byłyby naprawdę mile widziane.

PS. proszę zignoruj ​​moją niejawność VB, uwielbiam używać VB w ten sposób, to jest siła VB i dlatego kocham VB.

Howeva, oto jest:

DO#:

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
źródło
Zobacz stackoverflow.com/questions/79126/… w tym celu: if (! Type.IsEnum)
Concrete Gannet
możesz również dodać "struct" do swojego gdzie, ponieważ zapewni to, że jest to typ wartości, ale nie ogranicza go do Enum. To jest to, czego używam: gdzie T: struct, IComparable, IFormattable
jdawiz
2
Możesz zaktualizować swoją odpowiedź. W C # 7.3 można faktycznie wykonać metodę rozszerzenia, a także ograniczyć się do typu Enum (zamiast struct).
Nordes,
Nie jest to jednak metoda rozszerzająca . Ale dobra metoda użyteczności.
Ray
14

Jest to trochę dziwaczne, ale rzeczywista maksymalna wartość dowolnego enumto Int32.MaxValue(zakładając, że enumpochodzi z int). Całkowicie legalne jest rzutowanie dowolnej Int32wartości na dowolną, enumniezależnie od tego, czy faktycznie zadeklarowała ona członka o tej wartości.

Prawny:

enum SomeEnum
{
    Fizz = 42
}

public static void SomeFunc()
{
    SomeEnum e = (SomeEnum)5;
}
JaredPar
źródło
Miałem przypadki, w których ustawienie zmiennej typu Integer na zwrócone wyliczenie kończy się niepowodzeniem z niedopasowaniem typu, gdy podano szaloną wartość, więc najlepiej zamiast tego użyć Long (jeśli leniwie nie przechwytujesz błędu poprawnie!).
AjV Jsy
9

Po ponownym wypróbowaniu otrzymałem tę metodę rozszerzenia:

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
źródło
5

Użyj funkcji Last nie może uzyskać maksymalnej wartości. Użyj funkcji „max”. Lubić:

 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
źródło
4

W porozumieniu z Matthew J Sullivanem, dla C #:

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

Naprawdę nie jestem pewien, dlaczego ktoś miałby chcieć użyć:

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

... Jako słowo w słowo, mówiąc semantycznie, nie wydaje się to mieć aż tyle sensu? (zawsze dobrze jest mieć różne sposoby, ale nie widzę korzyści w tym drugim).

Inżynier
źródło
4
Użycie GetUpperBound zwraca dla mnie liczbę (4, a nie 40): MyEnum {a = 0, b = 10, c = 20, d = 30, e = 40}
alirobe
Niezły chwyt, nie zauważyłem tego. OK, więc ta wersja świetnie nadaje się do standardowych automatycznych wyliczeń, ale nie do wyliczeń z niestandardowymi wartościami. Myślę, że zwycięzcą pozostaje pan Hamilton. :)
Inżynier
GetUpperBound jest dobra, gdy Enum ma wartości bitowe. (tj. - 1, 2, 4, 8 itd.). Na przykład, jeśli piszesz test jednostkowy, chciałbyś przejść przez pętlę z tyloma iteracjami: Math.Pow (2, Enum.GetValues ​​(typeof (MyEnum)). GetUpperBound (0)).
WEFX
Co jest nie tak z długością (dla liczby wartości w wyliczeniu)? Prostsze znaczy lepsze. (Nie żeby to odpowiadało na pierwotne pytanie.)
Ian Goldby
2

Istnieją metody uzyskiwania informacji o typach wyliczeniowych w System.Enum.

Tak więc w projekcie VB.Net w Visual Studio mogę wpisać „System.Enum”. a inteligencja przynosi wszelkiego rodzaju dobro.

W szczególności jedną z metod jest System.Enum.GetValues ​​(), która zwraca tablicę wyliczonych wartości. Kiedy już masz tablicę, powinieneś być w stanie zrobić wszystko, co jest odpowiednie dla twoich szczególnych okoliczności.

W moim przypadku moje wyliczone wartości zaczynały się od zera i nie pomijały żadnych liczb, więc aby uzyskać maksymalną wartość dla mojego wyliczenia, muszę tylko wiedzieć, ile elementów było w tablicy.

Fragmenty kodu 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")

'''''''

źródło
Nie działa, gdy tablica ma przerwy. Działa to również przypadkowo, ponieważ indeks elementu jest równy wartości przechowywanej w tym indeksie. GetUpperBound()pobiera najwyższy możliwy indeks w tablicy zwrócony przez GetValues(), a nie najwyższą wartość przechowywaną w tej tablicy.
JensG
2

W F #, z funkcją pomocniczą, aby przekonwertować wyliczenie na sekwencję: In F #, with a helper function to convert the enum to a sequence:

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   

Uruchamiam to ...

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

when 'A : enum<'B>Nie jest wymagane przez kompilator dla definicji, ale wymagana jest ich wykorzystanie ToSeq, nawet przy pomocy ważnego typu wyliczeniowego.

Stephen Hosking
źródło
1

Nie nadaje się do użytku w każdych okolicznościach, ale często sam definiuję maksymalną wartość:

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

Oczywiście to nie zadziała, gdy używasz wartości niestandardowych w wyliczeniu, ale często może to być łatwe rozwiązanie.

yvan vander sanden
źródło
1

Użyłem następujących, gdy potrzebowałem wartości minimalnej i maksymalnej mojego wyliczenia. Po prostu ustawiłem min równe najniższej wartości wyliczenia i maksimum równe najwyższej wartości w wyliczeniu jako same wartości wyliczeniowe.

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
źródło