2011-01-11 8 views
4

Lange Geschichte kurz. Ich habe 2 Listen, die den gleichen Typ enthalten (aber für verschiedene Dinge verwendet werden) und ich möchte wissen, ob die EITHER-Liste einen Artikel mit einem bestimmten Namen enthält.Lambda Ausdruck Kuriosität

Meine ursprüngliche Code, der prefectly arbeitete, war:

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

Dieser Code perfekt funktioniert, ob das Element in einer der beiden Listen vorhanden war. Später hatte ich mehrere "unmögliche" Abstürze - Dinge, die nie hätte passieren können. Ich verfolgte es zurück, wenn die Anweisung NIE wahr zurückgibt.

Das hat mich schon ewig beschäftigt ... Ich konnte nicht herausfinden, was falsch lief. Schließlich gab ich es auf und steckte Klammern um den Körper der Lamda Ausdrücke wie so ...

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

nach erneuter läuft mein Programm, alle ‚unmöglich‘ Fehler ging weg und es funktioniert normal. Durch das Entfernen der zusätzlichen Backets werden die Fehler erneut angezeigt.

Ich hatte noch nie dieses Problem mit Lambda-Ausdrücke (vor allem, wo sie arbeiten und dann, nach mehreren Läufen funktioniert ordnungsgemäß Pause) und meine anderen Lambda-Ausdrücke funktionieren ordnungsgemäß.

Beispiel: Der folgende Code funktioniert 100%, wie erwartet (Unter der Annahme, dass es eine Übereinstimmung in einer der Listen ist)

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

Was ist los? Warum ist der Compiler wählerisch über einige Lambda-Ausdrücke und nicht andere? (Auch wenn sie identisch sind?) ???

UPDATE :: Systemdetails Dies wurde mit Microsoft Visual Studio 2008 (Professional), Windows Vista 32bit festgestellt.

UPDATE Video link, hat dieser Test auf anderen Computern gewesen und ist NICHT reproduzierbare. Ich fühle mich wie mein Computer verloren ist. Die Neuinstallation von VS hat keine Auswirkungen.

Bitte ignoriere Hintergrundgeräusche, sie miaut nur viel, wenn sie mich etwas aufzeichnen hört.

+3

Ist das der tatsächliche verbatim-Code, der sich merkwürdig verhält? – LukeH

+1

Wenn Sie dies tatsächlich reproduzieren können, sollten Sie auf Ihrem eigenen Computer mehr Schritte unternehmen, um das Problem zu identifizieren, z. Was unterscheidet den Bytecode beim Hinzufügen oder Entfernen der Klammern? – mquander

+0

Der Code ist wörtlich, mit der Ausnahme, dass die Namen der Listen geändert wurden, um das Auslesen des Kontexts zu erleichtern, und der tatsächliche Typ heißt "Variable", nicht "Item". – James

Antwort

3

Diese zusätzlichen Klammern ändern nicht die Bedeutung dieser Lambda-Ausdrücke und auch nicht, wie der Compiler diese zwei verschiedenen Code-Blöcke kompiliert. Basierend auf dem, was Sie zur Verfügung gestellt haben, scheint es keinen funktionalen Unterschied zwischen Ihren beiden Möglichkeiten zu geben und somit ist das Problem unabhängig von den Klammern.

EDIT:

Also habe ich eine schnelle Klasse, die genau das tut, was Sie gesagt, und die IL unterscheidet sich nur in Klassennamen. Ich habe ildasm und windiff verwendet, um dies zu bestätigen. Es ändert sich auch nicht mit /optimize- oder /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 

Nota bene: gibt es zwei davon erzeugt, eine für jede der listX.Any Anrufe. Beide unterscheiden sich nur in ihren Kommentaren.Die generierte IL ist identisch.

EDIT 2:

Die Ausgabe von ildasm für Visual Studio 2008 (csc 3.5.30729.4926) ist auch nicht anders, so dass ich wirklich nicht in der Lage, warum von VS2k8 in Ihrem Beispiel zu sagen, stirbt es außer vielleicht Die Version von LINQ, die Sie haben, ist abgehört oder der Compiler auf diesem Computer hat einen abgeimpften Ausdruck-Generator, weil ich keinen Unterschied replizieren kann.

+0

Darum geht es in der Frage. Es sollte keinen Unterschied geben – James

+0

Nebenbei sollten Sie wahrscheinlich einen besseren Variablennamen als 'var' wählen. Während es nur ein kontextabhängiges Schlüsselwort ist, ist etwas wie "item" oder "xx" usw. wahrscheinlich eine bessere Wahl. – user7116

+3

James Ich sage da ** ist ** kein Unterschied. Der Unterschied liegt woanders, wenn das wirklich dein Code ist. – user7116