Why is `foo is not { } bar` not semantically equivalent to `!(foo is { } bar)`?

Viewed 127

Let's have string foo variable in C#.

Why are the following statements not semantically equivalent?

if (!(foo is { } bar) || bar.Length == 0)

and

if (foo is not { } bar || bar.Length == 0)

For foo = null, both appear to behave identically.

var foo = null;

if (!(foo is { } bar) || bar.Length == 0)
{
    Console.WriteLine("bar");
}
  
if (foo is not { } baz || baz.Length == 0)
{
    Console.WriteLine("baz");
}

But for foo = "something", the first statement works, but the second fails with NullReferenceException.

Check sharplab.

Is it a feature?

Edit: It seems that it was a bug: https://github.com/dotnet/roslyn/issues/51996#issuecomment-803508111

1 Answers

From the comments on the question:

Could you be running into an issue with definite assignment? Per the docs, pattern variables are only considered definitely assigned when the is pattern is true. The changelog links to a number of git issues on the subject.

More insight from the docs:

Should we elect to defer such work until later (which I advise), we could say in C# 9 beneath a not or or, pattern variables may not be declared. Then, we would have time to develop some experience that would provide insight into the possible value of relaxing that later.

Related