Skip to main content
 首页 » 编程设计

c#之为什么在 linq 中迭代一个组与迭代一个集合不同

2024年02月06日37zdz8207

在下面的查询中很容易看出我们基本上是如何遍历 sampleOrders 并评估每个订单的,即我们可以访问 ord 的属性:

var o = ( 
            from ord in sampleOrders 
            select ord.Quantity 

在下面的查询中,我们按产品将订单分组到 orderGroups 中。 orderGroups 在概念上是一个“列表的列表”,其中每个列表都有一个键和一个包含关联订单的属性。前面的描述当然是非常松散的 - 关键是如果我迭代集合我希望收到一个具有键属性和包含订单的属性的对象。此行为似乎与上面的第一个查询一致。但实际上每一组都是一个命令。通过迭代组列表,我们似乎绕过了组本身,实际上迭代了每个订单。
为什么这种分组行为与上面第一个查询中的行为不同?

var o = ( 
            from ord in sampleOrders 
            group ord by ord.Product into orderGroups 
            from grp in orderGroups 
            select grp  // grp is an order, not a group of orders 

顺便说一句,请注意我不是在问如何访问这些组......我的问题与概念和语法有关。 以下是如何访问每个组....我不是在问如何做到这一点:

var o = ( 
            from ord in sampleOrders 
            group ord by ord.Product into orderGroups 
            select new {orderGroups.Key, SumQty=orderGroups.Sum(x => x.Quantity)} 

编辑:每个请求的工作示例

public static void LinqTest() 
    {  
        List<Order> sampleOrders = new List<Order>() 
        { 
            new Order{Product="A", Quantity = 3}, 
            new Order{Product="A", Quantity = 4}, 
            new Order{Product="B", Quantity = 5}, 
            new Order{Product="C", Quantity = 6}, 
        }; 
 
 
        var orders = 
            from ord in sampleOrders 
            group ord by ord.Product into orderGroups 
            select orderGroups; 
 
        foreach (var grp in orders)      // here is the outer list 
        { 
            Console.WriteLine("Product: " + grp.Key.ToString()); 
 
            // here is the inner list hence the term "list of lists"  
            // it is in fact a hierarchical object however this fact is irrelelvent to my question 
            //  
            foreach (Order ord in grp)    
                Console.WriteLine("Qty: " + ord.Quantity); 
 
 
        } 
 
        Console.WriteLine(); 
        Console.WriteLine("following listing is grouped orders but we see each order not groups"); 
 
        var orders2 = 
         from ord in sampleOrders 
         group ord by ord.Product into orderGroups 
         from grp in orderGroups 
         select grp; 
 
        foreach(Order ord2 in orders2) 
            Console.WriteLine("Product: " + ord2.Product);  // each item returned is an order not a group of orders 
 
 
        Console.ReadKey(); 
    } 
} 
 
class Order 
{ 
    public string Product { get; set; } 
    public int Quantity { get; set; } 
} 

编辑 2:

这是澄清我的问题的另一个尝试: 在上面的第一个查询中,“from x in listOfx”为您提供了一个对象 x,它与组成 listOfx 的对象相同。

在组查询中,“from x in listOfxGroups”为您提供了一个与构成 listOfxGroups 的对象不同的对象。 listOfxGroups 实际上是一组组的列表...但是当我们对其进行迭代时,我们得到的是 x 而不是“x 组”。

请您参考如下方法:

        from ord in sampleOrders 
        group ord by ord.Product into orderGroups 
        from grp in orderGroups 
        select grp 

乍一看,您可能会认为这转化为:

sampleOrders.GroupBy(ord => ord.Product) 
    .Select(grp => grp); // grp is an `IGrouping<,>` 

但实际上,它转化为:

sampleOrders.GroupBy(ord => ord.Product) 
    .SelectMany(orderGroup => orderGroup); // orderGroup is an `IGrouping<,>` 
    .Select(grp => grp); // grp is an Order 

这是 LINQ 语句的翻译方式:在初始 from 之后关键字,任何剩余 from产生一个 SelectMany。例如:

A query expression with a second from clause followed by a select clause

from x1 in e1 
from x2 in e2 
select v 

is translated into

( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v ) 

C# 5 language spec, section 7.16.2.4

from a in x 
from b in a 
select b 

现在,SelectMany 中的 lambda 表达式必须返回 IEnumerable<T> (它确实如此,因为 IGrouping<TKey, TValue> 也是一个 IEnumerable<TValue> ),并且 SelectMany 展平了所有 IEnumerable<T> s成一大IEnumerable<T> , 这意味着其中的单个项目是 Order s,在这种情况下。

更新

为了帮助澄清,想象一下:

var orderGroups =              // orderGroups is plural: IEnumerable<IGrouping<,>> 
    from ord in sampleOrders 
    group ord by ord.Product; 
var o = 
    from orderGroup in orderGroups // orderGroup is singular: IGrouping<,> 
    from item in orderGroup        // `item` instead of `grp` 
    select item; 

这实际上是您使用 into 产生的结果子句,但具有不同的变量名称。

var o =  
        from ord in sampleOrders 
        group ord by ord.Product into orderGroup 
        from item in orderGroup 
        select item;               // item is an order, not a group of orders