在C#中的枚举类型约束
枚举类型约束在C#中的出现原因是为了限制泛型类型参数T必须为枚举类型。在给定的代码中,使用了where T : struct
的约束来限制T必须为结构体类型,但这个约束过于宽泛,包括int、float、double、DateTime等类型,甚至用户自定义的结构体类型。
为了解决这个问题,可以对T进行运行时检查,判断T是否为枚举类型。通过!typeof(T).IsEnum
可以判断T是否为枚举类型。如果T不是枚举类型,则抛出InvalidOperationException
异常,提示方法需要一个枚举类型作为参数。
以下是一个示例代码,用于在第一行进行类型检查:
if (!typeof(T).IsEnum) { throw new InvalidOperationException("MethodBlah requires an enum!"); }
通过这种方式,可以在泛型方法中限制类型参数T必须为枚举类型,从而确保代码的正确性和安全性。
枚举类型约束在C#中是一个被偶尔提出的功能需求。然而,目前还没有人为此设计、规范、实现、测试、文档化和发布这个功能。这并没有什么特别的原因,因为我们有很多其他的工作要做,预算有限,而且这个功能在语言设计团队的讨论中从未超过“这个功能会很好吗?”的阶段。
有一些不错的使用案例,但没有一个案例是如此引人注目,以至于我们会为之付出代价,而不是去实现那些更频繁请求的其他功能,或者那些使用案例更具说服力和更具影响力的功能。如果我们要修改这段代码,我个人会将委托约束的优先级远远放在枚举约束之上。
某些情况下委托约束,因为我也很希望看到这个功能。
上次我在与CLR团队的对话中听到关于这个的问题时,他们说他们没有准备好,但我完全可能误解了他们的讨论。
让我们这样说,有没有一种方法可以在ILASM中实现呢?
据我了解(我太懒了,不想尝试,也不写IL),是的:<.ctor (class [mscorlib]System.Enum) T>
。
我以前没注意到这篇文章。在另一个问题中,这篇博文被链接了:The Case of the Missing Generic (Parse Method) 这表明它在IL中是可用的。我似乎记得以前尝试过它,并且它可以工作,虽然可能是针对Delegate
的。如果可能的话,我很乐意进行更多的实验,以便取消C# 5中的限制(我假设C# 4现在已经锁定)。
我对你所说的CLR不支持枚举约束感到困惑,因为根据我对规范第166/167页的理解,CLR确实支持(ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf),以及Jon Skeet在Unconstrained Melody的工作(code.google.com/p/unconstrained-melody)。
好吧,那你自己想一想,哪种可能性更大?规范是错误的,Jon的实现是虚构的,还是我记错了或者误解了一年多前与CLR团队的对话?
这个功能并没有未实现,而是被禁止了。SLaks下面的解决方案并不是实现这个功能的方式,而是消除CS0702错误的方法。由于将泛型参数约束为继承自枚举类型是合理的,并且显然是有效的,添加一个特定禁止它的规则似乎很奇怪。
你的链接是错误的;我想你是指The Case of the Missing Generic (Parse Method)。
是的,就是这个。哎呀,糟糕了。
检查这个答案:stackoverflow.com/a/38410351/4009642
我可以在哪里正式请求这个功能?
GitHub。
那么,我应该将其报告为一个问题吗?
不仅如此,CLR还支持它,至少根据我对规范第166/167页的理解。我将一个类从C++/CLI移植到C#,想知道为什么在C#中不可能。
嘿,因为这个问题几年后仍然没有得到解答,你能否请你就这个具体问题发表评论:这真的是一个需要设计、规范、实现、测试等等的功能吗?这个功能已经存在了(泛型类型约束),为什么where T : Enum
约束需要特别处理(即任何需要为此约束设计、规范等的东西,而不是由泛型类型约束功能覆盖)?
你是在暗示存在一些不需要进行设计、规范和测试的功能吗?如果没有进行设计、规范和测试,你怎么知道这个功能是否正确实现了?
我怀疑你在这里对“设计”有过于狭义的理解。设计团队面临的问题不仅仅是“语法应该是什么?” 这只需要五分钟。设计团队面临的问题是“这个功能是否会对任何现有程序引入任何可能的破坏性变化?” 和“这个功能是否会使我们提出的未来功能列表中的任何功能变得更难实现?” 还有很多类似的问题。即使是小功能的设计也必须经过慎重的思考,如果你希望你的语言能够存活几十年。
谢谢你的回答!让我澄清一下我的意思,希望你能回答:我们正在讨论的功能已经是语言的一部分:给定一个非密封类C
,可以使用类型约束where T : C
。语法是清晰的,编译器和运行时都支持它。但是有意识地决定禁止where T : System.Enum
。所以我的问题是,System.Enum
有什么特殊之处?为什么不能像对待其他类一样对待System.Enum
?什么是使将System.Enum
视为其他类一样的东西成为“新功能”的原因?
好吧,我们来思考一下。假设我们有void M<T>(T t) where T : System.Enum{}
这是合法的吗?M((System.Enum)null);
看起来应该是合法的,它看起来应该是类型推断能够推断出T
是System.Enum
,而且看起来这个约束已经满足了。由于System.Enum
是引用类型,它可以是null。你希望这个程序是合法的吗?现在我们有一个设计问题,我们可以争论一番,你现在知道我过去七年的时间都在做什么。
因为我敢保证设计团队中有一半的人会说,显然这应该是合法的,因为它符合法律的字面意思,另一半的人会说显然这应该是非法的,因为它违反了类型是一个特定的枚举类型的意图,然后有人说等一下,在这个其他的偏远角落里,CLR验证器会做什么特殊的规则,然后我们去调查一段时间...
然后有人说,对于class C<T> { public virtual void M<U>(U u) where U : T { } } class B : C<System.Enum> { public override void M<U>(U u) { } }
现在怎么办?这与我们写where U : System.Enum
而不是where U : T
时完全相同吗?争论再次开始...
...然后你会发现验证器对于这种情况有特殊的规则,C#编译器必须在使用u
时生成装箱/拆箱对,并且JIT编译器不能为它们生成高效的代码,这是否影响设计?因为我们不希望用户在编写代码时被误导以为他们正在编写高效的代码,于是争论又开始了。总结一下:在泛型类型系统的世界中,没有简单的功能。每一个功能都必须经过仔细的思考。
谢谢你的详细解释。我认为避免在这种情况下进行特殊处理是有道理的。语言允许where T : SomeClass
约束,那么为什么要把System.Enum
和其他类区别对待呢?一些用户可能会对约束允许或不允许System.Enum
本身提出抱怨,虽然他们可能有很好的理由,但在没有非常明确的理由的情况下,我认为应该“遵循法律的字面意思”,避免特殊情况和额外的约束。无论如何,我现在更好地理解了其中的复杂性。再次感谢!
请参阅在Proposal: support an enum constraint on generic type parameters中讨论的设计考虑。
值得一提的是,C# 7.3中增加了枚举约束的功能。
在C#中,枚举类型的约束是不被支持的。然而,可以通过一个不太美观的技巧来实现。这个技巧可以用于普通的方法,但不能用于扩展方法。
下面是一个使用这个技巧的示例代码:
public abstract class Enumswhere 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
需要注意的是,这个技巧不能用于创建扩展方法。
此方法对于委托是有效的,只是不会排除`Delegate`本身。
还有一个更优雅的方法是使用C++/CLI,不需要任何技巧:
genericwhere T : Enum public ref class CClassName
为什么要指定"class"和"struct"的约束?如果删除这些约束,代码仍然可以工作,只是会在非枚举类型上抛出一个编译错误,指示"类型 'x' 必须能够转换为 'System.Enum' 才能..."。
这个技巧主要是为了防止使用`Enums
尽管这些技巧可以实现枚举类型的约束,但在编译时检查是更好的选择,因为它可以在编译时捕获错误,而不是在运行时。