Linq Union 的行为有问题吗?

发布于 2024-09-03 08:39:09 字数 1249 浏览 5 评论 0原文

考虑以下示例:

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

consider the following example:

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

I'd hoped for it to produce "test", "t" as output, but it does not (The output is only "t"). I'm not sure, but may have to do something with the deferred processing. Any ideas how to get this to work or for a good alternative?

Edit: Please note that this is just a simplified example. lexicalStrings and allLexicals are different types in the original code. So I cannot directly combine these.

Edit2 the problem to solve looks more like this:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

弃爱 2024-09-10 08:39:09

正如其他答案所解释的那样,您正在使用错误的操作。但有趣的是为什么你的代码看起来很好但运行不正确。

让我们稍微修改一下您的应用程序:

        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 会强制迭代。

You are using wrong operation as other answer explaining. But still it is interesting why your code works incorrectly despite looking fine.

let's modify your app a bit:

        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)
        {
        }

what output do you expect? here is it:

t
t
t
t
t
t
t
t

interesting, is not it?

now let's modify it again:

    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)
    {                
    }

now the output and results are fine:

test
test
test
test
t
t
t
t

Why does it happen? You use closure - the use of outer var in inner lambda. Since you do not actually iterate your sequence the current value of s doesn't get into the lambda. foreach exits and all inner copies of s hold value of last iteration. In case of inner variable they hold values copies that are created for every iteration. This conflict comes from inner lazyness of LINQ. If you do things like List.AddRange inside loop result will be fine, because List.AddRange forces iteration.

眼角的笑意。 2024-09-10 08:39:09
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;
}
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;
}
此刻的回忆 2024-09-10 08:39:09

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

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

编辑:

或者更好的是,正如@Dave建议的:

lexicals.Intersect( allLexicals, StringComparer.OrdinalIgnoreCase )

编辑2:

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

lexicals.Intersect( allLexicals, new MyCustomTComparer() )

Is this what you are trying to achieve?

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

EDIT:

Or better yet as @Dave suggested:

lexicals.Intersect( allLexicals, StringComparer.OrdinalIgnoreCase )

EDIT 2:

If they are different types one of them must implement IEqualityComparer to the other. Then pass this class to the Intersect method:

lexicals.Intersect( allLexicals, new MyCustomTComparer() )

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文