Enum as Generic Constraint: What's the Reason Microsoft Didn't Want to Implement It?
I realize that enum
ca开发者_如何转开发nnot be used as a generic constraint, and Microsoft has declined to fix this bug.
Any reason why?
The link you posted says why:
and is a somewhat arbitrary limitation of the language
Potentially will change:
If we ever reopen constraints as a feature, this will be one of the things we will reevaluate. For the upcoming release we don't have the opportunity to add any more language features, so you'll see this resolved as "Won't Fix", but it remains on our lists for future consideration.
I suspect the reason that enum
is not accepted as a generic constraint is that while there are some things one might "expect" to be able to do with an enum-constrained parameter that one can't do with an unconstrained parameter, the only one that would actually work would be calling the very slow non-generic HasFlag
; in particular, operations which involve converting between a enum and its associated base type would not be usable. Allowing a constraint which wouldn't allow programmers to use variables in ways they'd expect, but only add the ability to call a horribly slow non-generic method didn't seem worthwhile.
As it is, I don't think the inability to use Enum
as a type constraint would have been a loss, but for the addition of a feature which was not anticipated when the decision was made: extension methods and their interaction with Intellisense. If writes a method bool FlagCheck.HasAnyFlags<T>(T enum1, T enum2) where T:struct
which takes two matching-type enumerations and checks whether one contains any flags which are also in the other [one can write such a method to be about an order of magnitude faster than Enum.HasFlag
], it may not make sense to call it on parameters of type double
, but only consequence of that is that such things will be caught at run-time rather than compile time. It's only if one makes such a thing an extension method that the lack of an enum constraint becomes annoying; in that case, it means that there's no way to have Intellisense offer HasAnyFlags
on a variable of an enum
type without it also popping up on variables of other types.
BTW, I think I disagree with the philosophy on enum
constraints for the same reason I disagree with the rule that one can't constrain to a sealed type. Even if it would be useless to create a generic type parameter that was constrained to a type that would always be sealed, the fact that a type is sealed in a [perhaps preliminary] version of an assembly implies it will always be so. Further, if a type is unsealed but has only internal
constructors [called via factory methods] I don't know that replacing it with a sealed class would be a breaking change but for the rule about sealed type constraints.
The underlying type issues and performance are valid, but they have workarounds and the CLR and C++/CLI support generic enum constraints. Working with flags enums has always been less readable than I prefer. HasFlag helps but as has been pointed out there's room for increasing performance.
I have this and several other useful enum extension/helper methods here. If you really need to write a method that constrains on the enum type, that language can handle it and it's not all that difficult to learn enough of it to write if these kinds of simple methods coming from a C# background.
I suspect some of limitations of generic enum types I experienced in C++/CLI have something to do with reasons why it was seen as "not important enough." For instance, most useful operators are missing other than assignment. To do anything with the TEnum, you have to cast to the underlying type, which can be expensive depending on how it's done. Consider that the binary operation to Add/Remove/Test for flag is extremely fast, adding in a single type conversion requirement dramatically changes the performance. Going to a (known) underlying type value in C++/CLI can be done very quickly in a manner that is implemented in IL as the equivalent of "take in an enum parameter and pass it out as though it was actually the underlying type". Doing that back to the enum, however, isn't possible in C++/CLI and requires an Enum.ToObject call, which is an expensive conversion.
I implemented a workaround that basically takes the set of "convertBackToTEnum" methods and rewrites the IL to do it exactly the way the ConvertToUnderlyingType methods does.
The casts are also risky if you screw up and cast to the wrong underlying type. I was concerned enough about it that I wrote a T4 script to generate unit tests for each operation on each underlying type (with values that would cause problems if converted incorrectly).
If you need to write methods that support this, the above project has several examples, including class and method constraints.
精彩评论