Feature request: Partial enums #2669
Replies: 23 comments 65 replies
-
Beta Was this translation helpful? Give feedback.
-
Why wouldn't it be enum is just a class when compiled and partial on enum would yield the same result. Also if you work with large enums it's much easier to sort/separate them in partial classes so it's easier to navigate thru them |
Beta Was this translation helpful? Give feedback.
-
You can always format your enum using whitespace and comments. It seems like this would be a lot of work for the language team for something that doesn't provide that much benefit. As stated already, partial classes were made to make using code generation tools easier. You wouldn't want a generator to create your enum values, or at least I can't think of a scenario for it. |
Beta Was this translation helpful? Give feedback.
-
As you mentioned, the difference with enum is that the order of the members matters. To implement this feature the compiler team would have to add new features that would explicitly specify that behavior. Given said behavior would have no utility outside of partial enums that would mean that partial enums would need to provide enough of a benefit to justify that cost in terms of time and effort. I believe that the number of enums that would benefit from this feature, either due to being excessively large or due to being partially generated, is vanishingly small. |
Beta Was this translation helpful? Give feedback.
-
That gets me wondering: what happens with |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
I still don't see a use case where you'd need partial enums. Even for large enums, assuming you need those often, and don't like any alternatives, you'd still want to write them into a single file, just to reduce maintenance overhead. Partial classes are already considered an anti pattern outside of designer generated classes. I don't think people will rush over to using partial enums. |
Beta Was this translation helpful? Give feedback.
-
This idea hasn't seemed to get much traction, but I did just have a use case today. We have an enum that is auto-generated from a database table using a T4 template. I realized I wanted to add a custom entry to the enum not sourced from the database. In this case it would be useful to have a partial enum with the additional item specified in a separate non-generated file. Similar to the reasoning for partial classes with auto-generated code. Obviously it is simple enough workaround to tweak the T4 template to generate the extra item, but a partial enum would be useful here. |
Beta Was this translation helpful? Give feedback.
-
The main issue in my view is that partial enums with automatic values (i.e. no initializers for the members) have a file ordering dependency. You can get weird ordering-dependent behavior with static field initializers, but it's a bit harder to do by accident: partial class C
{
static int F1 = 0;
}
partial class C
{
static int F2 = F1 + 1;
}
partial class C
{
static int F3 = F2 + 1;
} In the above sample if the partial declarations get unexpectedly reordered then the values in the fields may change. That's a pre-existing/shipped behavior. Imagine a similar partial enum: partial enum E
{
E1
}
partial enum E
{
E2
}
partial enum E
{
E3
} To resolve this it might be reasonable to require the first member in a given partial enum declaration to have an initializer. That prevents this kind of cross-file ordering dependency from occurring, but it could be inconvenient if adding a new member to a "previous" declaration required explicitly updating the initializer on the "later" declarations. |
Beta Was this translation helpful? Give feedback.
-
One odd scenario where a partial enum is useful is for controlling visibility. If a code generator doesn't explicitly assign visibility, and a user wants to make the enum public, they can do so in a user-owned file.
User
I don't think that's true, if instead they required that the first member of a partial enum was explicitly defined as RikkiGibson suggested above. I could imagine a code generator that owned a specific range of values (1-1000) and allowed the user to extend this with values from a different range. It seems to me, that any type that is defined in a generated file should be partial to give users the ability to extend/amend what the generator provides. |
Beta Was this translation helpful? Give feedback.
-
Not something I would use commonly, but in cases where compile directives / shared projects are used to facilitate progressive migration from legacy to new solutions or for different hardware implementations, I might leverage this to selectively include partial enums base on the current compiler config and the #defines in play -- could also have same enums with different values for say bit-wise use. Don't see it as a high yield feature, but it seems intuitive and simple. |
Beta Was this translation helpful? Give feedback.
-
suggestion for resolving conflicts, to allow different files to offer values out of the (quite large) integer valuespace range:
Enums are defined over 'int' by default not 'byte' or 'short', indicating an intent to support enums with vastly more than 255 options. |
Beta Was this translation helpful? Give feedback.
-
So I guess my next question is did someone from the language team even consider it over the past 3 years?
The fact that someone already implemented it in their private branch without much trouble says otherwise, at least to me. Not to mention that
Why? I am genuinely curious. It doesn't seem very hard to implement and it would make I understand that resources are limited, but at some point a decision has to be made. I guess what irks me is that those decisions seem to keep being postponed indefinitely and people who submit any feature requests don't get any feedback or closure. For this one, it's been 3 years, if you won't do it then just say so and be done with it. Or if you want to do it but cannot do it now, then at least let us know and commit to a timeframe, even if it is "we'll do it in X months/years" or even "we'll consider it for C# version 15.0". The worst possible thing is silence, it discourages contribution. |
Beta Was this translation helpful? Give feedback.
-
Came here because of a use case and trying to solve it without just doing the enum as a bunch of hard-coded constants. We have a core framework used in a number of apps. That framework has a number of enumerated UI blocking "Flows", and the framework brings about 5 Flow "Levels". The app itself will then define another 50 Levels or so. In order for the Flow framework to just relate to Levels as a whole, I'd need a partial enum that defined the 5 Levels available in the framework, and an app-specific partial enum for app Levels. The real problem with partial enums, I think, is that a single enum class can comfortably start at 0 and increase. But who gets to start in a partial enum class? Will my first framework enum value be 0 or 50? If partial enum classes were implemented, I think the language spec would have to require that a value is assigned to the first enum, and a compiler error thrown if duplicate values are used. I would use this immediately if it existed. For now, we'll likely just hard-code a bunch of constants and create some kind of convention about which number ranges are used. |
Beta Was this translation helpful? Give feedback.
-
Maybe a bit cynical? I'm doing something I can do now. And I'm in Unity, so even if C# gains this feature, I won't see it for another 5 years. But yes, I can do attributes. I can also just make a convention, which I did. It's also possible to use attributes in ways that don't protect myself from myself, so I have to be disciplined either way. |
Beta Was this translation helpful? Give feedback.
-
Very occasionally I've had situations where I wanted to add more entries to an existing enum that is not part of the source code. The way I solved it was to create a new enum whose first member had a value higher than the last member of the other enum. So this might be doable using the existing public enum BaseBitops
{
And,
Or,
Nand,
Nor
}; then be able to code: public enum GeneralBitops : BaseBitops
{
Exor,
ExNor,
RotateLeft,
RotateRight
}; Then InsertOperations(GeneralBitops.Nand, GeneralBitops.RotateLeft); This might possibly have some potential...? |
Beta Was this translation helpful? Give feedback.
This comment was marked as disruptive content.
This comment was marked as disruptive content.
-
For example, with my serializer: // MyEnum.cs
public partial enum MyEnum { A, B, C }
// MyEnum.Generated.cs
[TypeId(123)]
[Guid("x")]
public partial enum MyEnum { } This behavior would be match that of Additionally, though not useful to me, |
Beta Was this translation helpful? Give feedback.
-
To provide another real-world use-case for this feature I would like to mention Vulkan API. It has multiple enums (for example VkResult) that get extended with new versions and extensions. For example: // Core/Version1.0.cs
public partial enum Result {
ErrorUnknown = -13,
ErrorFragmentedPool = -12,
ErrorFormatNotSupported = -11,
ErrorTooManyObjects = -10,
ErrorIncompatibleDriver = -9,
ErrorFeatureNotPresent = -8,
ErrorExtensionNotPresent = -7,
ErrorLayerNotPresent = -6,
ErrorMemoryMapFailed = -5,
ErrorDeviceLost = -4,
ErrorInitializationFailed = -3,
ErrorOutOfDeviceMemory = -2,
ErrorOutOfHostMemory = -1,
Success = 0,
NotReady = 1,
Timeout = 2,
EventSet = 3,
EventReset = 4,
Incomplete = 5,
} // Core/Version1.1.cs
public partial enum Result {
ErrorInvalidExternalHandle = -1000072003,
ErrorOutOfPoolMemory = -1000069000,
} // Extensions/KHR/Swapchain.cs
public partial enum Result {
ErrorOutOfDateKHR = -1000001004,
SuboptimalKHR = 1000001003,
} // Extensions/NV/GLSLShader.cs
public partial enum Result {
ErrorInvalidShaderNV = -1000012000,
} In this specific use-case every enum member has assigned value so there is no ambiguity about member values and load order doesn't matter. I think it would be a reasonable compromise to require every member of a partial enum to have assigned value or at least the first member of every block. |
Beta Was this translation helpful? Give feedback.
-
We also have a requirement for partial enums. We are looking to hold a base class holding some standard enums, then as we superclass from that class we add in specific enums for those classes. The allocation of numbers are specific and would be defined - but these numbers could be similar between superclasses, though the enum reference is different. Base Class
SuperClass 1
SuperClass 2
|
Beta Was this translation helpful? Give feedback.
-
this would be useful to expand on an partial enum.. but would it work if the enum was in another name space? |
Beta Was this translation helpful? Give feedback.
-
I would like to contribute my two cents because I'm running into needing this. Like someone said for error handling. I want to handle the errors in my application in a uniform way, but I don't want one giant enum to dump all the codes in because it would make them harder to manage (and would expose error codes to modules that shouldn't use that particular code). After reading a lot of comments in this thread I think partial enum isn't the way to go because of various limitations, pitfalls, and complexities. If it was there was probably a solution offered for it in the past five years since this item has been open. Let's say you have these three enums:
You can use these enums in various places where they make sense. But in some places you would prefer that they were one large enum. For that I propose the following syntax:
There are two things that make this not a regular existing enum, and those two things are keywords already part of the language and already used in different ways in different places. "new" is used to create new instances, but also to shadow members in base classes (forgive me if shadow isn't the right word). So the use of those two keywords in a different context wouldn't pose a problem and they make it very obvious that this isn't like the existing regular enum while keeping the same simple declaration that a regular enum has. It would of course be possible to use namespaces as well:
They "as" keyword which is currently used a type conversion operator could be repurposed here to resolve conflicts in the name of enums:
To "new enum" type would behave like a regular enum when it is cast to a different data type, for example:
But in code the syntax to assign a value would be slightly different (but logical):
Because the enum members would always be fully qualified duplicate names in the used enums wouldn't pose a problem.
Values assigned by the developer or the compiler to the enum members of the used enums would be added to the start of the range that is set on the "new enum". This would make that...
...has an unique value while the compiler could still know that these are true:
That might seems strange but there is nothing preventing a coder from currently doing:
And getting a result of true on that. The compiler could handle casting to and from the "new enum" and the various enums it uses, but just like with regular existing enums once you cast to number data types you are taking the steering wheel and should be paying attention. The existing Enum class and other ToString methods / reflection classes would work with this "new enum" like with regular enums but with the understanding that things like...
... would produce the fully qualified name as "Foods.fries". The "new enum" would fall in the category of things that you shouldn't worry about if you don't know that it exists, but a valuable tool to have when you need it. I think this syntax would satisfy and perhaps exceed the expectations / needs of the persons asking for a partial enum. The "new enum" wouldn't be limited to the same namespace or even the same assembly that partial would be limited to. You could create a "new enum" that includes any enum (and even other "new enum") that isn't declared as private or protected in any assembly. Looking forward to feedback, suggestions, or questions. |
Beta Was this translation helpful? Give feedback.
-
yet another use case: I'm writing a library which is supposed to work with any other languages and support user generated values (users being other devs using the tool), so storing as an integer but having an enum (with explicitly defined values, which imo a partial enum or whatever syntax is used could require, erroring if there's a duplicate) makes a lot of sense. (as I want to write a reference implementation with custom things) declaring custom flags is also something you might want to do (and one where partial/inherited enums would be nice for purposes of toString with [flags] giving all the flags) as a workaround, using a dictionary<int, string> (maybe generated) does the job, though it's sensitive to typos and not as easy to find by name if you need specific things treated differently, and probably slower, using it with a template to generate an enum might work however.
enums are happy to take a value that's not declared (most likely intended, because flags), and since in my case I'm just using 2 enums, another option is to simply cast one to the other as needed.
a slightly more involved solution is to write a class implementing IEquatable, operator == and != (with itself, int, and your enums), has a method to get the name corresponding to an int from the enums you want, and implements toString for all flags. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Ok so I thought of maybe of doing the partial keyword on enums
for large enum files like for example cil OpCode list
Example
for example to do it like this
and
and after compiling those 2 (or more) concatenate like any other normal enum in finalized clr module
Issue
The only issue I can see here is the order of unspecified enum values
in the case above its not sure which enum part should be "First" in ordering
so I thought maybe to sort it by file name, or if user want to change the order
maybe some enum placeholder can be done like
or by simply specifying the first value in enum like in standard c language
like
Beta Was this translation helpful? Give feedback.
All reactions