Bagaimana cara mengonversi dari System.Enum ke basis integer?

93

Saya ingin membuat metode umum untuk mengonversi tipe turunan System.Enum apa pun ke nilai integer yang sesuai, tanpa casting dan sebaiknya tanpa parsing string.

Misalnya, yang saya inginkan adalah seperti ini:

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

Tapi ini sepertinya tidak berhasil. Resharper melaporkan bahwa Anda tidak dapat menggunakan ekspresi tipe 'System.Enum' untuk mengetik 'int'.

Sekarang saya telah menemukan solusi ini tetapi saya lebih memilih sesuatu yang lebih efisien.

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

Ada saran?

orj
sumber
1
Saya percaya itu kompiler yang mengeluh, bukan Resharper.
Kugel
1
Belum tentu. Saya memiliki metode ekstensi pada System.Enum, dan kadang-kadang Resharper memutuskan untuk mengeluh: Tidak dapat mengonversi jenis argumen contoh 'Some.Cool.Type.That.Is.An.Enum' menjadi 'System.Enum' ketika tidak diragukan lagi adalah enum. Jika saya mengkompilasi dan menjalankan kode itu berfungsi dengan baik. Jika saya kemudian mematikan VS, meniup cache Resharper, dan menyalakannya kembali, semuanya baik-baik saja setelah pemindaian ulang selesai. Bagi saya itu semacam cache snafu. Mungkin sama baginya.
Mir
@Mir Aku punya ReSharper "mengeluh" tentang ini juga. Perbaikan yang sama untuk saya. Tidak yakin mengapa tipe ini tercampur, tapi jelas bukan kompilernya.
akousmata

Jawaban:

134

Jika Anda tidak ingin melakukan cast,

Convert.ToInt32()

bisa melakukan triknya.

Pemeran langsung (melalui (int)enumValue) tidak dimungkinkan. Catatan bahwa ini juga akan menjadi "berbahaya" karena enum dapat memiliki jenis yang mendasari yang berbeda ( int, long, byte...).

Secara lebih formal: System.Enumtidak memiliki hubungan pewarisan langsung dengan Int32(meskipun keduanya adalah ValueType), jadi pemeran eksplisit tidak dapat benar dalam sistem tipe

MartinStettner
sumber
2
Converter.ToInteger(MyEnum.MyEnumConstant);tidak akan memberi Anda kesalahan di sini. Harap edit bagian itu.
nawfal
Terima kasih, @nawfal, Anda benar. Saya mengedit tanggapan (dan belajar sesuatu yang baru :) ...)
MartinStettner
Tidak berfungsi jika jenis yang mendasarinya berbeda dariint
Alex Zhukovskiy
@YaroslavSivakov itu tidak akan berfungsi misalnya untuk longenum.
Alex Zhukovskiy
System.Enumadalah kelas, jadi ini adalah tipe referensi. Nilai mungkin dikotakkan.
Teejay
42

Saya membuatnya bekerja dengan mentransmisikan ke objek dan kemudian ke int:

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

Ini jelek dan mungkin bukan cara terbaik. Saya akan terus mengotak-atiknya, untuk melihat apakah saya bisa menghasilkan sesuatu yang lebih baik ....

EDIT: Baru saja akan memposting bahwa Convert.ToInt32 (enumValue) berfungsi juga, dan perhatikan bahwa MartinStettner mengalahkan saya untuk itu.

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

Uji:

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

EDIT 2: Di komentar, seseorang mengatakan bahwa ini hanya berfungsi di C # 3.0. Saya baru saja menguji ini di VS2005 seperti ini dan berhasil:

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));
    }
