Come convertire da System.Enum a intero di base?


93

Vorrei creare un metodo generico per convertire qualsiasi tipo derivato System.Enum nel suo valore intero corrispondente, senza eseguire il cast e preferibilmente senza analizzare una stringa.

Ad esempio, quello che voglio è qualcosa del genere:

// Trivial example, not actually what I'm doing.
class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        (int)anEnum;
    }
}

Ma questo non sembra funzionare. Resharper segnala che non è possibile eseguire il cast di un'espressione di tipo "System.Enum" nel tipo "int".

Ora ho escogitato questa soluzione, ma preferisco qualcosa di più efficiente.

class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        return int.Parse(anEnum.ToString("d"));
    }
}

Eventuali suggerimenti?


1
Credo che sia il compilatore a lamentarsi, non il resharper.
Kugel

1
Non necessariamente. Ho un metodo di estensione su System.Enum e occasionalmente Resharper decide di lamentarsi: Impossibile convertire il tipo di argomento di istanza 'Some.Cool.Type.That.Is.An.Enum' in 'System.Enum' quando è indiscutibilmente un'enumerazione. Se compilo ed eseguo il codice funziona bene. Se poi spengo VS, soffio via la cache del resharper e lo riaccendo, va tutto bene una volta eseguita la nuova scansione. Per me è una specie di cache snafu. Potrebbe essere lo stesso per lui.
Mir

@ Mir ho avuto ReSharper "lamentarsi" anche su questo. Stessa soluzione per me. Non sono sicuro del motivo per cui si confondono questi tipi, ma sicuramente non è il compilatore.
akousmata

Risposte:


134

Se non vuoi trasmettere,

Convert.ToInt32()

potrebbe fare il trucco.

Il cast diretto (via (int)enumValue) non è possibile. Si noti che questo sarebbe anche "pericoloso", in quanto un enum può avere diversi tipi di base ( int, long, byte...).

Più formalmente: System.Enumnon ha una relazione di ereditarietà diretta con Int32(sebbene entrambi siano ValueTypes), quindi il cast esplicito non può essere corretto all'interno del sistema di tipi


2
Converter.ToInteger(MyEnum.MyEnumConstant);non ti darà alcun errore qui. Si prega di modificare quella parte.
nawfal

Grazie, @nawfal, hai ragione. Ho modificato la risposta (e ho imparato qualcosa di nuovo :) ...)
MartinStettner

Non funziona se il tipo di fondo è diverso daint
Alex Zhukovskiy

@YaroslavSivakov non funzionerà ad esempio per longenum.
Alex Zhukovskiy

System.Enumè una classe, quindi è un tipo di riferimento. I valori sono probabilmente inscatolati.
Teejay

42

L'ho fatto funzionare eseguendo il casting su un oggetto e poi su un int:

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return (int)((object)enumValue);
    }
}

Questo è brutto e probabilmente non è il modo migliore. Continuerò a giocarci, per vedere se riesco a trovare qualcosa di meglio ...

EDIT: Stavo per postare che Convert.ToInt32 (enumValue) funziona e ho notato che MartinStettner mi ha battuto.

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

Test:

int x = DayOfWeek.Friday.ToInt();
Console.WriteLine(x); // results in 5 which is int value of Friday

EDIT 2: nei commenti, qualcuno ha detto che funziona solo in C # 3.0. L'ho appena testato in VS2005 in questo modo e ha funzionato:

public static class Helpers
{
    public static int ToInt(Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

    static void Main(string[] args)
    {
        Console.WriteLine(Helpers.ToInt(DayOfWeek.Friday));
    }

Ti ho dato +1 ma questa soluzione funzionerà solo con C # 3.0 e versioni successive.
Vadim

Penso che questo soddisfi solo il compilatore. Sei riuscito a testarlo in fase di esecuzione? Non sono riuscito a trasferire alcun valore a questa funzione ...
MartinStettner,

Perché è un metodo di estensione? O c'è qualcosa di diverso nelle enumerazioni nelle versioni precedenti di C #?
BFree

Ho aggiunto un test alla fine del mio post. Ho provato e ha funzionato per me.
BFree

@BFree: sembra funzionare solo con metodi di estensione. L'ho provato con una funzione "vecchio stile" (in C # 2.0) e non sono riuscito a trovare la sintassi per passare effettivamente alcun valore (questo è ciò di cui
parlava

14

Se è necessario convertire qualsiasi enumerazione nel tipo sottostante (non tutte le enumerazioni sono supportate da int), è possibile utilizzare:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));

