Проблемное поведение Linq Union? - PullRequest
3 голосов
/ 01 июня 2010

рассмотрим следующий пример:

    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;
    }

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

Изменить: Обратите внимание, что это просто упрощенный пример. 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 ]

3 голосов
/ 01 июня 2010

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

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

        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 вызывает итерацию.

1 голос
/ 01 июня 2010
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 голосов
/ 01 июня 2010

Это то, что вы пытаетесь достичь?

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

РЕДАКТИРОВАТЬ:

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

lexicals.Intersect( allLexicals, StringComparer.OrdinalIgnoreCase )

РЕДАКТИРОВАТЬ 2:

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

lexicals.Intersect( allLexicals, new MyCustomTComparer() )

...