Anyone know a good workaround for the lack of an enum generic constraint?
我想做的是这样的事情:我有带组合标记值的枚举。
1 2 3 4 5 6 7 8 | 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; } } |
所以我可以这样做:
1 2 3 4 | MyEnum tester = MyEnum.FlagA | MyEnum.FlagB if( tester.IsSet( MyEnum.FlagA ) ) //act on flag a |
不幸的是,C的泛型where约束没有枚举限制,只有类和结构。C不将枚举视为结构(即使它们是值类型),因此我无法添加这样的扩展类型。
有人知道解决方法吗?
编辑:现在这是0.0.0.2版的UntrainedMelody。
(根据我博客上关于枚举约束的要求)。为了一个独立的答案,我在下面列出了基本事实。)
最好的解决方案是等待我将其包含在未经培训的日志中1。这是一个使用带有"假"约束的C代码的库,例如
1 | where T : struct, IEnumConstraint |
把它变成
1 | where T : struct, System.Enum |
通过后期构建步骤。
写
如果你打电话来的话,你想做什么?
1 | tester.IsSet(MyFlags.A | MyFlags.C) |
?它应该检查是否设置了所有指定的标志?那是我的期望。
今晚回家的路上我会尽力的…我希望对有用的枚举方法进行快速的测试,以使库快速达到可用的标准,然后放松一点。
编辑:顺便说一句,我不确定
- 包括
- 包含
- hasFlag(或hasFlags)
- ISSET(这当然是一种选择)
思想是受欢迎的。我敢肯定,任何东西都要等一段时间才会变成石头…
或者作为补丁提交,当然……
Darren,如果类型是特定的枚举就可以了-要使一般的枚举工作,您必须将它们强制转换为int(或者更可能是uint)才能进行布尔数学:
1 2 3 4 | public static bool IsSet( this Enum input, Enum matchTo ) { return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0; } |
从C 7.3开始,现在有一种内置的方法来添加枚举约束:
1 | public class UsingEnum<T> where T : System.Enum { } |
来源:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint
事实上,这是可能的,用一个丑陋的伎俩。但是,它不能用于扩展方法。
1 2 3 4 5 6 7 8 | 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") |
如果愿意,可以给
你可以通过IL编织和课外培训来达到这个目的。
允许您编写此代码
1 2 3 4 5 6 7 8 9 | public class Sample { public void MethodWithDelegateConstraint<[DelegateConstraint] T> () { } public void MethodWithEnumConstraint<[EnumConstraint] T>() { } } |
编译的内容
1 2 3 4 5 6 7 8 9 10 | public class Sample { public void MethodWithDelegateConstraint<T>() where T: Delegate { } public void MethodWithEnumConstraint<T>() where T: struct, Enum { } } |
这并不能回答最初的问题,但是在.NET 4中现在有一个名为Enum.HasFlag的方法,它可以完成您在示例中尝试执行的操作。
我这样做的方法是放置一个结构约束,然后在运行时检查t是否是一个枚举。这并不能完全消除这个问题,但它确实在一定程度上减少了这个问题。
从C 7.3开始,可以对泛型类型使用枚举约束:
1 2 3 4 | public static TEnum Parse<TEnum>(string value) where TEnum : Enum { return (TEnum) Enum.Parse(typeof(TEnum), value); } |
如果要使用可为空的枚举,则必须保留Orginial结构约束:
1 2 3 4 5 6 7 | public static TEnum? TryParse<TEnum>(string value) where TEnum : struct, Enum { if( Enum.TryParse(value, out TEnum res) ) return res; else return null; } |
这里有一些代码,我刚刚做了,看起来像你想要的工作,而不必做任何太疯狂的事情。它不仅限于设置为标志的枚举,而且如果需要的话,也可能总是有一个签入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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."); } } |
使用原始代码,在方法内部还可以使用反射来测试t是否为枚举:
1 2 3 4 5 6 7 8 9 10 11 |
如果有人需要通用的ISset(即即时创建的ISset)和或字符串到枚举on fly转换(使用下面介绍的EnumConstraint):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | 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); } } |
如果有人仍然需要示例hot来创建枚举编码约束:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | 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)); } } |
希望这能帮助别人。
我只想添加枚举作为通用约束。
虽然这只是一个使用
我决定只创建一个
1 2 3 4 5 |