For some reason, M1() causes a compiler error, while M2(), which does the same thing, causes no error. Any idea why?
Using false ==>
The issue here is with the way the compiler handles "definitely assigned when true". ! inverts that; == false doesn't. So for the code:
if (!(obj is int i))
return;
System.Console.WriteLine(i);
The compiler can infer that if obj is int i is false, the ! inverts that, thus return will occur if it's not an int. Therefore i can be allowed to "leak" into subsequent code safely.
However, the same rules do not apply to == false. Whilst semantically identical to a human reader of the code, the compiler treats ! and == false as very different things. So for:
if (false == (obj is int i))
the compiler baulks and takes the view it cannot know the assignment state of i, thus the error.
For a discussion on this, please see Incorrect "Use of unassigned local variable" for (x is T y) == false.
The moral of the story: avoid comparing to false and use ! when using C# patterns.
EDIT
It should be noted that == false is not a special case here. Any use of == removes the ability of the compiler to determine "definitely assigned when true". For example, the following code compiles:
object x = 1;
if (!(x is bool y))
return 0;
var z = y;
But add a == true and it no longer does:
object x = 1;
if (!(x is bool y == true))
return 0;
var z = y; // error: use of an unassigned variable
EDIT2
Incidently, for anyone who uses if (expression == false) because they find if (!expression) difficult to read, you may be interested to know that the syntax, if !(expression) is being considered for C# 8.