在C#3.0中是否存在针对“特殊类”Enum的generics类型约束的解决方法?

更新:有关C#解决方法,请参阅此问题的底部。

嗨,您好,

请考虑以下扩展方法:

public static bool HasFlags(this T value, T flags) where T : System.Enum { // ... } 

如您所知,这将在编译时抛出错误,因为通常不允许类从System.Enuminheritance。 问题是使用enum关键字指定的任何枚举实际上都是从System.Enuminheritance的,因此上述代码将是仅将扩展方法限制为枚举的理想方法。

现在明显的解决方法是使用Enum而不是T ,但是你失去了generics类型的好处:

 MyEnum e; e.HasFlags(MyOtherEnum.DoFunkyStuff); 

上面的代码会使用generics类型抛出编译时错误,而它只能使用Enum类型抛出运行时错误(如果我实现它就会这样做。)

是否有任何编译器选项可用于关闭约束检查,还是有其他一些漂亮的方法来做到这一点?

在建议之前,我想说我不会where T : struct或者其他where T : struct使用,从那以后你就可以做到像123.HasFlags(456)这样奇怪的东西。

我很困惑为什么这个错误根本存在…这是你where T : System.Object使用的同样的问题,但为此你有where T : class …为什么没有where T : enum

C#解决方法

Jon Skeet已经开始研究一个库,该库通过对IEnumConstraint的约束来编译类,然后将其替换为System.Enum后构建。 我相信,这是目前最接近解决这个问题的人。

看到:

  • 代码项目: http : //code.google.com/p/unconstrained-melody/
  • 博客条目: http : //msmvps.com/blogs/jon_skeet/archive/2009/09/10/generic-constraints-for-enums-and-delegates.aspx

如果此解决方法不可行,则必须将库编写为C ++ / CLI代码,这不会限制可用于generics类型约束的内容(请参阅下面的答案中的代码。)

编辑:现在可以通过ildasm / ilasm: UnconstrainedMelody支持此库。


C#团队的成员之前曾表示他们希望能够支持where T : Enumwhere T : Delegate ,但它从来都不是一个足够高的优先级。 (我不确定首先是限制的原因是什么,不可否认……)

C#中最实用的解决方法是:

 public static bool HasFlags(this T value, T flags) where T : struct { if (!(value is Enum)) { throw new ArgumentException(); } // ... } 

这会失去编译时检查“enum-ness”,但会检查你在两个地方都使用相同的类型。 当然,它也有检查的执行时间惩罚。 在第一次调用之后,您可以通过使用通用嵌套类型来避免执行时间损失,该实现将在静态构造函数中抛出exception:

 public static bool HasFlags(this T value, T flags) where T : struct { if (!(value is Enum)) { throw new ArgumentException(); } return EnumHelper.HasFlags(value, flags); } private class EnumHelper where T : struct { static EnumHelper() { if (!typeof(Enum).IsAssignableFrom(typeof(T)) { throw new InvalidOperationException(); // Or something similar } } internal static HasFlags(T value, T flags) { ... } } 

正如Greco所提到的,您可以在C ++ / CLI中编写该方法,然后从C#引用类库作为另一种选择。

有一个解决方法

看这里

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

 public abstract class Enums where Temp : class { public static TEnum Parse(string name) where TEnum : struct, Temp { return (TEnum)Enum.Parse(typeof(TEnum), name); } } public abstract class Enums : Enums { } Enums.Parse("Local") 

如果您愿意,可以为Enums一个私有构造函数和一个公共嵌套的抽象inheritance类,其中TempEnum ,以防止非枚举的inheritance版本。

我无法抗拒参加C ++的解决方案,而且自从我开始工作以来,我想我会和其余的人一起分享它!

这是C ++代码(我的C ++非常生疏,所以请指出任何错误,特别是如何定义参数):

 #include "stdafx.h" using namespace System; using namespace System::Runtime::CompilerServices; namespace Blixt { namespace Utilities { [Extension] public ref class EnumUtility abstract sealed { public: generic  where T : value class, Enum [Extension] static bool HasFlags(T value, T flags) { __int64 mask = Convert::ToInt64(flags); return (Convert::ToInt64(value) & mask) == mask; } }; } } 

用于测试的C#代码(控制台应用程序):

 using System; using Blixt.Utilities; namespace Blixt.Playground { [Flags] public enum Colors : byte { Black = 0, Red = 1, Green = 2, Blue = 4 } [Flags] public enum Tastes : byte { Nothing = 0, Sour = 1, Sweet = 2, Bitter = 4, Salty = 8 } class Program { static void Main(string[] args) { Colors c = Colors.Blue | Colors.Red; Console.WriteLine("Green and blue? {0}", c.HasFlags(Colors.Green | Colors.Red)); Console.WriteLine("Blue? {0}", c.HasFlags(Colors.Blue)); Console.WriteLine("Green? {0}", c.HasFlags(Colors.Green)); Console.WriteLine("Red and blue? {0}", c.HasFlags(Colors.Red | Colors.Blue)); // Compilation error: //Console.WriteLine("Sour? {0}", c.HasFlags(Tastes.Sour)); Console.WriteLine("Press any key to exit..."); Console.ReadKey(true); } } } 

您可以使用IL Weaving和ExtraConstraints实现此目的

允许您编写此代码

 public static bool HasFlags<[EnumConstraint] T>(this T value, T flags) { // ... } 

什么被编译

 public static bool HasFlags(this T value, T flags) where T : System.Enum { // ... }