5
Questa è l'unica risposta a prova di proiettile.
Rudey

Questa è la causa della boxe unboxing?
b.ben

@ b.ben sì. Convert.ChangeTypeaccetta objectper il primo argomento e restituisce object.
Drew Noakes

Sì, in realtà sono d'accordo con @Rudey su questo, comunque dovresti fare test delle prestazioni. La soluzione di BFree è di gran lunga la più rapida. Circa otto volte più veloce. Comunque stiamo ancora parlando di zecche.
Venti

10

Perché hai bisogno di reinventare la ruota con un metodo di supporto? È perfettamente legale enumattribuire un valore al suo tipo sottostante.

È meno digitazione e, a mio parere, più leggibile, da usare ...

int x = (int)DayOfWeek.Tuesday;

... piuttosto che qualcosa di simile ...

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();

6
Voglio convertire QUALSIASI valore enumerativo. Cioè, ho una varietà di classi che hanno campi enum che vengono elaborati da un codice in modo generico. Questo è il motivo per cui un semplice cast a int non è appropriato.
orj

@ orj, non sono proprio sicuro di cosa intendi. Puoi fare un esempio che mostri l'utilizzo di cui hai bisogno?
Luca

2
A volte non è possibile eseguire il cast direttamente di un'enumerazione, come nel caso di un metodo generico. Poiché i metodi generici non possono essere vincolati alle enumerazioni, è necessario vincolarlo a qualcosa come struct, che non può essere cast in un int. In tal caso, puoi usare Convert.ToInt32 (enum) per farlo.
Daniel T.

Mi piace l'idea che il metodo di estensione ToInteger () segua lo stesso formato di ToString (). Penso che sia meno leggibile eseguire il cast su int da un lato quando si desidera il valore int e utilizzare ToString () quando abbiamo bisogno del valore stringa, specialmente quando il codice è fianco a fianco.
Louise Eggleton

Inoltre, l'utilizzo di un metodo di estensione può aggiungere coerenza al codice di base. Poiché, come ha sottolineato @nawfal, ci sono diversi modi in cui puoi ottenere il valore int da un'enumerazione, creare un metodo di estensione e imporne l'uso significa che ogni volta che ottieni il valore int di un'enumerazione stai usando lo stesso metodo
Louise Eggleton

4

Dalla mia risposta qui :

Dato ecome in:

Enum e = Question.Role;

Quindi questi funzionano:

int i = Convert.ToInt32(e);
int i = (int)(object)e;
int i = (int)Enum.Parse(e.GetType(), e.ToString());
int i = (int)Enum.ToObject(e.GetType(), e);

Gli ultimi due sono semplicemente brutti. Il primo dovrebbe essere più leggibile, anche se il secondo è molto più veloce. O può essere un metodo di estensione è il migliore, il migliore di entrambi i mondi.

public static int GetIntValue(this Enum e)
{
    return e.GetValue<int>();
}

public static T GetValue<T>(this Enum e) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
{
    return (T)(object)e;
}

Adesso puoi chiamare:

e.GetValue<int>(); //or
e.GetIntValue();

Sei sicuro che il secondo sia più veloce? Presumo che inscatolerà l'enumerazione e quindi unbox di nuovo a int?
yoyo

1
@yoyo la evariabile ha già l'istanza boxed del tipo di valore effettivo, cioè enum. Quando scrivi Enum e = Question.Role;è già inscatolato. La domanda riguarda la riconversione del boxed System.Enumal sottostante tipo int (unboxing). Quindi qui l'unboxing è solo la preoccupazione delle prestazioni. (int)(object)eè una chiamata unbox diretta; sì dovrebbe essere più veloce di altri approcci. Vedi questo
nawfal

