Yang ingin saya lakukan adalah seperti ini: Saya memiliki enum dengan gabungan nilai yang ditandai.
public static class EnumExtension
{
public static bool IsSet<T>( this T input, T matchTo )
where T:enum //the constraint I want that doesn't exist in C#3
{
return (input & matchTo) != 0;
}
}
Jadi saya bisa melakukan:
MyEnum tester = MyEnum.FlagA | MyEnum.FlagB
if( tester.IsSet( MyEnum.FlagA ) )
//act on flag a
Sayangnya, C # generik dimana batasan tidak memiliki batasan enum, hanya class dan struct. C # tidak melihat enum sebagai struct (meskipun mereka adalah tipe nilai) jadi saya tidak bisa menambahkan tipe ekstensi seperti ini.
Apakah ada yang tahu solusinya?
struct
baik.Jawaban:
EDIT: Ini sekarang tayang dalam versi 0.0.0.2 dari UnconstrainedMelody.
(Seperti yang diminta di posting blog saya tentang kendala enum . Saya telah menyertakan fakta dasar di bawah ini demi jawaban yang berdiri sendiri.)
Solusi terbaik adalah menunggu saya memasukkannya ke dalam UnconstrainedMelody 1 . Ini adalah pustaka yang mengambil kode C # dengan batasan "palsu" seperti
where T : struct, IEnumConstraint
dan mengubahnya menjadi
where T : struct, System.Enum
melalui langkah postbuild.
Seharusnya tidak terlalu sulit untuk menulis
IsSet
... meskipun melayani bendera berbasisInt64
danUInt64
berbasis bisa menjadi bagian yang sulit. (Saya mencium beberapa metode pembantu yang akan datang, pada dasarnya memungkinkan saya untuk memperlakukan setiap flag enum seolah-olah itu memiliki tipe dasarUInt64
.)Perilaku apa yang Anda inginkan jika dipanggil
? Haruskah itu memeriksa bahwa semua bendera yang ditentukan disetel? Itulah harapan saya.
Saya akan mencoba melakukan ini dalam perjalanan pulang malam ini ... Saya berharap mendapatkan kilasan cepat tentang metode enum yang berguna untuk membuat perpustakaan mencapai standar yang dapat digunakan dengan cepat, lalu bersantai sedikit.
EDIT: Omong
IsSet
-omong, saya tidak yakin tentang nama. Pilihan:Pikiran diterima. Aku yakin itu akan memakan waktu sebelum apapun menjadi batu ...
1 atau kirimkan sebagai tambalan, tentu saja ...
sumber
colors.HasAny(Colors.Red | Colors.Blue)
Sepertinya kode yang sangat mudah dibaca.=)
where T : System.Enum
. Ini sudah ditulis di tempat lain di utas; hanya berpikir saya akan mengulanginya di sini.Mulai C # 7.3, sekarang ada cara built-in untuk menambahkan batasan enum:
public class UsingEnum<T> where T : System.Enum { }
sumber: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint
sumber
Darren, itu akan berfungsi jika jenisnya adalah pencacahan khusus - agar pencacahan umum berfungsi, Anda harus memasukkannya ke int (atau lebih mungkin uint) untuk melakukan matematika boolean:
public static bool IsSet( this Enum input, Enum matchTo ) { return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0; }
sumber
Convert.ToUInt32
saya tidak menemukan tempat lain. AFAIK, Ini satu-satunya solusi Pre-Net-4 yang layak yang juga berfungsi di VB. BTW, jikamatchTo
mungkin memiliki beberapa bit bendera, maka ganti!= 0
dengan== Convert.ToUInt32(matchTo)
.Convert.ToUInt32
digunakan dengan enum akan menggunakanConvert.ToUInt32(object)
overload, yang berarti bahwa CLR akan mengemas nilai-nilai ini terlebih dahulu sebelum meneruskannya keToUInt32
metode. Dalam kebanyakan kasus, ini tidak masalah, tetapi ada baiknya mengetahui bahwa Anda akan membuat GC agak sibuk jika Anda menggunakan sesuatu seperti ini untuk mengurai jutaan enum per detik.Sebenarnya, itu mungkin, dengan trik yang jelek. Namun, ini tidak dapat digunakan untuk metode ekstensi.
public abstract class Enums<Temp> where Temp : class { public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp { return (TEnum)Enum.Parse(typeof(TEnum), name); } } public abstract class Enums : Enums<Enum> { } Enums.IsSet<DateTimeKind>("Local")
Jika mau, Anda bisa memberikan
Enums<Temp>
konstruktor privat dan kelas warisan abstrak bersarang publik denganTemp
asEnum
, untuk mencegah versi turunan untuk non-enum.sumber
Anda dapat mencapai ini menggunakan IL Weaving dan ExtraConstraints
Memungkinkan Anda menulis kode ini
public class Sample { public void MethodWithDelegateConstraint<[DelegateConstraint] T> () { } public void MethodWithEnumConstraint<[EnumConstraint] T>() { } }
Apa yang dikompilasi
public class Sample { public void MethodWithDelegateConstraint<T>() where T: Delegate { } public void MethodWithEnumConstraint<T>() where T: struct, Enum { } }
sumber
Mulai C # 7.3, Anda dapat menggunakan batasan Enum pada tipe generik:
public static TEnum Parse<TEnum>(string value) where TEnum : Enum { return (TEnum) Enum.Parse(typeof(TEnum), value); }
Jika Anda ingin menggunakan Nullable enum, Anda harus meninggalkan batasan struct orginial:
public static TEnum? TryParse<TEnum>(string value) where TEnum : struct, Enum { if( Enum.TryParse(value, out TEnum res) ) return res; else return null; }
sumber
Ini tidak menjawab pertanyaan awal, tetapi sekarang ada metode di .NET 4 yang disebut Enum.HasFlag yang melakukan apa yang Anda coba lakukan dalam contoh Anda
sumber
flag
. .NET 4.0 adalah lima tahun sekarang.Cara saya melakukannya adalah dengan meletakkan batasan struct, kemudian memeriksa bahwa T adalah enum saat runtime. Ini tidak menghilangkan masalah sepenuhnya, tetapi sedikit menguranginya
sumber
Menggunakan kode asli Anda, di dalam metode ini Anda juga dapat menggunakan refleksi untuk menguji bahwa T adalah enum:
public static class EnumExtension { public static bool IsSet<T>( this T input, T matchTo ) { if (!typeof(T).IsEnum) { throw new ArgumentException("Must be an enum", "input"); } return (input & matchTo) != 0; } }
sumber
Berikut beberapa kode yang baru saja saya lakukan yang tampaknya berfungsi seperti yang Anda inginkan tanpa harus melakukan sesuatu yang terlalu gila. Ini tidak terbatas hanya pada enum yang disetel sebagai Flags, tetapi selalu ada pemeriksaan yang dimasukkan jika perlu.
public static class EnumExtensions { public static bool ContainsFlag(this Enum source, Enum flag) { var sourceValue = ToUInt64(source); var flagValue = ToUInt64(flag); return (sourceValue & flagValue) == flagValue; } public static bool ContainsAnyFlag(this Enum source, params Enum[] flags) { var sourceValue = ToUInt64(source); foreach (var flag in flags) { var flagValue = ToUInt64(flag); if ((sourceValue & flagValue) == flagValue) { return true; } } return false; } // found in the Enum class as an internal method private static ulong ToUInt64(object value) { switch (Convert.GetTypeCode(value)) { case TypeCode.SByte: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: return (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture); case TypeCode.Byte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: return Convert.ToUInt64(value, CultureInfo.InvariantCulture); } throw new InvalidOperationException("Unknown enum type."); } }
sumber
jika seseorang membutuhkan IsSet generik (dibuat di luar kotak dengan cepat dapat ditingkatkan), dan atau string ke konversi Enum onfly (yang menggunakan EnumConstraint yang disajikan di bawah):
public class TestClass { } public struct TestStruct { } public enum TestEnum { e1, e2, e3 } public static class TestEnumConstraintExtenssion { public static bool IsSet<TEnum>(this TEnum _this, TEnum flag) where TEnum : struct { return (((uint)Convert.ChangeType(_this, typeof(uint))) & ((uint)Convert.ChangeType(flag, typeof(uint)))) == ((uint)Convert.ChangeType(flag, typeof(uint))); } //public static TestClass ToTestClass(this string _this) //{ // // #generates compile error (so no missuse) // return EnumConstraint.TryParse<TestClass>(_this); //} //public static TestStruct ToTestStruct(this string _this) //{ // // #generates compile error (so no missuse) // return EnumConstraint.TryParse<TestStruct>(_this); //} public static TestEnum ToTestEnum(this string _this) { // #enum type works just fine (coding constraint to Enum type) return EnumConstraint.TryParse<TestEnum>(_this); } public static void TestAll() { TestEnum t1 = "e3".ToTestEnum(); TestEnum t2 = "e2".ToTestEnum(); TestEnum t3 = "non existing".ToTestEnum(); // default(TestEnum) for non existing bool b1 = t3.IsSet(TestEnum.e1); // you can ommit type bool b2 = t3.IsSet<TestEnum>(TestEnum.e2); // you can specify explicite type TestStruct t; // #generates compile error (so no missuse) //bool b3 = t.IsSet<TestEnum>(TestEnum.e1); } }
Jika seseorang masih membutuhkan contoh panas untuk membuat kendala pengkodean Enum:
using System; /// <summary> /// would be same as EnumConstraint_T<Enum>Parse<EnumType>("Normal"), /// but writen like this it abuses constrain inheritence on System.Enum. /// </summary> public class EnumConstraint : EnumConstraint_T<Enum> { } /// <summary> /// provides ability to constrain TEnum to System.Enum abusing constrain inheritence /// </summary> /// <typeparam name="TClass">should be System.Enum</typeparam> public abstract class EnumConstraint_T<TClass> where TClass : class { public static TEnum Parse<TEnum>(string value) where TEnum : TClass { return (TEnum)Enum.Parse(typeof(TEnum), value); } public static bool TryParse<TEnum>(string value, out TEnum evalue) where TEnum : struct, TClass // struct is required to ignore non nullable type error { evalue = default(TEnum); return Enum.TryParse<TEnum>(value, out evalue); } public static TEnum TryParse<TEnum>(string value, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // struct is required to ignore non nullable type error { Enum.TryParse<TEnum>(value, out defaultValue); return defaultValue; } public static TEnum Parse<TEnum>(string value, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // struct is required to ignore non nullable type error { TEnum result; if (Enum.TryParse<TEnum>(value, out result)) return result; return defaultValue; } public static TEnum Parse<TEnum>(ushort value) { return (TEnum)(object)value; } public static sbyte to_i1<TEnum>(TEnum value) { return (sbyte)(object)Convert.ChangeType(value, typeof(sbyte)); } public static byte to_u1<TEnum>(TEnum value) { return (byte)(object)Convert.ChangeType(value, typeof(byte)); } public static short to_i2<TEnum>(TEnum value) { return (short)(object)Convert.ChangeType(value, typeof(short)); } public static ushort to_u2<TEnum>(TEnum value) { return (ushort)(object)Convert.ChangeType(value, typeof(ushort)); } public static int to_i4<TEnum>(TEnum value) { return (int)(object)Convert.ChangeType(value, typeof(int)); } public static uint to_u4<TEnum>(TEnum value) { return (uint)(object)Convert.ChangeType(value, typeof(uint)); } }
semoga ini membantu seseorang.
sumber
Saya hanya ingin menambahkan Enum sebagai batasan umum.
Meskipun ini hanya untuk metode pembantu kecil menggunakan
ExtraConstraints
terlalu banyak overhead bagi saya.Saya memutuskan untuk hanya membuat
struct
batasan dan menambahkan pemeriksaan runtimeIsEnum
. Untuk mengonversi variabel dari T ke Enum, saya melemparkannya ke objek terlebih dahulu.public static Converter<T, string> CreateConverter<T>() where T : struct { if (!typeof(T).IsEnum) throw new ArgumentException("Given Type is not an Enum"); return new Converter<T, string>(x => ((Enum)(object)x).GetEnumDescription()); }
sumber