组合使用null-checks和Pattern Matches的'if'语句时出错

时间:2018-05-16 09:43:21

标签: c# pattern-matching

以下按预期方式工作:

dynamic foo = GetFoo();

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

但是如果我像这样组合if语句:

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

然后我收到编译器警告

Use of unassigned local variable 'i'

有人可以解释为什么会这样吗?

2 个答案:

答案 0 :(得分:8)

事实上,这似乎不是编译器错误。

以前报告过它是一个错误here

然而,它已被关闭,因为不是一个错误。原因是因为C#语言规范的这一部分(注意:我在这里引用GitHub上的用户gafter - 这不是我自己的原创内容):

  

如果条件逻辑运算符的操作数具有动态编译时类型,则表达式是动态绑定的(动态绑定)。在这种情况下,表达式的编译时类型是动态的,下面描述的解析将在运行时使用具有编译时类型动态的那些操作数的运行时类型进行。

具体而言,&& operation不是编译时布尔短路操作,因为它的右侧操作数是动态类型。

微妙的东西,正如DavidG上面所说,另一个可能避免dynamic的理由! (而且我必须承认,我仍然不完全相信它是不是一个错误,但那只是我不理解我想的一切......)

答案 1 :(得分:1)

好吧,我一直在思考这个问题,看起来编译器行为非常正确,即使没有dynamic值,重现它也不是那么难。

然而,还需要一些奇怪的代码。

首先,我们会为&&类型重载Foo运算符。直接过载短路逻辑运算符是不可能的,因此我们将分别重载truefalse&

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();

最初我们在foo != null && foo is Foo i块中有一个表达式if,现在我们希望它&&绑定到我们的重载。出于这个原因,我们会重载!=运算符和==以及它们应该始终配对。

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

现在foo != null评估为Foofoo is Foo评估为bool,但我们的&&重载已签名(Foo, Foo) - 仍然不匹配,将为bool的隐式转换添加一个重载。

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

以下是我们到目前为止所拥有的Foo类型的代码

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();
}

瞧!我们对这件作品有同样的错误。

 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();

事实上,如果我们使用foo is string i代替foo is Foo ii将不会在运行时初始化,但我们将在if块内。< / p>

由于涉及dynamic值,因此初始问题相当等效。 foo != nulldynamicfoodynamic,因此意味着&&应在运行时绑定,我们无法保证{{1}将被初始化。

看起来Matthew从github问题中引用了同样的东西,但我个人从一开始就无法理解它。