Error combining 'if' statements that null-checks and Pattern Matches

前端 未结 2 952
太阳男子
太阳男子 2021-01-11 12:50

The following works as expected:

dynamic foo = GetFoo();

if (foo != null)
{
    if (foo is Foo i)
    {
        Console.WriteLine(i.Bar);
    }
}

相关标签:
2条回答
  • 2021-01-11 13:22

    Ok, I was thinking for some time about this issue, and it looks that compiler behavior is pretty damn correct and it's not so hard to reproduce it even without dynamic values.

    Some strange code will be required, nevertheless.

    To start with we will overload && operator for our Foo type. It's not possible to overload short circuit logical operators directly, so we will overload true, false and & separately.

    public static bool operator true(Foo x) => true;
    public static bool operator false(Foo x) => true;
    public static Foo operator &(Foo foo, Foo val) => new Foo();
    

    Initially we had an expression foo != null && foo is Foo i in if block, now we want && from it to bound to our overload. For this reason we will overload != operator and == as well as they should be paired always.

    public static Foo operator !=(Foo val, Foo val2) => new Foo();  
    public static Foo operator ==(Foo val, Foo val2) => new Foo();
    

    For now foo != null evaluates to Foo and foo is Foo evaluates to bool, but our && overload has signature (Foo, Foo) — still a mismatch, will add one more overload for implicit conversion from bool.

    public static implicit operator Foo(bool val) => new Foo();
    

    Here is the code for Foo type we've got so far

    class Foo
    {
        public static bool operator true(Foo x) => true;
        public static bool operator false(Foo x) => true;
        public static Foo operator &(Foo foo, Foo val) => new Foo();
    
        public static implicit operator Foo(bool val) => new Foo();
    
        public static Foo operator !=(Foo val, Foo val2) => new Foo();
        public static Foo operator ==(Foo val, Foo val2) => new Foo();
    }
    

    And voila! We have the same error for this piece.

     static void Main(string[] args)
     {
         Foo foo = GetFoo();
    
         if (foo != null && foo is Foo i)
         {
             // Use of unassigned local variable i
             // Local variable 'i' might not be initialized before accessing 
             Console.WriteLine(i);
         }
     }
    
     static Foo GetFoo() => new Foo();
    

    And indeed, if we for example use foo is string i instead of foo is Foo i, i won't be initialized at runtime, but we will be inside if block.

    Initial issue is rather equivalent, because of dynamic values involved. foo != null is dynamic, as far as foo is dynamic, so it means && should be bound at runtime, and we have no guarantees that i will be initialized.

    Looks like Matthew quoted the same thing from github issue, but I personally wasn't able to grasp it from the beginning.

    0 讨论(0)
  • 2021-01-11 13:27

    It would appear that this is not, in fact, a compiler error.

    It was previously reported as a bug here.

    However, it has been closed as not a bug. The reason is because of this part of the C# language spec (note: I am quoting here from user gafter on GitHub - this is NOT original content from myself):

    If an operand of a conditional logical operator has the compile-time type dynamic, then the expression is dynamically bound (Dynamic binding). In this case the compile-time type of the expression is dynamic, and the resolution described below will take place at run-time using the run-time type of those operands that have the compile-time type dynamic.

    Specifically, the && operation is not a compile-time boolean short-circuiting operation because its right-hand operand is of type dynamic.

    Subtle stuff, and as DavidG says above, another reason to avoid dynamic where possible! (And I must confess, I'm still not completely convinced it's not a bug, but that's just me not understanding everything I guess...)

    0 讨论(0)
提交回复
热议问题