当前位置: 首页 > 编程语言 > C#

非短路布尔运算符和C#7模式匹配分享

时间:2023-04-10 22:28:38 C#

非短路布尔运算符和C#7模式匹配我目前正在编写一个针对.NET4.7(C#7)的C#应用程序。在尝试使用“is”关键字声明变量的新方法后,我感到困惑:if(variableisMyClassclassInstance)thiswayif(variableisMyClassclassInstance),但是在执行时:if(true&variableisMyClassclassInstance){vara=类实例;VisualStudio(我正在使用2017)向我显示错误Useofunassignedlocalvariable'classInstance'。使用&(&&)的简短版本,它工作正常。我是否缺少有关&运算符的内容?(我知道使用短路版本更常见,但此时我只是好奇)这个让我头疼,但我想我已经弄明白了。这种混淆是由两个怪癖引起的:is操作使变量未声明(非空)的方式,以及编译器优化掉布尔运算而不是位运算的方式。Quirk1.如果转换失败,则变量未分配(非空)根据新文档的语法:如果exp为真并与if语句一起使用,则分配varname并仅在if语句中具有局部范围。如果您从字里行间读到,这意味着如果转换失败,该变量将被视为未分配。这可能违反直觉(有些人可能认为它是空的)。这意味着如果整个if子句在没有类型匹配的情况下计算结果可能为真,则if块中依赖于该变量的任何代码都不会编译。因此,例如这样编译:if(instanceisMyClassy){varx=y;}这个编译:if(true&&instanceisMyClassy){varx=y;}但这不是:voidTest(boolf){if(f&&instanceisMyClassy){varx=y;//Error:Useofunassignedlocalvariable}}Quirk2.当编译器检测到预定义的布尔值时,布尔运算被优化掉,而二进制运算则没有。因此,编译器不会发出无法访问的代码,从而跳过某些验证。例如:这个编译:voidTest(boolf){objectneverAssigned;if(false&&f){varx=neverAssigned;//OK(从不执行)}}但是如果你使用&而不是&&,它不会编译:voidTest(boolf){objectneverAssigned;if(false&f){varx=neverAssigned;//Error:Useofunassignedlocalvariable}}当编译器看到类似true&&的东西时,它会完全忽略它。因此if(true&&instanceisMyClassy)与if(instanceisMyClassy)完全相同,但是这个if(true&instanceisMyClassy)是不一样的。编译器仍然需要发出执行&操作的代码,并在条件语句中使用其输出。或者即使它没有,当前的C#7编译器显然会执行与它相同的验证。这可能看起来有点奇怪,但请记住,当您使用&而不是&&时,可以保证&必须执行,虽然在这个示例中它可能看起来不重要,但通常必须允许其他复杂情况,例如运算符重载。怪癖如何组合在最后一个示例中,if子句的结果是在运行时确定的,而不是编译时。因此,在if块的内容被执行之前,编译器无法确定y最终是否会被赋值。所以你得到if(true&instanceisMyClassy){varx=y;//Error:useofunassignedlocalvariable}TLDR在复合逻辑操作的情况下,c#无法确定当且仅当转换成功时整体if条件是否会解析为真。如果不确定,它不允许访问该变量,因为它可能未分配。当表达式可以在编译时简化为非复合操作时引发异常,例如通过删除true&&。解决方法我认为我们使用新语法的方式是将if子句用作单个条件。在开头添加true&&是有效的,因为编译器只是将其删除。但是,与新语法结合的任何其他内容都会导致在运行代码块时是否未分配新变量产生歧义,编译器不允许这样做。解决方法当然是嵌套条件而不是组合它们:将不起作用:voidTest(boolf){if(f&instanceisMyClassy){varx=y;//错误:使用未分配的局部变量}}将起作用好:voidTest(boolf){if(f){if(instanceisMyClassy){varx=y;//Works}}}我是否遗漏了关于&运算符的内容?不,我不这么认为。你的期望对我来说似乎是正确的。考虑这个替代示例,它编译没有错误:objectvariable=null;我的类类实例;if(true&((classInstance=variableasMyClass)!=null)){vara=classInstance;}varb=classInstance;(对我来说,考虑if主体之外的赋值更有趣,因为短路会影响行为。)通过显式赋值,编译器将a和b中的赋值classInstance识别为显式赋值。它应该能够用新语法做同样的事情。逻辑与,短路与否无关紧要。您的第一个值为true,因此应该始终需要评估后半部分以获得整个表达式。正如您所注意到的,编译器确实以不同方式对待&和&&,这是出乎意料的。这段代码的变化是:staticvoidM3(){objectvariable=null;if(true|variableisMyClassclassInstance){vara=classInstance;}}编译器正确地将classInstance识别为||但同样明显的不当行为|(即还声明classInstance未明确分配),即使使用非短路运算符,也必须进行分配。同样,上面的工作正确分配是明确的,而不是使用新语法。如果它只是明确的赋值规则没有被新语法解决,那么我希望&&和&一样被破坏。但事实并非如此。编译器正确处理它。事实上,在函数文档中(我犹豫说“规范”,因为还没有ECMA批准的C#7规范),它是这样写的:type_pattern既测试表达式是否属于给定类型,如果测试成功,将它转换为该类型。这引入了由给定标识符命名的给定类型的局部变量。当模式匹配操作的结果为真时,这个局部变量被显式赋值。[强调我的]由于短路会在没有模式匹配的情况下产生正确的行为,并且由于模式匹配会在没有短路的情况下产生正确的行为(并且明确的分配在特征描述中明确解决),我会说这是立即行为编译器错误。非短路布尔运算符之间可能存在一些被忽视的交互,以及模式匹配表达式的评估方式,这会导致在改组中丢失明确的分配。您应该考虑向当局报告。我想这些天,RoslynGitHub问题跟踪器是跟踪此类事情的地方。如果您在报告中解释如何找到它以及为什么特定语法在您的场景中很重要,这可能会有所帮助(因为在您发布的代码中,&&运算符等效地工作......非短路并且似乎不给它任何代码善良)。这是由于明确赋值的规则,它有&&的特例,但&没有特例。我相信它按照C#设计团队的预期工作,但规范可能还有一些工作要做,至少在清晰度方面。根据ECMAC#5标准,第5.3.3.24节:对于expr-first&&expr-second形式的expr-first&&expr-second:...expr之后v的明确赋值状态由:相关部分确定本例中的第i个部分以粗体突出显示。classInstance是“definitelyassignedafterreal-expression”模式(上面的expr-second),前面的情况都不适用,所以条件结束时的整体状态是“definitelyassignedafterreal-expression”。这意味着在if语句的主体中,它是显式赋值的。&运算符没有等效子句。虽然可能存在,但所涉及的类型会很复杂——它只需要在与bool表达式一起使用时应用于&运算符,而且我认为大多数其他显式赋值不会以这种方式处理类型。请注意,您不需要使用模式匹配来查看它。考虑以下程序:usingSystem;类程序{staticvoidMain(){boola=false;布尔x;布尔y=真;if(true&(y&&(x=a))){Console.WriteLine(x);}}}表达式y&&(x=a)是另一个表达式,其中x最终被“在真正的表达式之后明确赋值”。此外,上面的代码不会编译,因为没有显式赋值,而如果将&更改为&&,它会编译。所以至少这不是模式匹配本身的问题。令我困惑的是,由于5.3.3.21(“5.3.3.21带有嵌入表达式的表达式的一般规则”),x仍然没有“在真实表达式之后明确分配”,其中包含:我怀疑这只是包含“明确分配”或“未显式分配”,而不是包括“显式分配后的真实表达式”部分——尽管它没有应有的清晰。以上就是C#学习教程分享的全部内容:非短路布尔运算符和C#7模式匹配。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: