c# Join和lambda表达式

本文关键字:表达式 lambda Join | 更新日期: 2023-09-27 18:13:49

我试图理解下面的代码行。Sequence1和sequence2是两个字符串数组。代码应该实现内连接效果。有人能帮我解释一下怎么读吗?也就是x => x.gn2是什么?我知道n1 => n1。长度是连接条件。我正在努力与lambda表达式。提前感谢!

var j = sequence1.GroupJoin ( sequence2 , 
n1 => n1.Length , n2 => n2.Length , (n1, gn2) => new { n1, gn2 })
.SelectMany (x => x.gn2,(x, n2) => new { x.n1, n2 });

c# Join和lambda表达式

我不确定这个表达式是否像你想的那样。但就是这样。让我们重写一下:

static void Foo1()
{
    string[] sequence1 = new[] { "12", "34", "567" };
    string[] sequence2 = new[] { "ab", "cd", "efg" };
    var result = sequence1.GroupJoin(sequence2,
    n1 => n1.Length, n2 => n2.Length, (n1, gn2) => new { n1, gn2 })
    .SelectMany(x => x.gn2, (x, n2) => new { x.n1, n2 });
    result.ToList().ForEach(Console.WriteLine);
}

,现在以另一种等效形式重写:

static void Foo2()
{
    string[] sequence1 = new[] { "12", "34", "567" };
    string[] sequence2 = new[] { "ab", "cd", "efg" };
    var joinResult = sequence1.GroupJoin(
        sequence2,
        n1 => n1.Length,
        n2 => n2.Length,
        (n1, gn2) => new {n1, gn2});
    Console.WriteLine("joinResult: ");
    joinResult.ToList().ForEach(Console.WriteLine);
    var result = joinResult.SelectMany(
        x => x.gn2,
        (x, n2) => new { x.n1, n2 });
    Console.WriteLine("result: ");
    result.ToList().ForEach(Console.WriteLine);
}

现在让我们看第一部分(GroupJoin):

    var joinResult = sequence1.GroupJoin(
        sequence2,
        n1 => n1.Length,
        n2 => n2.Length,
        (n1, gn2) => new {n1, gn2});

我们正在连接两个集合。注意,GroupJoin是在sequence1上调用的扩展方法。阅读GroupJoin的文档,我们看到sequence1是外序列,第一个参数sequence2是内序列。
第二个参数n1 => n1.Length是一个方法,该方法基于外部集合的每个元素生成该元素的键。
第三个参数n2 => n2.Length是一个方法,它基于内部集合的每个元素生成该元素的键。
GroupJoin现在有足够的数据来匹配第一个序列的元素和第二个序列的元素。在本例中,字符串是根据其长度进行匹配的。第一个序列中所有长度为2的字符串都与第二个序列中长度为2的字符串匹配。第一个序列中所有长度为3的字符串都与第二个序列中长度为3的字符串匹配。对于任意长度的字符串,依次类推。
最后一个参数(n1, gn2) => new {n1, gn2}是一个方法,它基于外部序列中的一个元素(即sequence1)和sequence2中所有匹配元素的集合,将生成一些结果。在本例中,结果是一个带有两个字段的匿名类型:

  • 第一个名为n1的字段是sequence1中的元素。
  • 第二个名为gn2的字段是sequence2中所有匹配元素的集合。

接下来是SelectMany:

var result = joinResult.SelectMany(
    x => x.gn2,
    (x, n2) => new { x.n1, n2 });

SelectMany是在joinResult上调用的扩展方法。花点时间看看我的帖子的末尾,我复制了应用程序的输出,看看joinResult序列是什么样的。注意,joinResult中的每个元素x都是带有字段{n1, gn2}的匿名类型,其中gn2本身是一个序列。

第一个参数x => x.gn2是一个以lambda形式编写的委托。SelectMany将对输入序列joinResult中的每个元素调用这个方法。SelectMany调用这个方法,这样每次调用都有机会生成一个中间集合。请记住,joinResult中的每个元素x都是带有字段{n1, gn2}的匿名类型,其中gn2本身是一个序列。有了这些,lambda x => x.gn2转换集合x.gn2中的每个元素x。

现在SelectMany基于输入序列的每个元素可以生成一个新的中间序列,它将继续处理该中间序列。我们有第二个参数。

第二个参数(x, n2) => new { x.n1, n2 }是另一个以lambda形式编写的委托。这个委托由SelectMany对中间序列的每个元素调用,带有两个参数:

  • 第一个参数是输入序列中的当前元素。
  • 第二个参数是中间序列的一个连续元素。

lambda将这两个参数转换为另一个具有两个字段的匿名类型:

  • 第一个名为n1的字段。如果您遵循来自joinResult中的集合的n1字段的数据流。
  • 第二个名为n2的字段是中间序列的当前元素。

这一切听起来非常复杂,但如果你调试应用程序并在战略点上放置一些断点,它就会变得清晰。

让我们用等价的形式再重写一次:

static void Foo3()
{
    string[] sequence1 = new[] { "12", "34", "567" };
    string[] sequence2 = new[] { "ab", "cd", "efg" };
    var joinResult = sequence1.GroupJoin(
        sequence2,
        element1 => GetKey1(element1),
        element2 => GetKey2(element2),
        (n1, gn2) =>
        {
            // place a breakpoint on the next line
            return new {n1, gn2};
        });
    Console.WriteLine("joinResult: ");
    joinResult.ToList().ForEach(Console.WriteLine);
    var result = joinResult.SelectMany(
        x =>
        {
            // place a breakpoint on the next line
            return x.gn2;
        },
        (x, n2) =>
        {
            // place a breakpoint on the next line
            return new {x.n1, n2};
        });
    Console.WriteLine("result: ");
    result.ToList().ForEach(Console.WriteLine);
}
private static int GetKey1(string element1)
{
    // place a breakpoint on the next line
    return element1.Length;
}
private static int GetKey2(string element2)
{
    // place a breakpoint on the next line
    return element2.Length;
}

我建议您运行最详细的方法Foo3,并在指示的地方放置断点。这将帮助你更详细地了解这一切是如何工作的。

最后,我必须说,所有这些看起来如此复杂的一个原因是变量的命名方式。下面是另一种形式,不像Foo3那样冗长,但相当容易阅读:

static void Foo4()
{
    string[] sequence1 = new[] { "12", "34", "567" };
    string[] sequence2 = new[] { "ab", "cd", "efg" };
    var groupJoinResult = sequence1.GroupJoin(
        sequence2,
        elementFromSequence1 => elementFromSequence1.Length,
        elementFromSequence2 => elementFromSequence2.Length,
        (elementFromSequence1, matchingCollectionFromSequence2) => new { elementFromSequence1, matchingCollectionFromSequence2 });
    var result = groupJoinResult.SelectMany(
        inputElement => inputElement.matchingCollectionFromSequence2,
        (inputElement, elementFromMatchingCollection) => new { inputElement.elementFromSequence1, elementFromMatchingCollection });
    result.ToList().ForEach(Console.WriteLine);
}

备注:运行Foo3的输出为:

joinResult:
{ n1 = 12, gn2 = System.Linq.Lookup`2+Grouping[System.Int32,System.String] }
{ n1 = 34, gn2 = System.Linq.Lookup`2+Grouping[System.Int32,System.String] }
{ n1 = 567, gn2 = System.Linq.Lookup`2+Grouping[System.Int32,System.String] }
result:
{ n1 = 12, n2 = ab }
{ n1 = 12, n2 = cd }
{ n1 = 34, n2 = ab }
{ n1 = 34, n2 = cd }
{ n1 = 567, n2 = efg }