Linq Union的问题行为?

时间:2010-06-01 12:37:31

标签: c# linq union enumeration

考虑以下示例:

    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”)。我不确定,但可能需要对延迟处理做些什么。任何想法如何使这个工作或一个好的替代品?

编辑:请注意,这只是一个简化的示例。 lexicalStringsallLexicals是原始代码中的不同类型。所以我不能直接结合这些。

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 个答案:

答案 0 :(得分: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

为什么会这样?你使用闭包 - 在内部lambda中使用外部var。由于您实际上没有迭代序列,因此s的当前值不会进入lambda。 foreach退出和s的所有内部副本保存最后一次迭代的值。在内部变量的情况下,它们保存为每次迭代创建的值副本。这种冲突来自LINQ的内心懒惰。如果您执行List.AddRange内部循环结果会很好,因为List.AddRange会强制迭代。

答案 1 :(得分: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;
}

答案 2 :(得分:0)

这是你想要实现的目标吗?

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

编辑:

或者更好,因为@Dave建议:

lexicals.Intersect( allLexicals, StringComparer.OrdinalIgnoreCase )

编辑2:

如果它们是不同类型,则其中一个必须将 IEqualityComparer 实现到另一个。然后将此类传递给Intersect方法:

lexicals.Intersect( allLexicals, new MyCustomTComparer() )