6 ms·
Very excited about the Nullable Reference Types. Besides that, the feature I want most in C# is enum exhaustion checking. So I can write: enum Color { red,
by ajennings 9y ago
Very excited about the Nullable Reference Types.
Besides that, the feature I want most in C# is enum exhaustion checking. So I can write:
enum Color { red, green, blue };
public static string SuperheroFromColor(Color c)
{
if (c == Color.red)
{
return "Superman";
}
else if (c == Color.green)
{
return "Green Lantern";
}
else if (c == Color.blue)
{
return "Spiderman";
}
}
and not get a compiler error (that not all code paths return a value). But if I added "yellow" as an enum option, then I would get a compiler error. I realize that Color can be cast from an out-of-range int, so it's a little complicated. I'm willing to put something extra after the last else:
unreachable return null;
or use a special switch statement. I just want something so the compiler checks that I have accounted for all normal enum values.
Is anyone talking about such a feature, or where would I request it? Can I write it myself as a Roslyn extension?
- opmac 9y agoKotlin has been doing null safety since release. I suggest checking it out if that sort of thing excites you. It's probably my favorite language currently.
- UnoriginalGuy 9y agoYou're asking for a language change to fix poorly written code. The final else if() is redundant, if you change it to a simple else you remove the "not all code paths return a value" issue and remove a redundant check. This requires you to consider the "and others" possibility, rather than the compiler making assumptions about the type's definition. Plus what you're asking for is a maintenance nightmare; since changing the type (Color) in one part of the code can break method definitions in another part of the code (inc. other assemblies). I'd hope they would never support what you're asking for.
- chowells 9y agoHe's asking for exhaustiveness checking of pattern matching algebraic data types. Many languages have had this for essentially forever. It does sometimes mean that when you update some code, you have to update a bunch of other things. But it's rarely spurious. Usually those other things are broken by failing to consider the new possibility. In the long term, it's a maintenance win.
- jameshart 9y agoBut C# enums are a terribly weak data type, not a good implementation for the semantic you're looking for at all. Better to ask for a C# union type and matching syntax.
- parenthephobia 9y agoBy turning the "else if" into an "if" you introduce a silent bug when Color.yellow is added, since the final else clause would now have to deal with both blue and yellow correctly. Adding a new member to the Color enum should break code that isn't written to handle it: which is any code written before Color.yellow was introduced.
- ajennings 9y agoCreated a compiler extension to help me: https://github.com/abjennings/EnumAnalyzer https://github.com/abjennings/EnumAnalyzer
- ajennings 9y agoIf I add a "yellow" option to the enum, then I _want_ to get compiler errors (or warnings) about all the places in my code that need to handle that new case. That's what compilers are for, that kind of busy work. Is there a way to write that SuperheroFromColor function so that I will get a compiler error (or warning) if another option is added to the enum? (Imagine that function is far away from the Color definition so it's not visually obvious.) When I create an enum with three possible values, I reason about it as having only three possible values. As long as I never cast from an int, that should be true, right? So I don't want to consider the "and others" possibility. It's hard to even think about what to do in that situation, since it shouldn't happen. I said that I'm willing to put some keyword before or after the if statement to tell the compiler to make sure I'm handling all valid enum values. So it can only give me these errors (or warnings) when I ask for them. I think pattern matching in Haskell will make sure you handle all possible cases and give you a compiler error if you don't. I'm asking for a "poor-man's version of pattern matching for enums" in C#.
- deleted 9y ago[deleted]
- wvenable 9y agoUse the switch statement without a default clause. If you haven't exhausted the options, you get an error.
- ajennings 9y agoA compile-time error? It doesn't do that for me on VS2012 or in .NET Fiddle: https://dotnetfiddle.net/f4NSvf https://dotnetfiddle.net/f4NSvf
- deleted 9y ago[deleted]
- abiox 9y agoclicking that link, i see a 'not all code paths return' error.
- ajennings 9y agoI can see how that's unclear. That fiddle was supposed to be an example where I did exhaust all the options, so I shouldn't get a compiler error. In any case, to make the error go away, I add a "return null" or throw an exception at the end: https://dotnetfiddle.net/I7LXkW https://dotnetfiddle.net/I7LXkW Then I want to be able to add a "yellow" option to the enum and get a compiler error, but there is no error: https://dotnetfiddle.net/IIUfye https://dotnetfiddle.net/IIUfye That's the bigger problem. I should've shared that one. Can someone show me a fiddle of a C# program that compiles, but gives a compiler error when I add another value to the enum?
- int_19h 9y agoThere isn't one. You're correct, this is not something that C# can do. To be even more specific, it's not doable in C#, because enum types actually have the complete set of values of the underlying integer type. Thus, this is valid: Color c = (int)123; So enum doesn't actually limit the value domain of the integer type. It just gives names to some of the values, while still allowing all other values. If you actually mean to constrain the values as part of pre-contracts in your public API, you have to check for that yourself, e.g. via `Enum.IsDefined`. So, getting back to your scenario - "Yellow" was always an option. It's just that you gave it a name when it didn't have one before. Consequently, for switch to be truly exhaustive, it'd have to check for all 2 billion possibilities. This all is the unfortunate consequence of inheriting enum design from C++, and retaining its very low-level semantics. But now that it's there, and has been there since C# 1.0, they're stuck with it. I suspect any future advances in this area will come with a new construct that would be a "better enum" - e.g. case classes or full-fledged ADT variants. Now that the language has pattern matching, it's begging for something like this.
- jaredpar 9y agoThere is a proposal for "sealed enums" on the C# language repo that have the semantics you are looking for. https://github.com/dotnet/csharplang/issues/782 https://github.com/dotnet/csharplang/issues/782
- khaledh 9y agoI know this doesn't solve your problem in C#, but F# gives you a compiler warning in such a case. The following code compiles without warnings or errors: type Color = Red | Green | Blue let superheroFromColor c = match c with | Red -> "Superman" | Green -> "Green Lantern" | Blue -> "Spiderman" However, if you add Yellow as a new case to the Color discriminated union without adding a corresponding match pattern everywhere you match a Color value, you get a compiler warning: type Color = Red | Green | Blue | Yellow ... match c with ----------^ warning FS0025: Incomplete pattern matches on this expression. For example, the value 'Yellow' may indicate a case not covered by the pattern(s). Aside: notice how terse and readable the code is compared to the C# equivalent; F# tends to have a high signal-to-noise ratio.
- ajennings 9y agoI wrote myself a compiler extension to check this for me: https://github.com/abjennings/EnumAnalyzer https://github.com/abjennings/EnumAnalyzer