Search code examples
c#compiler-errorspattern-matchingvariable-assignmentvariable-declaration

How can I use a declaration pattern outside of an if condition?


I'm trying to use declaration patterns as described here: https://learn.microsoft.com/dotnet/csharp/language-reference/operators/patterns#declaration-and-type-patterns

I can see that the examples only mention using declaration patterns in if conditions, like this:

object greeting = "Hello, World!";
if (greeting is string message)
{
    Console.WriteLine(message.ToLower());  // output: hello, world!
}

Patterns do generally work outside of if conditions, i.e. anywhere you could put a Boolean expression. So I could write this:

var isString = greeting is string;

But then if I try making it a declaration pattern, I get a compiler error... not from the declaration pattern itself, but only when I try using the declared variable:

var isString = greeting is string str;

Console.WriteLine(str); // Compiler Error CS0165: Use of unassigned local variable 'str'

Intuitively, I would expect the variable str to be initialized with the value of greeting just like when I do that in an if condition. Is there a way I can get it to work?


Solution

  • Compare to the following (roughly equivalent) code:

    object? greeting = "test";
    
    string str;
    bool isString = greeting is string;
    if (isString)
    {
        str = (string)greeting;
    }
    
    if (isString)
    {
        Console.WriteLine(str);
        // error CS0165: Use of unassigned local variable 'str'
    }
    

    You can see that the variable str is declared in all cases, but it's only assigned a value if isString is true.

    The definite assignment analysis isn't able to deduce that if isString was true for the Console.WriteLine test, it must also have been true for the case which assigns str, and that therefore str must be assigned.


    Now, with the above code, you can write:

    bool isString = greeting is string;
    if (isString)
    {
        str = (string)greeting;
    }
    else
    {
        str = "default";
    }
    
    // str can now be used, as it is definitely assigned
    

    You can do the same with patterns:

    if (greeting is string str)
    {
        // ...
    }
    else
    {
        str = "default";
    }