2010-06-01 2 views
3

рассмотрим следующий пример:Проблемное поведение Linq Union?

public IEnumerable<String> Test() 
    { 
     IEnumerable<String> lexicalStrings = new List<String> { "test", "t" }; 
     IEnumerable<String> allLexicals = new List<String> { "test", "Test", "T", "t" }; 

     IEnumerable<String> lexicals = new List<String>(); 
     foreach (String s in lexicalStrings) 
      lexicals = lexicals.Union (allLexicals.Where (lexical => lexical == s)); 

     return lexicals; 
    } 

Я надеялся, что производить «тест», «т» в качестве выходного сигнала, но это не делает (Выход только «т»). Я не уверен, но, возможно, придется что-то сделать с отложенной обработкой. Любые идеи, как заставить это работать или для хорошей альтернативы?

Редактировать: Обратите внимание, что это просто упрощенный пример. lexicalStrings и allLexicals - это разные типы в исходном коде. Поэтому я не могу их напрямую комбинировать.

Edit2 проблема решить выглядит примерно так:

public IEnumerable<Lexical> Test() 
    { 
     IEnumerable<String> lexicalStrings = new List<String> { "test", "t" }; 
     IEnumerable<Lexical> allLexicals = new List<Lexical> { ... }; 

     IEnumerable<Lexical> lexicals = new List<Lexical>(); 
     foreach (String s in lexicalStrings) 
      lexicals = lexicals.Union (allLexicals.Where (lexical => lexical.Text == s)); 

     return lexicals; 
    } 

ответ

3

Вы используете неправильную операцию другого ответа объяснения. Но все же интересно, почему ваш код работает некорректно, несмотря на то, что он выглядит хорошо.

давайте изменим ваше приложение немного:

 IEnumerable<String> lexicalStrings = new List<String> { "test", "t" }; 
     IEnumerable<String> allLexicals = new List<String> { "test", "Test", "T", "t" }; 

     IEnumerable<String> lexicals = new List<String>(); 
     foreach (String s in lexicalStrings) 
     { 
      lexicals = lexicals.Union(
       allLexicals.Where(
       lexical => 
       { 
        Console.WriteLine(s); 
        return lexical == s; 
       } 
       ) 
      ); 
     } 
     Console.WriteLine(); 
     foreach (var item in lexicals) 
     { 
     } 

, что выход вы ожидаете? вот он:

t 
t 
t 
t 
t 
t 
t 
t 

интересно, не так ли?

Теперь давайте изменим его снова:

IEnumerable<String> lexicalStrings = new List<String> { "test", "t" }; 
    IEnumerable<String> allLexicals = new List<String> { "test", "Test", "T", "t" }; 

    IEnumerable<String> lexicals = new List<String>(); 
    foreach (String s in lexicalStrings) 
    { 
     string ls = s; 
     lexicals = lexicals.Union(
      allLexicals.Where(
      lexical => 
      { 
       Console.WriteLine(ls); 
       return lexical == ls; 
      } 
      ) 
     ); 
    }    
    foreach (var item in lexicals) 
    {     
    } 

теперь выходные и результаты прекрасны:

test 
test 
test 
test 
t 
t 
t 
t 

Почему это происходит? Вы используете закрытие - использование внешнего var во внутренней лямбда. Поскольку вы фактически не повторяете свою последовательность, текущее значение s не попадает в лямбда. foreach выходы и все внутренние копии s удерживают значение последней итерации. В случае внутренней переменной они сохраняют значения, которые создаются для каждой итерации. Этот конфликт исходит из внутренней ленивости LINQ. Если вы делаете что-то вроде List.AddRange, результат внутри цикла будет хорошим, потому что List.AddRange заставляет итерацию.

+0

Спасибо за объяснение и легко исправить! Вверх и ответили. – Foxfire

+0

Я думал об использовании AddRange, но это приводит к дублированию в списке (который следует избегать автоматически). – Foxfire

+0

@Foxfire вы действительно должны использовать intersect, он решает проблему – Andrey

0

Является ли это то, что вы пытаетесь достичь?

lexicals.Union(allLexicals).Distinct(StringComparer.OrdinalIgnoreCase) 

EDIT:

Или еще лучше, как предложил @ Dave:

lexicals.Intersect(allLexicals, StringComparer.OrdinalIgnoreCase)

EDIT 2:

Если они разные типы один из них должен реализовывать IEqualityComparer к другому. Затем пройти этот класс метод Intersect:

lexicals.Intersect(allLexicals, new MyCustomTComparer())

+0

Я добавил еще несколько пояснений к вопросу, потому что lexicalStrings и allLexicals - это разные типы в исходном коде. – Foxfire

+0

это должно быть в комментариях, потому что вопрос о том, почему прекрасный код не работает.и проблема с закрытием – Andrey

1
public IEnumerable<Lexical> Test() 
{ 
    var lexicalStrings = new List<String> { "test", "t" }; 
    var allLexicals = new List<Lexical> { ... }; 

    var lexicals = new List<Lexical>(); 
    foreach (string s in lexicalStrings) 
    { 
     lexicals.AddRange(allLexicals.Where (lexical => lexical.Text == s)); 
    } 

    return lexicals; 
} 
+0

Я добавил еще несколько пояснений к вопросу, потому что lexicalStrings и allLexicals являются разными типами в исходном коде. – Foxfire

+0

Они должны использовать общий базовый тип, или вы должны лить/конвертировать, чтобы объединить их в свой результат. У вас не может быть IEnumerable двух совершенно разных типов. Вы можете передать IEqualityComparer для пересечения, поэтому просто напишите, что делает сравнение равенства (например, lexical == s в вашем примере) –

+0

Я не хочу их объединять. Я хочу объединить результаты ряда предложений linq «where» (пожалуйста, посмотрите на нижний пример, который работает в gerenal. Он просто не доставляет ожидаемого результата). – Foxfire

Смежные вопросы