Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why C# pattern matching is not exhaustive for enums?

Tags:

c#

enums

Say, I have the following enum and the code testing enum:

enum Flag
{
    On,
    Off
}

string GetMessage(Flag flag) =>
    flag switch
    {
        Flag.On  => "State is ON",
        Flag.Off => "State is OFF"
    };

However, I get the warning:

Warning CS8509 The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(ConsoleApp.Flag)2' is not covered.

Why it's not exhaustive when I listed all enum's values? And what is (ConsoleApp.Flg)2 enum value?

like image 263
JohnyL Avatar asked Aug 08 '20 13:08

JohnyL


People also ask

Why is C used?

C is a general-purpose programming language and can efficiently work on enterprise applications, games, graphics, and applications requiring calculations, etc. C language has a rich library which provides a number of built-in functions. It also offers dynamic memory allocation.

Why is C used in C?

%d is used to print decimal(integer) number ,while %c is used to print character . If you try to print a character with %d format the computer will print the ASCII code of the character.

Why should you learn C?

C is very fast in terms of execution time. Programs written and compiled in C execute much faster than compared to any other programming language. C programming language is very fast in terms of execution as it does not have any additional processing overheads such as garbage collection or preventing memory leaks etc.

Why is C language named so?

Quote from wikipedia: "A successor to the programming language B, C was originally developed at Bell Labs by Dennis Ritchie between 1972 and 1973 to construct utilities running on Unix." The creators want that everyone "see" his language. So he named it "C". C is about the tone C.


2 Answers

Counterexample:

string Foo()
{
    return GetMessage((Flag)42);
}

Unfortunately C# enums are not as robust as algebraic data types (or variant types, however you like to call them) in Haskell or other languages with better FP features. It's really just some metadata around an integral numeric value (int by default), so there's nothing in the type system stopping you from passing a value that does not correspond to a valid enum value. The compiler tells you just that, using (Flag)2 as a possible value. To fix the issue, add a standard catch-all:

string GetMessage(Flag flag) =>
    flag switch
    {
        Flag.On  => "State is ON",
        Flag.Off => "State is OFF",
        _        => throw new ArgumentOutOfRangeException(nameof(flag)),
    };
like image 161
V0ldek Avatar answered Oct 16 '22 21:10

V0ldek


Good news! In recent versions of the Roslyn compiler, this warning (where, for example, the pattern (ConsoleApp.Flag)2 is not covered) has been given a new code CS8524.

The original warning code CS8509 now applies only to missing named enum values.

So we can now tell the compiler to ignore CS8524 where we deem it unnecessary code bloat to write a catch-all handler for unnamed enum values but still want to catch cases where we forgot to handle a named value (or we add new named values to an existing enum).

Also, if previously we told the compiler to ignore CS8509 to avoid writing _ => throw ... handlers, we might want to change that to ignore CS8524 instead now so we get back our CS8509 warning for the cases we do want warnings about!

Background: The Roslyn change was made in dotnet/roslyn#47066 which I discovered when reading the comments for dotnet/csharplang#2671 (Exhaustability for switch expressions on enums should be less strict).

like image 33
wardies Avatar answered Oct 16 '22 21:10

wardies