【问题标题】:Lambda expression oddityLambda 表达式异常
【发布时间】:2011-01-11 15:36:56
【问题描述】:

长话短说。我有 2 个包含相同类型(但用于不同事物)的列表,我想知道其中一个列表是否包含具有特定名称的项目。

我的原始代码运行良好,是:

if (listA.Any(var => var.Name == strMatch) || listB.Any(var => var.Name == strMatch))
{
    //Do something
}

此代码运行良好,无论该项目出现在一个或两个列表中。 后来,我发生了几次“不可能”的崩溃——这些事情永远不会发生。 我将其追溯到 if 语句 NEVER 返回 true。

这让我很困惑......我无法弄清楚出了什么问题。最终我放弃了,像这样在 lamda 表达式的主体周围贴上了括号......

if (listA.Any(var => (var.Name == strMatch)) || listB.Any(var => (var.Name == strMatch)))
{
    //Do something
}

重新运行我的程序后,所有“不可能”的错误都消失了,并且运行正常。删除多余的备份会导致错误再次出现。

我以前从未遇到过 lambda 表达式的这个问题(尤其是在它们工作的地方,然后,在几次运行正常工作后中断)并且我的其他 lambda 表达式工作正常。

示例:以下代码按预期 100% 工作(假设其中一个列表中有匹配项)

Item item =
    ListA.FirstOrDefault(var => var.Name == strMatch) ??
    ListB.FirstOrDefault(var => var.Name == strMatch);

发生了什么事?为什么编译器对 some lamda 表达式而不是其他表达式挑剔? (即使它们相同?)???

更新 :: 系统详情 这在 Microsoft Visual Studio 2008(专业版)、Windows Vista 32 位中遇到过。

更新 Video link,这已经在其他计算机上进行了测试,并且不可重现。让我觉得我的电脑注定要失败。重装VS没有效果。

请忽略任何背景猫的声音,她只会在听到我录东西时才叫。

【问题讨论】:

  • 这是表现异常的实际逐字代码吗?
  • 如果你真的可以重现这个,你应该在你自己的机器上采取更多的步骤来识别问题,例如。添加或删除括号时,字节码有什么不同?
  • 代码是逐字逐句的,除了更改列表的名称以便更容易地阅读上下文之外,实际类型称为“变量”,而不是“项目”。
  • @mquander 肯定是指 IL 代码,因为很少有人阅读 .net ByteCode?​​span>
  • 您在代码中还做了什么?你是多线程吗?您是否在某处使用了不安全的代码段?我怀疑损坏的数据或代码是导致这种行为的原因,而不是编译器。

标签: c# lambda operator-precedence


【解决方案1】:

这些额外的括号不会改变那些 lambda 表达式的含义,也不会改变编译器如何选择编译这两个不同的代码块。根据您提供的内容,您的两个选择之间似乎没有功能差异,因此问题与括号无关。

编辑:

所以我创建了一个与您所说的完全一样的快速类,IL 仅在类名上有所不同。我使用ildasmwindiff 来确认这一点。 /optimize-/debug:full 也不会改变。

  .method /*06000005*/ public hidebysig 
          instance bool  '<Main>b__3'(class [mscorlib/*23000001*/]System.Tuple`2/*01000006*/<int32,string> var) cil managed
  // SIG: 20 01 02 15 12 19 02 08 0E
  {
    // Method begins at RVA 0x207c
    // Code size       22 (0x16)
    .maxstack  2
    .locals /*11000001*/ init ([0] bool CS$1$0000)
-   .line 29,29 : 69,92 ''
+   .line 29,29 : 67,88 ''
-   //000029:       if (listA.Any(var => (var.Item2 == strMatch)) || listB.Any(var => (var.Item2 == strMatch)))
+   //000029:       if (listA.Any(var => var.Item2 == strMatch) || listB.Any(var => var.Item2 == strMatch))
    IL_0000:  /* 03   |                  */ ldarg.1
    IL_0001:  /* 6F   | (0A)000005       */ callvirt   instance !1 class [mscorlib/*23000001*/]System.Tuple`2/*01000006*/<int32,string>/*1B000001*/::get_Item2() /* 0A000005 */
    IL_0006:  /* 02   |                  */ ldarg.0
-   IL_0007:  /* 7B   | (04)000001       */ ldfld      string Parentheses/*02000002*//'<>c__DisplayClass6'/*02000003*/::strMatch /* 04000001 */
+   IL_0007:  /* 7B   | (04)000001       */ ldfld      string NoParentheses/*02000002*//'<>c__DisplayClass6'/*02000003*/::strMatch /* 04000001 */
    IL_000c:  /* 28   | (0A)000006       */ call       bool [mscorlib/*23000001*/]System.String/*01000007*/::op_Equality(string, string) /* 0A000006 */
    IL_0011:  /* 0A   |                  */ stloc.0
    IL_0012:  /* 2B   | 00               */ br.s       IL_0014

注意事项:其中生成了两个,每个 listX.Any 调用一个。两者仅在其 cmets 上有所不同。生成的 IL 是相同的。

编辑 2:

ildasm 对于 Visual Studio 2008 (csc 3.5.30729.4926) 的输出也没有什么不同,所以我真的无法说明为什么在您的 VS2k8 实例中它会死掉,除了您拥有的 LINQ 版本可能有问题或者那台机器上的编译器有一个错误的表达式生成器,因为我无法复制任何差异。

【讨论】:

  • 这就是问题所在。应该没有区别
  • 顺便说一句,您可能应该选择一个比var 更好的变量名。虽然它只是一个上下文关键字,但 itemxx 等可能是更好的选择。
  • James 我是说 没有区别。如果那确实是您的代码,那么区别就在其他地方。
  • 詹姆斯,你的电脑没有魔法。您的源代码对正在运行的程序的唯一影响是通过编译的可执行文件。如果编译成同样的东西,没有区别,编译器也不“挑剔”。
  • 查看视频并告诉我它不挑剔。花了几个小时让该死的软件录制,但你看到的内容是未经编辑的(实际上我撒谎,我修剪了你看到我点击录制按钮的前 3 秒)
猜你喜欢
  • 1970-01-01
  • 2014-10-19
  • 1970-01-01
  • 1970-01-01
  • 2017-03-16
  • 1970-01-01
  • 1970-01-01
  • 2012-01-07
  • 1970-01-01
相关资源
最近更新 更多