任何人都知道一个很好的解决方法,缺乏枚举通用约束?

我想要做的是这样的:我已经枚举了标记值的组合。

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

那么我可以这样做:

 MyEnum tester = MyEnum.FlagA | MyEnum.FlagB if( tester.IsSet( MyEnum.FlagA ) ) //act on flag a 

不幸的是C#的generics约束没有枚举限制,只有类和结构。 C#没有看到枚举作为结构(即使它们是值types),所以我不能像这样添加扩展types。

任何人都知道解决方法?

编辑:这是现在生活在版本0.0.0.2的UnconstrainedMelody。

(根据我的博客文章中有关枚举约束的要求,为了独立回答,我已经包含了下面的基本事实。)

最好的解决办法是等待我把它包含在UnconstrainedMelody 1中 。 这是一个图书馆,采取C#代码与“假”的约束,如

 where T : struct, IEnumConstraint 

并把它变成

 where T : struct, System.Enum 

通过构build步骤。

编写IsSet不应该太难,尽pipe兼顾基于Int64的和基于UInt64的标志可能是棘手的部分。 (我嗅到一些帮助器方法,基本上允许我把任何标志枚举,如果它有一个基types的UInt64 。)

如果你打电话,你想要什么样的行为

 tester.IsSet(MyFlags.A | MyFlags.C) 

? 是否应检查是否设置了所有指定的标志? 那将是我的期望。

我将在今晚回家的路上尝试做这件事…我希望能快速地通过一些有用的枚举方法来快速获得可用的标准,然后放松一下。

编辑:顺便说一句,我不知道IsSet作为一个名称。 选项:

  • 包括
  • 包含
  • HasFlag(或HasFlags)
  • IsSet(这当然是一个选项)

思想欢迎。 我敢肯定,在任何事情发生之前都会有一段时间…


1或将其作为补丁提交,当然…

达伦,如果这些types是特定的枚举,那么这将起作用 – 对于一般的枚举工作,你必须将它们转换为整数(或者更可能是uint)来执行布尔运算:

 public static bool IsSet( this Enum input, Enum matchTo ) { return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0; } 

事实上,这是可能的,一个丑陋的伎俩。 但是,它不能用于扩展方法。

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

如果你愿意,你可以给Enums<Temp>一个私有的构造函数和一个TempEnum的公共嵌套抽象inheritance类,以防止非枚举的inheritance版本。

您可以使用IL编织和ExtraConstraints来实现此目的

允许你写这个代码

 public class Sample { public void MethodWithDelegateConstraint<[DelegateConstraint] T> () { } public void MethodWithEnumConstraint<[EnumConstraint] T>() { } } 

什么被编译

 public class Sample { public void MethodWithDelegateConstraint<T>() where T: Delegate { } public void MethodWithEnumConstraint<T>() where T: struct, Enum { } } 

这并不回答原来的问题,但是现在有一个名为Enum.HasFlag的 .NET 4中的方法,它可以在你的例子中做你正在做的事情

我这样做的方式是把一个结构约束,然后检查在运行时T是一个枚举。 这并不能完全消除这个问题,但它确实有所减less

使用你的原始代码,在这个方法里面,你也可以使用reflection来testingT是一个枚举:

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

以下是我刚刚完成的一些代码,看起来像你想要的那样工作,而不必做任何太疯狂的事情。 它不仅限于Flags的枚举,但是如果需要的话总是可以检查的。

 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."); } } 

如果有人需要通用的IsSet(创build开箱即用可以改进),和或string到枚举onfly转换(使用EnumConstraint呈现下面):

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

如果有人仍然需要示例热点来创build枚举编码约束:

 using System; /// <summary> /// would be same as EnumConstraint_T&lt;Enum>Parse&lt;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)); } } 

希望这可以帮助别人。

我只是想添加Enum作为一个通用的约束。

虽然这只是一个使用ExtraConstraints的小辅助方法,但对我来说有点太多了。

我决定只是创build一个struct约束,并添加一个运行时检查IsEnum 。 为了将一个variables从T转换为Enum,我先将它转换为对象。

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