C# LINQ 优雅地将 IEnumerable<IGrouping> 转换为 IEnumerable<IEnumerable>?

Posted

技术标签:

【中文标题】C# LINQ 优雅地将 IEnumerable<IGrouping> 转换为 IEnumerable<IEnumerable>?【英文标题】:C# LINQ convert IEnumerable<IGrouping> to IEnumerable<IEnumerable> elegantly? 【发布时间】:2018-03-15 23:39:18 【问题描述】:

如果您有IEnumerable&lt;IGrouping&lt;K, T&gt;&gt;,请说来自

var ans = src.GroupBy(s => s.Field);

有没有更好的方法(很少使用 LINQ 方法/变体)转换为IEnumerable&lt;IEnumerable&lt;T&gt;&gt;,然后像这样嵌套Selects:

var ans2 = ans.Select(g => g.Select(s => s));

对于不理解的人,

var anst = ans2.Select(g => g.GetType().ReflectedType).All(t => t == typeof(Enumerable));

应该返回anst == true

【问题讨论】:

IGrouping&lt;T&gt;是什么接口?我只知道IGrouping&lt;K, T&gt;,已经是IEnumerable&lt;T&gt;。由于IEnumerable&lt;T&gt; 是协变的,IEnumerable&lt;IGrouping&lt;K, T&gt;&gt; 已经自动成为IEnumerable&lt;IEnumerable&lt;T&gt;&gt; 你到底在问什么? 您的测试与您的问题不匹配...在IEnumerable&lt;IEnumerable&gt; ans2 = Enumerable.Repeat(new List&lt;int&gt;(),1); ans2 的类型为IEnumerable&lt;IEnumerable&gt;,但您的测试将返回anst = false。这是因为 t == typeof(Enumerable) 将返回 true 的唯一类型是 Enumerable(不是接口,实际类 Enumerable)。这几乎肯定不是你想要的。 这可能是 A/B 问题吗?你想做什么,你觉得你需要做这个转换?正如其他人所说,您已经有了实现IEnumerable&lt;IEnumerable&gt; 的东西。您实际上是否遇到了您认为以上是解决方案的不同问题? 你有一个非常奇怪的要求,所以我不会把这个放在我的答案中,但是看看参考源,如果你可以修改你对 GroupBy 的调用,这将通过你的测试:src.GroupBy( s => s.Field), (key, items) => items.Skip(0)); 如果您打算将 GroupEnumerable 转换为 Enumerable,那么您已经有了最巧妙的方法。无需再看其他地方。除非您对该嵌套选择有疑问。 【参考方案1】:

避免嵌套选择的一种解决方案是使用AsEnumerable() 方法。

var ans = src.GroupBy(s => s.Field).Select(e => e.AsEnumerable());

【讨论】:

原来IGrouping&lt;T&gt;.AsEnumerable() 返回IGrouping&lt;T&gt;。谁知道? 但是 Select 实际上什么也没做。将其关闭会得到相同的结果,因为 IGrouping&lt;T&gt; IEnumerable&lt;T&gt; @NetMage 是的,IGrouping&lt;T&gt; IEnumerable&lt;T&gt;,因为它实现了IEnumerable&lt;T&gt;。那你到底想完成什么?如果您最初提出的 IEnumerable&lt;IEnumerable&lt;T&gt;&gt; 不满足您的要求? 我在答案中添加了一个测试。【参考方案2】:

在这种情况下,IGrouping 已经实现了该接口:

public static class Program

    static void Main(string[] args)
    
        var foo = new[] 
            new MyClass  a = "1", b = "2",
            new MyClass  a = "1", b = "3",
            new MyClass  a = "2", b = "4",
            new MyClass  a = "2", b = "5" ;

        var ans = foo.GroupBy(x => x.a);

        IEnumerable<IEnumerable<MyClass>> ans2 = ans;
        IEnumerable<IEnumerable<MyClass>> result = ans2.ToArray();
    

    private class MyClass
    
        public string a;
        public string b;
    

【讨论】:

为什么要将结果转换为数组? 原来IGrouping&lt;T&gt;.Cast&lt;IEnumerable&lt;T&gt;&gt;() 返回IGrouping&lt;T&gt; ToArray 将强制枚举 IEnumerable,因此如果出现问题,我会看到抛出异常。在该行之前,任何分组或转换代码实际上都不会做任何工作。 NetMage, Cast 返回 IEnumerable - T 对象也可能碰巧实现了 IGrouping,但这与此处无关。代替上面的 var ans2 ,您可以编写 IEnumerable> ans2 并且它工作正常。我编辑了我的答案以删除 var 以使其更清楚。 @NetMage:没错。 IEnumerable&lt;IGrouping&lt;K, T&gt;&gt;已经IEnumerable&lt;IEnumerable&lt;T&gt;&gt;。您想要的转换是语言内置的,因此您的要求非常不清楚。你已经得到它了。

以上是关于C# LINQ 优雅地将 IEnumerable<IGrouping> 转换为 IEnumerable<IEnumerable>?的主要内容,如果未能解决你的问题,请参考以下文章

C#中的LINQ 基础

IEnumerable<T> 使用 c# 在函数内访问 T 的属性

C# IEnumerator IEnumerable接口

C# LINQ(10)

C# IQueryable和IEnumerable的区别

LINQ