BFree
sumber
Saya memberi Anda +1 tetapi solusi ini hanya akan berfungsi dengan C # 3.0 dan lebih tinggi.
Vadim
Saya pikir, ini hanya memenuhi kompiler. Apakah Anda berhasil menguji ini saat runtime? Saya tidak dapat memberikan nilai apa pun ke fungsi ini ...
MartinStettner
Karena ini metode ekstensi? Atau ada sesuatu yang berbeda tentang enum di versi C # yang lebih lama?
BFree
Saya menambahkan tes di akhir posting saya. Saya mencobanya dan itu berhasil untuk saya.
BFree
@BFree: Tampaknya hanya berfungsi dengan metode ekstensi. Saya mencobanya dengan fungsi "gaya lama" (di C # 2.0) dan tidak berhasil menemukan sintaks untuk benar-benar meneruskan nilai apa pun ke sana (itulah komentar saya sebelumnya)
MartinStettner
14

Jika Anda perlu mengonversi enum apa pun ke tipe dasarnya (tidak semua enum didukung oleh int), Anda dapat menggunakan:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));
Drew Noakes
sumber
5
Ini adalah satu-satunya jawaban antipeluru.
Rudey
Apakah ini penyebab unboxing tinju?
b. Ben
@ b. ya. Convert.ChangeTypemenerima objectargumen pertama, dan kembali object.
Drew Noakes
Ya, saya sebenarnya setuju dengan @ Rudey dalam hal ini, bagaimanapun Anda harus melakukan tes kinerja. Solusi BFree sejauh ini adalah yang tercepat. Sekitar delapan kali lebih cepat. Pokoknya kita masih membicarakan tentang kutu.
Dua puluh
10

Mengapa Anda perlu menemukan kembali roda dengan metode helper? Sangat legal untuk memberikan enumnilai pada tipe dasarnya.

Ini kurang mengetik, dan menurut saya lebih mudah dibaca, menggunakan ...

int x = (int)DayOfWeek.Tuesday;

... daripada sesuatu seperti ...

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();
LukeH
sumber
6
Saya ingin mengonversi nilai enum APA PUN. Yaitu, saya memiliki berbagai kelas yang memiliki bidang enum yang sedang diproses oleh beberapa kode secara umum. Inilah mengapa cast sederhana ke int tidak sesuai.
orj
@ orj, saya tidak begitu yakin apa yang Anda maksud. Bisakah Anda memberi contoh yang menunjukkan penggunaan yang Anda butuhkan?
LukeH
2
Terkadang Anda tidak bisa langsung mentransmisikan enum, seperti dalam kasus metode umum. Karena metode generik tidak dapat dibatasi ke enum, Anda telah membatasinya ke sesuatu seperti struct, yang tidak dapat dimasukkan ke dalam int. Dalam hal ini, Anda dapat menggunakan Convert.ToInt32 (enum) untuk melakukannya.
Daniel T.
Saya suka gagasan bahwa metode ekstensi ToInteger () mengikuti format yang sama seperti ToString (). Saya pikir itu kurang dapat dibaca untuk melemparkan ke int di satu sisi ketika Anda menginginkan nilai int dan menggunakan ToString () ketika kita membutuhkan nilai string terutama ketika kodenya berdampingan.
Louise Eggleton
Selain itu, menggunakan metode ekstensi dapat menambah konsistensi pada basis kode Anda. Karena, seperti yang ditunjukkan @nawfal, ada beberapa cara Anda bisa mendapatkan nilai int dari enum, membuat metode ekstensi dan memaksakan penggunaannya berarti bahwa setiap kali Anda mendapatkan nilai int dari enum, Anda menggunakan metode yang sama
Louise Eggleton
4

Dari jawaban saya di sini :

Diberikan eseperti pada:

Enum e = Question.Role;

Kemudian pekerjaan ini:

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

Dua yang terakhir sangat jelek. Yang pertama harus lebih mudah dibaca, meskipun yang kedua lebih cepat. Atau mungkin metode penyuluhan adalah yang terbaik, terbaik dari kedua dunia.

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

Sekarang Anda dapat menghubungi:

e.GetValue<int>(); //or
e.GetIntValue();
nawfal
sumber
Apakah Anda yakin yang kedua lebih cepat? Saya berasumsi itu akan mengemas enum dan kemudian membuka kotak kembali ke int?
yoyo
1
@yoyo evariabel sudah memiliki contoh kotak dari tipe nilai aktual, yaitu enum. Saat Anda menulis Enum e = Question.Role;itu sudah kotak. Pertanyaannya adalah tentang mengubah kotak System.Enumkembali ke tipe int yang mendasari (unboxing). Jadi di sini unboxing hanyalah masalah kinerja. (int)(object)eadalah panggilan unbox langsung; ya harus lebih cepat dari pendekatan lain. Lihat ini
nawfal
Terima kasih untuk penjelasannya. Saya mendapat kesan yang salah bahwa instance System.Enum adalah nilai yang tidak dikotakkan. Lihat ini juga - msdn.microsoft.com/en-us/library/aa691158(v=vs.71).aspx
yoyo
@yoyo hmm ya. Kutipan yang relevan dari tautan: Dari tipe enum apa pun ke tipe System.Enum.
nawfal
1

