5

我使用 LINQ 和Lambda 表达式已经有一段时间了,但我仍然对这个功能的各个方面并不完全满意。

所以,当我最近在做一个项目时,我需要根据一些属性获取一个不同的对象列表,我遇到了这段代码。它有效,我对此很好,但我想了解分组机制。如果我能提供帮助,我不喜欢简单地插入代码并逃避问题。

无论如何,代码是:

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

在上面的代码示例中,您首先调用GroupBy并传递一个 lambda 表达式,告诉它​​按属性分组value1。代码的第二部分引起了混乱。

我知道这keyvalue1(key, group)声明中的引用,但我仍然没有完全理解正在发生的一切。

4

6 回答 6

7

什么表达

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/

于 2013-02-28T15:59:02.690 回答
4

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

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

如果你GroupBy用打电话x => x,你会得到 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}

你的情况与此类似。

于 2013-02-28T16:04:29.877 回答
2

它使用该方法的重载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重载和 a获得相同的结果Select

var listDistinct = list
    .GroupBy(i => i.value1)
    .Select(g => g.First())
    .ToList();
于 2013-02-28T16:01:22.307 回答
1
(key, group) => group.First()

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

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

于 2013-02-28T15:57:17.897 回答
0

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

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
    }
}
于 2013-02-28T16:10:49.617 回答
0

相当于

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

原始代码中的部分i => i.value1key selector。在这段代码中,只是在key的group 元素i.value的语法中。

原始代码中的部分是结果选择器(key, group) => group.First()的委托。在这段代码中,它是用更具语义的语法from ... select编写的。是这里是原始代码。ggroup

于 2013-02-28T16:15:30.413 回答