你能解释一下这个 lambda 分组函数吗?

本文关键字:lambda 函数 能解释 一下 | 更新日期: 2023-09-27 18:01:39

我已经使用 LINQ 和 Lambda 表达式一段时间了,但我仍然不完全适应该功能的各个方面。

因此,当我最近在处理一个项目时,我需要基于某些属性获取一个不同的对象列表,我遇到了这段代码。它有效,我对此很好,但我想了解分组机制。我不喜欢简单地插入代码并在可以帮助它的情况下逃避问题。

无论如何,代码是:

var listDistinct
    =list.GroupBy(
        i => i.value1,
        (key, group) => group.First()
    ).ToList();

在上面的代码示例中,您首先调用GroupBy并向其传递一个 lambda 表达式,告诉它按属性 value1 进行分组。代码的第二部分导致混淆。

我知道key(key, group)声明中引用了value1,但我仍然没有对正在发生的一切进行思考。

你能解释一下这个 lambda 分组函数吗?

表达式是什么

list.GroupBy(
    i => i.value1,
    (key, group) => group.First())

做?

这将创建一个查询,该查询在执行时分析序列list以生成组序列,然后将组序列投影到新序列中。在这种情况下,投影是从每个组中取出第一项。

第一个 lambda 选择构建组的"密钥"。在这种情况下,列表中具有相同 value1 属性的所有项都放在一个组中。他们共享的价值成为组的"钥匙"。

第二个 lambda 从键控组序列中投影;就好像你对组序列做了一个select。此查询的净效果是从列表中选择一组元素,以便结果序列的每个元素都具有不同的 value1 属性值。

文档在这里:

http://msdn.microsoft.com/en-us/library/bb549393.aspx

如果文档不清楚,我很乐意将批评转达给文档经理。

此代码使用 group 作为 lambda 的形式参数。group不是保留关键字吗?

不,group是一个上下文关键字。LINQ 已添加到 C# 3.0 中,因此可能已经存在使用 group 作为标识符的程序。如果这些程序被设置为保留关键字,则重新编译时group这些程序将被破坏。相反,group 仅在查询表达式的上下文中是关键字。在查询表达式之外,它是一个普通标识符。

如果要引起注意它是普通标识符的事实,或者如果要在查询表达式中使用标识符group,可以通过在前面加上@来告诉编译器"将其视为标识符,而不是关键字"。如果我写上面的代码,我会说

list.GroupBy(
    i => i.value1,
    (key, @group) => @group.First())

说清楚。

C# 中还有其他上下文关键字吗?

是的。我在这里记录了它们:

http://ericlippert.com/2009/05/11/reserved-and-contextual-keywords/

我想

将其简化为int列表以及如何通过使用GroupBy在此列表中进行区分:

var list = new[] {1, 2, 3, 1, 2, 2, 3};

如果您使用 x => x 调用 GroupBy,您将获得 3 组类型为:

IEnumerable<IEnumerable<int>>
{{1,1},{2,2,2},{3,3}}

每组的键是:1,2,3。 然后,当调用group.First()时,这意味着你得到每个组的第一个项目:

{1,1}: -> 1.
{2,2,2}: -> 2
{3,3} -> 3

所以最终的结果是:{1, 2, 3}

您的情况与此类似。

它使用 Enumerable.GroupBy 方法的重载:

public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(
   this IEnumerable<TSource> source,
   Func<TSource, TKey> keySelector,
   Func<TKey, IEnumerable<TSource>, TResult> resultSelector
)

其中,如MSDN所述:

根据指定的键选择器函数对序列的元素进行分组,并从每个组及其键创建结果值。

因此,与其他返回一堆组(即 IEnumerable<IGrouping<TK, TS>> (,此重载允许您将每个组投影到所选TResult的单个实例。

请注意,您可以使用基本的GroupBy重载和Select获得相同的结果:

var listDistinct = list
    .GroupBy(i => i.value1)
    .Select(g => g.First())
    .ToList();
(key, group) => group.First()

它只是在每个组中获取First()元素。

在该 lambda 表达式中,key是一个用于创建该组的键(在您的示例中value1(,并且group与具有该key的所有元素IEnumerable<T>

下面的自描述示例应该可以帮助您理解分组:

class Item
{
    public int Value { get; set; }
    public string Text { get; set; }
}
static class Program
{
    static void Main()
    {
        // Create some items
        var item1 = new Item {Value = 0, Text = "a"};
        var item2 = new Item {Value = 0, Text = "b"};
        var item3 = new Item {Value = 1, Text = "c"};
        var item4 = new Item {Value = 1, Text = "d"};
        var item5 = new Item {Value = 2, Text = "e"};
        // Add items to the list
        var itemList = new List<Item>(new[] {item1, item2, item3, item4, item5});
        // Split items into groups by their Value
        // Result contains three groups.
        // Each group has a distinct groupedItems.Key --> {0, 1, 2}
        // Each key contains a collection of remaining elements: {0 --> a, b} {1 --> c, d} {2 --> e}
        var groupedItemsByValue = from item in itemList
                                  group item by item.Value
                                  into groupedItems
                                  select groupedItems;
        // Take first element from each group: {0 --> a} {1 --> c} {2 --> e}
        var firstTextsOfEachGroup = from groupedItems in groupedItemsByValue
                                    select groupedItems.First();
        // The final result
        var distinctTexts = firstTextsOfEachGroup.ToList(); // Contains items where Text is: a, c, e
    }
}

它等于

var listDistinct=(
    from i in list
    group i by i.value1 into g
    select g.First()
    ).ToList();

原始代码中i => i.value1的部分是键选择器。在此代码中,只是按键组元素的语法中i.value

原始代码中(key, group) => group.First()的部分是结果选择器的委托。在此代码中,它是用 from ...选择。在这里g group原始代码。