Mentransmisi dari a System.Enumke an intberfungsi dengan baik untuk saya (juga ada di MSDN ). Mungkin itu bug Resharper.

jpoh
sumber
1
Versi runtime apa yang Anda gunakan?
JoshBerke
2
tautan menunjukkan casting sub jenis System.Enum, bukan System.Enumdirinya sendiri.
nawfal
Masalah serupa yang saya miliki adalah snafu cache Resharper. Menutup VS dan menghapus cache Resharper membuat kesalahan hilang, bahkan setelah pemindaian ulang penuh.
Mir
1

Karena Enum dibatasi untuk byte, sbyte, short, ushort, int, uint, long dan ulong, kita dapat membuat beberapa asumsi.

Kami dapat menghindari pengecualian selama konversi dengan menggunakan wadah terbesar yang tersedia. Sayangnya, wadah mana yang akan digunakan tidak jelas karena ulong akan meledak untuk angka negatif dan panjang akan meledak untuk angka antara long.MaxValue dan ulong.MaxValue. Kita perlu beralih di antara pilihan ini berdasarkan jenis yang mendasarinya.

Tentu saja, Anda masih perlu memutuskan apa yang harus dilakukan jika hasilnya tidak sesuai dengan int. Saya pikir casting tidak apa-apa, tetapi masih ada beberapa kekurangan:

  1. untuk enum berdasarkan tipe dengan ruang bidang lebih besar dari int (panjang dan ulong), beberapa enum mungkin akan dievaluasi ke nilai yang sama.
  2. mentransmisikan angka yang lebih besar dari int.MaxValue akan memunculkan pengecualian jika Anda berada di wilayah yang dicentang.

Inilah saran saya, saya akan menyerahkannya kepada pembaca untuk memutuskan di mana akan mengekspos fungsi ini; sebagai pembantu atau perpanjangan tangan.

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

Jangan lupa bahwa tipe Enum itu sendiri memiliki banyak fungsi pembantu statis di dalamnya. Jika semua yang ingin Anda lakukan adalah mengonversi instance enum ke jenis bilangan bulat yang sesuai, maka transmisi mungkin adalah cara yang paling efisien.

Saya pikir ReSharper mengeluh karena Enum bukanlah pencacahan jenis tertentu, dan pencacahan itu sendiri berasal dari jenis penilaian skalar, bukan Enum. Jika Anda memerlukan casting yang dapat disesuaikan dengan cara yang umum, saya akan mengatakan ini dapat menyesuaikan Anda dengan baik (perhatikan bahwa jenis enumerasi itu sendiri juga termasuk dalam generik:

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

Ini kemudian dapat digunakan seperti ini:

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

// ...

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

Saya tidak bisa mengatakan dengan pasti dari atas kepala saya, tetapi kode di atas bahkan mungkin didukung oleh inferensi tipe C #, memungkinkan yang berikut:

int myStoplightColor = EnumHelpers.Convert<int>(StopLight.Red);
jrista
sumber
1
Sayangnya, dalam contoh Anda, E bisa berupa tipe apa saja. Generik tidak mengizinkan saya menggunakan Enum sebagai batasan parameter tipe. Saya tidak bisa mengatakan: di mana T: Enum
Vadim
Anda bisa menggunakan di mana T: struct. Ini tidak seketat yang Anda inginkan, tetapi akan memotong semua jenis referensi (yang menurut saya itulah yang Anda coba lakukan.)
jrista
Anda dapat menggunakan: if (! typeof (E) .IsEnum) melempar ArgumentException baru ("E harus berupa jenis enumerasi");
diadiora
1
Ini bahkan tidak benar, pembantu statis bahkan tidak valid.
Nicholi
1
Mengapa seseorang harus menggunakan EnumHelpers.Convert<int, StopLight>(StopLight.Red);daripada (int)StopLight.Read;? Juga pertanyaan yang sedang dibicarakan System.Enum.
nawfal