New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider having enums auto-implement IEquatable<T> #2349
Comments
/cc @jkotas and @CarolEidt since I heard you'd be interested in this or have thoughts on it. |
We have done similar auto-implementation of
Relaxing rules for when enums (or Spans of enums) can be converted to their underlying type would be interesting avenue to explore. This would be as much language feature as it is a runtime feature. cc @jaredpar |
I just ran into this issue when using the new records in a dictionary, everything seems super nice except when you use an enum even when you mark that enum as being What is the current best pratice way of dealing with this I am considering creating a wrapper that can cast / uncast it to an int. |
@stephentoub We've talked in the past about concerns with the lang/compiler auto-implementing things. Ignoring potential breaking changes in lang behavior here, would you have any concerns if all enum types in the BCL suddenly were IEquatable? |
@jannickj The best practice workaround right now would be to create a wrapper that uses |
@Joe4evr Right but wouldn't that mean that everywhere I use Enums I'll have this ugly wrapper?
becomes:
|
I think my main concern would be on IL size increase and what impact that could have on blazor and xamarin. We recently, for example, had some PRs to reduce some use of value tuples in favor of custom structs to reduce all the extra stuff that comes with tuples. This enum change isn't as much per type, but potentially expanding many more types, and in a way that couldn't be trimmed. |
There will be size impact from this for sure as IEquatable<> is an interface that is almost never trimmed. It would be great if we figure out a way to implement this inside Enum class or as was suggested above, extend conversion rules for enums and their underlying types. |
Given that we were very concerned about the potential for silently-breaking changes with type checks on structs, I don't know how we could entertain a similar change for enums. |
This is old but still open and still a thorn in lots of paws, including mine. What if, instead of auto-implementing interfaces on all enums, it were simply possible to add static and/or instance methods to enum types, thus making it possible for them to explicitly implement interfaces? That way there's no concern about breaking backwards compatibility, since it's an opt-in mechanism. I wouldn't imagine it being a particularly big problem for the runtime either, since the metadata remains the source of truth, and since enums can't be extended I wouldn't imagine there would be any issue with inheritance. As a use case, I'm attempting to store a large number of single-byte values in a type-safe way, and in all other respects an enum derived from byte works great. I can store them in an array and they only take one byte per element, I can use Memory and Span to do range-based access and Slice to my heart's content, and I can pass elements to an overloaded function and have it choose the right overload. What I can't do is an equality comparison on a range of values, for the same reasons OP lists. My current options are:
None of those is a particularly fantastic. If I could put user-defined methods on an enum type, though, then I could simply write my own IEquatable or IComparable implementation (or, as syntactic sugar, the compiler could insert the function definitions if it sees an enum deriving from those without an explicit implementation, but it's not a big deal) and be done with it. Note that I'm not suggesting instance fields on enums, which would (a) violate Oracle's ridiculous patent, (b) invisibly create a storage structure for the field values, which seems like a bad idea, and (c) be entirely unnecessary, given that C# has properties. Allowing method declarations for enum types would let you implement interfaces, though, and not only that, it would let you add operator overloads, or customize the string representation of Finally, adding interface and method declarations to enum types would be a great way to address @stephentoub's point about IL explosion, since IEquatable et al could be applied piecemeal across the BCL and only for classes where it might be particularly relevant (like DayOfWeek, which seems like an obvious candidate). Are there any downsides to that solution? Because I'm not seeing any, and I'd love to know if there's something I'm missing. |
Also, you're welcome. 🙂 |
As already stressed out by others, enums are very ugly to use in anything outside of a simple constant collection. To enjoy more of their presence, the whole runtime must undergo several changes that begin treating enums more nicely. This includes many other convenience features, as linked above, and not just the ability to compare against equality between two numerical values that have a different name. That being said, even after all these years of the proposal being voiced, I'm only expecting it to come around when a runtime overhaul is decided. Things will get nasty in the process of enhancing those types and it's fine, as long as they are delivered nicely and end the pain of using them. |
I wonder if extensions could help here? extension EnumExtension<T> for T : IEquatable<T> where T : Enum, struct { .. } |
It might? It might make sense if such an extension could ship in the runtime in the same release as the feature, both as a proof-of-concept/example of what extensions can do, while at the same time not making everyone define the same extension to solve this issue (where some people would do the sub-optimal thing). |
You can implement it now like this |
Your example is a known trick at least for equality comparison, but doesn't help with the general lackluster of interface implementations. And the JIT specifically optimizes the code for the frequently used types, so you'd get an optimization for additional types if you provided them in. For enums though, you wouldn't approach their lack of correlation with INumber like that, or anything else revolving enums and enum methods. This trick is specific to implementing equality across a range of values, enums or not. Enums unfortunately don't play too nicely with anything that isn't the same enum type itself, not even their underlying integer type. |
My perspective:
|
The following code does not compile:
The reason is that the
MemoryExtensions.SequenceEqual
extension method is constrained toT : IEquatable<T>
, but it seems like since enums are restricted to behaving exactly like their underlying primitives they should be similarly comparable. (After all, equality and comparison operators behave as expected.)If the
ConcreteEnum
type implicitly implementedIEquatable<ConcreteEnum>
, or if the compiler could somehow allowConcreteEnum
to fulfill the generic method constraint even if the interface isn't actually implemented, it would allow us to use enums in Span-based APIs.The text was updated successfully, but these errors were encountered: