以下按预期方式工作:
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'
有人可以解释为什么会这样吗?
答案 0 :(得分:8)
事实上,这似乎不是编译器错误。
以前报告过它是一个错误here。
然而,它已被关闭,因为不是一个错误。原因是因为C#语言规范的这一部分(注意:我在这里引用GitHub上的用户gafter
- 这不是我自己的原创内容):
如果条件逻辑运算符的操作数具有动态编译时类型,则表达式是动态绑定的(动态绑定)。在这种情况下,表达式的编译时类型是动态的,下面描述的解析将在运行时使用具有编译时类型动态的那些操作数的运行时类型进行。
具体而言,&& operation不是编译时布尔短路操作,因为它的右侧操作数是动态类型。
微妙的东西,正如DavidG上面所说,另一个可能避免dynamic
的理由! (而且我必须承认,我仍然不完全相信它是不是一个错误,但那只是我不理解我想的一切......)
答案 1 :(得分:1)
好吧,我一直在思考这个问题,看起来编译器行为非常正确,即使没有dynamic
值,重现它也不是那么难。
然而,还需要一些奇怪的代码。
首先,我们会为&&
类型重载Foo
运算符。直接过载短路逻辑运算符是不可能的,因此我们将分别重载true
,false
和&
。
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
评估为Foo
,foo 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 i
,i
将不会在运行时初始化,但我们将在if
块内。< / p>
由于涉及dynamic
值,因此初始问题相当等效。 foo != null
为dynamic
,foo
为dynamic
,因此意味着&&
应在运行时绑定,我们无法保证{{1}将被初始化。
看起来Matthew从github问题中引用了同样的东西,但我个人从一开始就无法理解它。