文档暗示out参数在发送到函数之前不需要初始化(仅声明).但是,这段代码:
class Program
{
static void Main()
{
dynamic p = "";
string s;
if (p != null && T(out s))
System.Console.WriteLine(s);
}
static bool T(out string s)
{
s = "";
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
给出构建错误:
使用未分配的局部变量's'
只有当p是dynamic.如果p输入为string或object,则不会产生错误.
方法T 在返回之前需要设置变量,因此这个错误对我来说似乎是一种混乱(请注意,即使使用短循环&&,第二个语句也必须执行才能执行"then"块).
注意:您也可以下载此repro repo进行复制.
那么,这是一个合法的错误(我在C#7.0上)?我该怎么处理?
Eri*_*ert 12
更新:这个问题是我2018年11月的博客主题.感谢有趣的问题!
该文档意味着在将
out参数发送到方法之前不需要初始化(仅声明)参数.
那是对的.此外,传递给out参数的变量在调用返回时是明确赋值的,因为正如您所注意到的:
方法T需要在返回之前设置变量,所以这个错误对我来说似乎是个蠢货
好像那样,不是吗?外表会骗人!
注意,即使有短路
&&,第二个表达式也必须执行才能执行"后果"块if.
令人惊讶的是,这是假的.即使调用T不执行,也有一种方法可以执行结果.这样做需要我们严重滥用C#的规则,但我们可以,所以让我们这样做吧!
代替
dynamic p = "";
string s;
if (p != null && T(out s))
System.Console.WriteLine(s);
Run Code Online (Sandbox Code Playgroud)
干的好
P p = new P();
if (p != null && T())
System.Console.WriteLine("in the consequence");
Run Code Online (Sandbox Code Playgroud)
并给出一个定义class P导致该程序运行结果但不运行调用T.
我们要做的第一件事是p != null转换为方法调用而不是null检查,并且该方法不能返回bool:
class P
{
public static C operator ==(P p1, P p2)
{
System.Console.WriteLine("P ==");
return new C();
}
public static C operator !=(P p1, P p2)
{
System.Console.WriteLine("P !=");
return new C();
}
}
Run Code Online (Sandbox Code Playgroud)
我们必须既超载==和!=C#中的同一时间.覆盖Equals并且GetHashCode是一个好主意但不是一个要求,这个程序中的任何内容都不是一个好主意,所以我们将跳过它.
好的,我们现在有了if (something_of_type_C && T()),而且既然C没有bool,我们就需要覆盖&&运营商.但C#不允许您&&直接覆盖运算符.让我们离题一刻,谈谈语义&&.对于布尔返回功能A与B,的语义bool result = A() && B();是:
bool a = A();
bool c;
if (a == false) // interesting operation
c = a;
else
{
bool b = B();
c = a & b; // interesting operation
}
bool r = c;
Run Code Online (Sandbox Code Playgroud)
因此,我们生成三个临时工,a,b,和c,我们评估的左侧A(),我们检查,看看是否a是假的.如果是,我们使用它的价值.如果没有,我们计算B()然后计算a & b.
该工作流中仅有两种特定于bool类型的操作是检查虚假和非短路&,因此*那些是在用户定义中重载的操作&&.C#要求你重载三个操作:用户定义&,用户定义"我是真的吗?" 用户定义"我是假的?".(喜欢==和!=,最后两个必须成对定义.)
现在,一个明智的人会写operator true,operator false所以他们总是回到对立面.我们今天不是明智的人:
class C
{
public static bool operator true(C c)
{
System.Console.WriteLine("C operator true");
return true;
}
public static bool operator false(C c)
{
System.Console.WriteLine("C operator false");
return true; // Oops
}
public static C operator &(C a, C b)
{
System.Console.WriteLine("C operator &");
return a;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我们还要求用户定义的&两个Cs并返回a C,它确实如此.
好的,所以,我们记得
if (p != null && T())
Run Code Online (Sandbox Code Playgroud)
并且p != null是类型C.所以我们现在必须将其生成为:
C a = p != null; // Call to P.operator_!=
C c;
bool is_false = a is logically false; // call to C.operator_false
if (is_false)
c = a;
else
{
bool b = T();
c = a & b; // Call to C.operator_&
}
Run Code Online (Sandbox Code Playgroud)
但现在我们遇到了问题.operator &需要两个Cs并返回一个C,但我们有一个bool返回T.我们需要一个C.没问题,我们将添加一个隐含的用户定义的转换到C从bool:
public static implicit operator C(bool b)
{
System.Console.WriteLine("C implicit conversion from bool");
return new C();
}
Run Code Online (Sandbox Code Playgroud)
好的,我们现在的逻辑是:
C a = p != null; // Call to P.operator_!=
C c;
bool is_false = C.operator_false(a);
if (is_false)
c = a;
else
{
bool t = T();
C b = t; // call to C.operator_implicit_C(bool)
c = a & b; // Call to C.operator_&
}
Run Code Online (Sandbox Code Playgroud)
记住我们要走的是:
if (c)
System.Console.WriteLine("in the consequence");
Run Code Online (Sandbox Code Playgroud)
我们如何计算?C#的原因,如果你有operator true,C那么你应该能够if通过简单的调用在条件中使用它operator true.所以完成它,最终我们有语义:
C a = p != null; // Call to P.operator_!=
C c;
bool is_false = C.operator_false(a);
if (is_false)
c = a;
else
{
bool t = T();
C b = t; // call to C.operator_implicit_C(bool)
c = a & b; // Call to C.operator_&
}
bool is_true = C.operator_true(c);
if (is_true) …
Run Code Online (Sandbox Code Playgroud)
但正如我们在这个疯狂的例子中看到的那样,我们可以输入结果而if不会调用T没有问题,operator false并且operator true两者都返回true.当我们运行程序时,我们得到:
P !=
C operator false
C operator true
in the consequence
Run Code Online (Sandbox Code Playgroud)
一个明智的人永远不会在同时C被认为既真实又错误的情况下编写代码,但今天像我这样一个不明智的人可以,并且编译器知道,因为我们设计编译器是正确的,无论是否程序是明智的.
这就解释了为什么if (p != null && T(out s))说s可以在结果中取消分配.如果p是,dynamic则编译器原因" p可能是运行时这些疯狂类型之一,在这种情况下,我们不再使用bool操作数,因此s可能不会被分配".
故事的寓意是:dynamic使编译器对可能发生的事情保持极为保守 ; 它必须承担最坏的情况.在这种特殊情况下,它假设p != null可能不为空引用检查,可能不会是bool,这operator true和operator false可能都回报true.
那么,这是一个合法的错误(我在C#7.0上)?
编译器的分析是正确的 - 相信我,这不是编写或测试的简单逻辑.
你的代码有bug; 修理它.
我该怎么处理?
如果你想对a进行空参考检查dynamic,你最好的选择是:如果你这样做会伤害,不要这样做.
抛弃dynamic并返回object,然后进行引用等式检查: if (((object)p) == null && …
或者,另一个很好的解决方案是使它非常明确:
if (object.ReferenceEquals((object)p, null) && …
这些是我的首选解决方案.更糟糕的解决方案是将其分解:
if (p != null)
if (T(out string s))
consequence
Run Code Online (Sandbox Code Playgroud)
现在operator &即使在最坏的情况下也没有被召唤.请注意,在这种情况下,我们仍然可以处于p != nulltrue和pnull的情况,因为没有任何东西可以阻止任何人重载!=,无论其操作数如何都始终返回true.
| 归档时间: |
|
| 查看次数: |
394 次 |
| 最近记录: |