Grazie per la spiegazione. Avevo l'impressione errata che un'istanza di System.Enum fosse un valore unboxed. Vedi anche questo - msdn.microsoft.com/en-us/library/aa691158(v=vs.71).aspx
yoyo

@yoyo hmm sì. La citazione pertinente dal collegamento: Da qualsiasi tipo enum al tipo System.Enum.
nawfal

1

Il casting da a System.Enuma an intfunziona bene per me (è anche su MSDN ). Forse è un bug di Resharper.


1
Quale versione del runtime stai utilizzando?
JoshBerke

2
il collegamento mostra il casting di sottotipi di System.Enum, non System.Enumse stesso.
nawfal

Un problema simile che ho avuto è stato un problema con la cache Resharper. La chiusura di VS e l'eliminazione della cache Resharper hanno fatto scomparire l'errore, anche dopo una nuova scansione completa.
Mir

1

Poiché gli enum sono limitati a byte, sbyte, short, ushort, int, uint, long e ulong, possiamo fare alcune ipotesi.

Possiamo evitare eccezioni durante la conversione utilizzando il contenitore più grande disponibile. Sfortunatamente, quale contenitore utilizzare non è chiaro perché ulong esploderà per numeri negativi e long per numeri compresi tra long.MaxValue e ulong.MaxValue. Dobbiamo passare da una scelta all'altra in base al tipo sottostante.

Ovviamente, devi ancora decidere cosa fare quando il risultato non rientra in un int. Penso che il casting vada bene, ma ci sono ancora alcuni trucchi:

  1. per le enumerazioni basate su un tipo con uno spazio di campo più grande di int (long e ulong), è possibile che alcune enumerazioni restituiscano lo stesso valore.
  2. il cast di un numero maggiore di int.MaxValue genererà un'eccezione se ti trovi in ​​una regione selezionata.

Ecco il mio suggerimento, lascio al lettore decidere dove esporre questa funzione; come aiutante o come estensione.

public int ToInt(Enum e)
{
  unchecked
  {
    if (e.GetTypeCode() == TypeCode.UInt64)
      return (int)Convert.ToUInt64(e);
    else
      return (int)Convert.ToInt64(e);
  }
}

-1

Non dimenticare che il tipo Enum stesso contiene un sacco di funzioni di supporto statiche. Se tutto ciò che vuoi fare è convertire un'istanza di enum nel suo tipo intero corrispondente, il casting è probabilmente il modo più efficiente.

Penso che ReSharper si stia lamentando perché Enum non è un'enumerazione di alcun tipo particolare e le enumerazioni stesse derivano da un tipo di valore scalare, non da Enum. Se hai bisogno di casting adattabili in modo generico, direi che questo potrebbe adattarti bene (nota che il tipo di enumerazione stesso è incluso anche nel generico:

public static EnumHelpers
{
    public static T Convert<T, E>(E enumValue)
    {
        return (T)enumValue;
    }
}

Questo potrebbe quindi essere utilizzato in questo modo:

public enum StopLight: int
{
    Red = 1,
    Yellow = 2,
    Green = 3
}

// ...

int myStoplightColor = EnumHelpers.Convert<int, StopLight>(StopLight.Red);

Non posso dirlo con certezza dalla cima della mia testa, ma il codice sopra potrebbe persino essere supportato dall'inferenza del tipo di C #, consentendo quanto segue:

int myStoplightColor = EnumHelpers.Convert<int>(StopLight.Red);

1
Sfortunatamente, nel tuo esempio E può essere qualsiasi tipo. I generici non mi consentono di utilizzare Enum come vincolo di parametro di tipo. Non posso dire: dove T: Enum
Vadim

Potresti usare where T: struct, però. Non è così rigoroso come vorresti che fosse, ma
eliminerebbe

puoi usare: if (! typeof (E) .IsEnum) throw new ArgumentException ("E deve essere un tipo enumerato");
diadiora

1
Questo non è nemmeno lontanamente corretto, l'helper statico non è nemmeno valido.
Nicholi

1
Perché qualcuno dovrebbe usare EnumHelpers.Convert<int, StopLight>(StopLight.Red);invece di (int)StopLight.Read;? Anche la domanda sta parlando System.Enum.
nawfal
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.