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 });
我不确定这个表达式是否像你想的那样。但就是这样。让我们重写一下:
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 }