Saya membangun fungsi untuk memperluas Enum.Parse
konsep itu
- Mengizinkan nilai default diurai jika nilai Enum tidak ditemukan
- Tidak sensitif huruf
Jadi saya menulis yang berikut:
public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
Saya mendapatkan Galat Kendala tidak boleh kelas khusus System.Enum
.
Cukup adil, tetapi apakah ada solusi untuk memungkinkan Generic Enum, atau saya harus meniru Parse
fungsi dan meneruskan tipe sebagai atribut, yang memaksa persyaratan tinju jelek ke kode Anda.
Sunting Semua saran di bawah ini sangat kami hargai, terima kasih.
Telah diselesaikan (Saya telah meninggalkan loop untuk mempertahankan ketidakpekaan huruf - Saya menggunakan ini saat mem-parsing XML)
public static class EnumUtils
{
public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
{
if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
}
EDIT: (16 Feb 2015) Julien Lebosquain baru-baru ini memposting solusi generik aman jenis-kompiler yang ditegakkan dalam MSIL atau F # di bawah ini, yang layak untuk dilihat, dan upvote. Saya akan menghapus hasil edit ini jika solusinya menggelembung di atas halaman.
Jawaban:
Karena
Enum
Type mengimplementasikanIConvertible
antarmuka, implementasi yang lebih baik harus seperti ini:Ini masih akan mengizinkan berlalunya jenis nilai implementasi
IConvertible
. Kemungkinannya jarang.sumber
Fitur ini akhirnya didukung di C # 7.3!
Cuplikan berikut (dari sampel dotnet ) menunjukkan caranya:
Pastikan untuk mengatur versi bahasa Anda dalam proyek C # Anda ke versi 7.3.
Jawaban Asli di bawah ini:
Saya terlambat ke pertandingan, tetapi saya menganggapnya sebagai tantangan untuk melihat bagaimana itu bisa dilakukan. Itu tidak mungkin dalam C # (atau VB.NET, tetapi gulir ke bawah untuk F #), tetapi dimungkinkan dalam MSIL. Saya menulis .... hal kecil ini
Yang menghasilkan fungsi yang akan terlihat seperti ini, jika itu valid C #:
Kemudian dengan kode C # berikut:
Sayangnya, ini berarti bagian kode Anda ini ditulis dalam MSIL dan bukan C #, dengan satu-satunya manfaat tambahan adalah Anda dapat membatasi metode ini
System.Enum
. Ini juga agak menyebalkan, karena akan dikompilasi menjadi rakitan terpisah. Namun, itu tidak berarti Anda harus menggunakannya dengan cara itu.Dengan menghapus garis
.assembly MyThing{}
dan memohon ilasm sebagai berikut:Anda mendapatkan netmodule alih-alih perakitan.
Sayangnya, VS2010 (dan sebelumnya, jelas) tidak mendukung penambahan referensi netmodule, yang berarti Anda harus meninggalkannya dalam 2 rakitan terpisah ketika Anda sedang melakukan debug. Satu-satunya cara Anda dapat menambahkannya sebagai bagian dari perakitan Anda adalah dengan menjalankan csc.exe sendiri menggunakan
/addmodule:{files}
argumen baris perintah. Tidak mungkin terlalu menyakitkan dalam skrip MSBuild. Tentu saja, jika Anda berani atau bodoh, Anda dapat menjalankan csc sendiri secara manual setiap kali. Dan itu tentu saja menjadi lebih rumit karena banyak majelis membutuhkan akses ke sana.Jadi, BISA dilakukan di .Net. Apakah ini sepadan dengan usaha ekstra? Um, well, kurasa aku akan membiarkanmu memutuskan yang itu.
F # Solusi sebagai alternatif
Kredit Ekstra: Ternyata pembatasan generik
enum
mungkin dilakukan pada setidaknya satu bahasa .NET selain MSIL: F #.Yang ini lebih mudah dipertahankan karena ini adalah bahasa yang terkenal dengan dukungan Visual Studio IDE penuh, tetapi Anda masih memerlukan proyek terpisah dalam solusi Anda untuk itu. Namun, secara alami menghasilkan IL yang sangat berbeda (kode ini sangat berbeda) dan bergantung pada
FSharp.Core
perpustakaan, yang, seperti perpustakaan eksternal lainnya, perlu menjadi bagian dari distribusi Anda.Inilah cara Anda dapat menggunakannya (pada dasarnya sama dengan solusi MSIL), dan untuk menunjukkan bahwa itu gagal dengan benar pada struct yang identik:
sumber
There's no particularly unusual reason why not; we have lots of other things to do, limited budgets, and this one has never made it past the "wouldn't this be nice?" discussion in the language design team.
T
untukSystem.Enum
tidak akan mampu melakukan semua hal denganT
yang orang mungkin harapkan, penulis C # pikir mereka mungkin juga melarang sama sekali. Saya menganggap keputusan ini tidak menguntungkan, karena C # hanya mengabaikan penanganan khususSystem.Enum
kendala, akan mungkin untuk menulisHasAnyFlags<T>(this T it, T other)
metode ekstensi yang urutan besarnya lebih cepat daripadaEnum.HasFlag(Enum)
jenis yang memeriksa argumennya.C # ≥ 7.3
Dimulai dengan C # 7.3 (tersedia dengan Visual Studio 2017 ≥ v15.7), kode ini sekarang benar-benar valid:
C # ≤ 7.2
Anda dapat memiliki kompiler enum constraint yang diberlakukan dengan menyalahgunakan pewarisan kendala. Kode berikut menentukan a
class
dan sekaligusstruct
kendala:Pemakaian:
Catatan: ini secara khusus dinyatakan dalam spesifikasi bahasa C # 5.0:
sumber
EnumClassUtils<System.Enum>
cukup untuk membatasi T untuk semuaSystem.Enum
jenis turunan.struct
diParse
kemudian membatasi lebih jauh ke tipe enum nyata. Anda perlu membatasi hinggaEnum
titik tertentu. Untuk melakukannya, kelas Anda harus disarangkan. Lihat gist.github.com/MrJul/7da12f5f2d6c69f03d79where TClass : class
kendala di sini?enum DefinitelyNotAnInt : byte { Realize, That, I, Am, Not, An, Int }
enum AlsoNotAnInt : long { Well, Bummer }
Edit
Pertanyaan itu sekarang luar biasa dijawab oleh Julien Lebosquain . Saya juga ingin memperluas jawabannya dengan
ignoreCase
,defaultValue
dan argumen opsional, sambil menambahkanTryParse
danParseOrDefault
.Contoh penggunaan:
Tua
Perbaikan lama saya pada jawaban Vivek dengan menggunakan komentar dan perkembangan 'baru':
TEnum
untuk kejelasan bagi penggunaTryParse
peganganignoreCase
dengan parameter yang ada (diperkenalkan pada VS2010 / .Net 4)default
nilai (diperkenalkan di VS2005 / Net 2)defaultValue
danignoreCase
yang menghasilkan:
sumber
Anda bisa mendefinisikan konstruktor statis untuk kelas yang akan memeriksa bahwa tipe T adalah enum dan melemparkan pengecualian jika tidak. Ini adalah metode yang disebutkan oleh Jeffery Richter dalam bukunya CLR via C #.
Kemudian dalam metode parse, Anda bisa menggunakan Enum.Parse (typeof (T), input, true) untuk mengkonversi dari string ke enum. Parameter benar terakhir adalah untuk mengabaikan kasus input.
sumber
Enum
T
ketika konstruktor dijalankan. Meskipun ini jauh lebih bagus daripada menunggu contoh konstruktor.Juga harus dipertimbangkan bahwa sejak pelepasan C # 7.3 menggunakan batasan Enum didukung di luar kotak tanpa harus melakukan pemeriksaan dan hal-hal tambahan.
Jadi terus maju dan mengingat Anda telah mengubah versi bahasa proyek Anda menjadi C # 7.3 kode berikut akan berfungsi dengan baik:
Jika Anda tidak tahu cara mengubah versi bahasa ke C # 7.3 lihat tangkapan layar berikut:
EDIT 1 - Diperlukan Versi Visual Studio dan mempertimbangkan ReSharper
Agar Visual Studio mengenali sintaks baru, Anda memerlukan setidaknya versi 15.7. Anda dapat menemukan yang juga disebutkan dalam catatan rilis Microsoft, lihat Visual Studio 2017 15.7 Catatan Rilis . Terima kasih @MohamedElshawaf untuk menunjukkan pertanyaan yang valid ini.
Tolong juga perhatikan bahwa dalam kasus saya ReSharper 2018.1 saat penulisan EDIT ini belum mendukung C # 7.3. Setelah ReSharper mengaktifkannya menyoroti kendala Enum sebagai kesalahan yang memberitahukan saya Tidak dapat menggunakan 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'objek' sebagai batasan parameter tipe . ReSharper menyarankan sebagai perbaikan cepat untuk menghapus batasan 'Enum' dari tipe paramter T metode
Namun, jika Anda mematikan ReSharper sementara di bawah Tools -> Options -> ReSharper Ultimate -> General Anda akan melihat bahwa sintaksinya baik-baik saja mengingat Anda menggunakan VS 15.7 atau lebih tinggi dan C # 7.3 atau lebih tinggi.
sumber
where T : struct, Enum
, untuk menghindari melewatkanSystem.Enum
dirinya sebagai parameter tipe.struct, Enum
. Dasar pemikiran saya dijelaskan dalam jawaban dan komentar di sini .Saya memodifikasi sampel dengan dimarzionist. Versi ini hanya akan bekerja dengan Enums dan tidak membiarkan struct melewati.
sumber
Saya mencoba sedikit memperbaiki kode:
sumber
defaultValue.ToString("D", System.Globalization.NumberFormatInfo.CurrentInfo)
meskipun Anda tidak tahu jenis enum apa itu, hanya saja objeknya adalah enum.IsDefined
akan merusak ketidaksensitifan kasus. Tidak sepertiParse
,IsDefined
tidak memilikiignoreCase
argumen, dan MSDN mengatakan itu hanya cocok dengan kasus yang tepat .Saya memang memiliki persyaratan khusus di mana saya harus menggunakan enum dengan teks yang terkait dengan nilai enum. Sebagai contoh ketika saya menggunakan enum untuk menentukan jenis kesalahan itu diperlukan untuk menjelaskan detail kesalahan.
sumber
Semoga ini bermanfaat:
sumber
return (TValue)Enum.Parse(typeof (TValue), value);
denganreturn (TValue)Enum.Parse(typeof (TValue), value, true);
Cukup menarik, ternyata ini dimungkinkan di bahasa lain (Managed C ++, IL langsung).
Kutipan:
Siapa tahu
sumber
Ini adalah pendapat saya. Digabungkan dari jawaban dan MSDN
Sumber MSDN
sumber
TEnum
sebenarnya adalah tipe Enum tetapitext
adalah string kosong maka Anda mendapatkanArgumentException
pepatah "TEnum harus menjadi tipe Enum" meskipun itu.Jawaban yang ada benar pada C # <= 7.2. Namun, ada permintaan fitur bahasa C # (terkait dengan permintaan fitur corefx ) untuk memungkinkan hal berikut;
Pada saat penulisan, fitur ini adalah "Dalam diskusi" di Pertemuan Pengembangan Bahasa.
EDIT
Sesuai info nawfal , ini sedang diperkenalkan di C # 7.3 .
sumber
Saya selalu menyukai ini (Anda dapat memodifikasi sesuai kebutuhan):
sumber
Saya menyukai solusi Christopher Currens menggunakan IL tetapi bagi mereka yang tidak ingin berurusan dengan bisnis rumit termasuk MSIL ke dalam proses pembangunan mereka, saya menulis fungsi serupa di C #.
Harap perhatikan bahwa Anda tidak dapat menggunakan pembatasan umum seperti
where T : Enum
karena Enum adalah tipe khusus. Karena itu saya harus memeriksa apakah tipe generik yang diberikan benar-benar enum.Fungsi saya adalah:
sumber
Saya telah merangkum solusi Vivek ke dalam kelas utilitas yang dapat Anda gunakan kembali. Harap dicatat bahwa Anda masih harus mendefinisikan batasan tipe "di mana T: struct, IConvertible" pada tipe Anda.
sumber
Saya membuat Metode ekstensi
to get integer value from enum
lihat implementasi metodeini adalah penggunaan
sumber
Sebagaimana dinyatakan dalam jawaban lain sebelumnya; sementara ini tidak dapat diekspresikan dalam kode-sumber itu sebenarnya dapat dilakukan pada IL Level. @Christopher Currens menjawab menunjukkan bagaimana IL lakukan untuk itu.
Dengan Fody 's Add-In ExtraConstraints.Fody ada cara yang sangat sederhana, lengkap dengan build-tooling, untuk mencapai ini. Cukup tambahkan paket nuget mereka (
Fody
,ExtraConstraints.Fody
) ke proyek Anda dan tambahkan kendala sebagai berikut (Kutipan dari Readme of ExtraConstraints):dan Fody akan menambahkan IL yang diperlukan agar kendala hadir. Perhatikan juga fitur tambahan dari delegasi yang membatasi:
Mengenai Enum, Anda mungkin juga ingin mencatat Enums.NET yang sangat menarik .
sumber
Ini implementasi saya. Pada dasarnya, Anda dapat mengatur atribut apa saja dan berfungsi.
sumber
Jika boleh menggunakan casting langsung setelah itu, saya kira Anda dapat menggunakan
System.Enum
kelas dasar dalam metode Anda, di mana pun diperlukan. Anda hanya perlu mengganti parameter tipe dengan hati-hati. Jadi implementasi metode akan seperti:Maka Anda bisa menggunakannya seperti:
sumber
Enum.ToObject()
akan menghasilkan hasil yang lebih fleksibel. Selain itu, Anda dapat melakukan perbandingan string tanpa sensitivitas case yang akan meniadakan kebutuhan untuk memanggilToLower()
Hanya untuk kelengkapan, berikut ini adalah solusi Java. Saya yakin hal yang sama bisa dilakukan di C # juga. Ini menghindari keharusan untuk menentukan jenis di mana saja dalam kode - sebagai gantinya, Anda menentukannya di string yang Anda coba parsing.
Masalahnya adalah bahwa tidak ada cara untuk mengetahui enumerasi mana yang cocok dengan String - jadi jawabannya adalah untuk menyelesaikan masalah itu.
Alih-alih hanya menerima nilai string, terima String yang memiliki enumerasi dan nilai dalam bentuk "enumeration.value". Kode kerjanya di bawah ini - membutuhkan Java 1.8 atau yang lebih baru. Ini juga akan membuat XML lebih tepat karena Anda akan melihat sesuatu seperti color = "Color.red" daripada hanya color = "red".
Anda akan memanggil metode acceptEnumeratedValue () dengan string yang berisi nama nilai titik enum nama.
Metode mengembalikan nilai enumerasi formal.
sumber