C#/.NET应用程序编程开发中如何对一个泛型集合进行LINQ动态分组(GroupBy)?

C#开发 作者: Rector 8 次阅读 · 读完约需 1 分钟 收藏本文

郑重申明:本文未经许可,禁止任何形式转载

问题描述

在C#/.NET应用程序编程开发中,如何对一个泛型集合进行LINQ动态分组(GroupBy)?

当前有一个泛型集合,一般情况下,使用LINQ进行分组都是按固定属性名称,比如有一个Customer集合,现要按固定的属性Grade进行分组统计数量,如下:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    public class Customer
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Grade { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var customers = new List<Customer>
            {
                new Customer{ Id = 1, FirstName="Rector" ,LastName = "Liu",Grade = 1},
                new Customer{ Id = 2, FirstName="James" ,LastName = "Liu",Grade = 2},
                new Customer{ Id = 3, FirstName="Stephen" ,LastName = "Liu",Grade = 3},
                new Customer{ Id = 4, FirstName="Loin" ,LastName = "Liu",Grade = 2},
                new Customer{ Id = 5, FirstName="Stephen" ,LastName = "Curry",Grade = 1},
            };
            var groupByFirstName = customers.GroupBy(x=>x.Grade)
                .Select(x=>new {
                    x.Key,
                    Count = x.Count()
                }).ToList();
            groupByFirstName.ForEach(x => Console.WriteLine("Key:{0},Count:{1}",x.Key, x.Count));
            Console.ReadLine();
        }
    }
}

输出结果:

Key:1,Count:2
Key:2,Count:2
Key:3,Count:1

那么,如果要在程序中依据不同情况动态对集合进行分组和统计,应该如何实现呢?

方案一

创建一个获取属性的方法,如下:

private static object GetPropertyValue(object obj, string propertyName)
{
    return obj.GetType().GetProperty(propertyName).GetValue(obj, null);
}

完整示例:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    public class Customer
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Grade { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var customers = new List<Customer>
            {
                new Customer{ Id = 1, FirstName="Rector" ,LastName = "Liu",Grade = 1},
                new Customer{ Id = 2, FirstName="James" ,LastName = "Liu",Grade = 2},
                new Customer{ Id = 3, FirstName="Stephen" ,LastName = "Liu",Grade = 3},
                new Customer{ Id = 4, FirstName="Loin" ,LastName = "Liu",Grade = 2},
                new Customer{ Id = 5, FirstName="Stephen" ,LastName = "Curry",Grade = 1},
            };
            var groupByFirstName = customers.GroupBy(x=> GetPropertyValue(x,"LastName"))
                .Select(x=>new {
                    x.Key,
                    Count = x.Count()
                }).ToList();
            groupByFirstName.ForEach(x => Console.WriteLine("Key:{0},Count:{1}",x.Key, x.Count));
            Console.ReadLine();
        }

        private static object GetPropertyValue(object obj, string propertyName)
        {
            return obj.GetType().GetProperty(propertyName).GetValue(obj, null);
        }
    }
}

输出结果:

Key:Liu,Count:4
Key:Curry,Count:1

方案二

创建一个基于表达式树的静态方法,如下:

private static Expression<Func<Menu, string>> GetGroupKey(string property)
{
    var parameter = Expression.Parameter(typeof(Menu));
    var body = Expression.Property(parameter, property);
    return Expression.Lambda<Func<Menu, string>>(body, parameter);
}

调用示例:

customers.GroupBy(GetGroupKey("LastName").Compile())

完整示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace ConsoleApp1
{
    public class Customer
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Grade { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var customers = new List<Customer>
            {
                new Customer{ Id = 1, FirstName="Rector" ,LastName = "Liu",Grade = 1},
                new Customer{ Id = 2, FirstName="James" ,LastName = "Liu",Grade = 2},
                new Customer{ Id = 3, FirstName="Stephen" ,LastName = "Liu",Grade = 3},
                new Customer{ Id = 4, FirstName="Loin" ,LastName = "Liu",Grade = 2},
                new Customer{ Id = 5, FirstName="Stephen" ,LastName = "Curry",Grade = 1},
            };
            var groupByFirstName = customers.GroupBy(GetGroupKey("LastName").Compile())
                .Select(x=>new {
                    x.Key,
                    Count = x.Count()
                }).ToList();
            groupByFirstName.ForEach(x => Console.WriteLine("Key:{0},Count:{1}",x.Key, x.Count));
            Console.ReadLine();
        }

        private static Expression<Func<Customer, string>> GetGroupKey(string property)
        {
            var parameter = Expression.Parameter(typeof(Customer));
            var body = Expression.Property(parameter, property);
            return Expression.Lambda<Func<Customer, string>>(body, parameter);
        }
    }
}

方案三

创建一个支持LINQ动态分组的静态扩展方法,如下:

public static IEnumerable<IGrouping<object, TElement>> GroupByMany<TElement>(this IEnumerable<TElement> elements, params string[] groupSelectors)
{
    var selectors = new List<Func<TElement, object>>(groupSelectors.Length);
    selectors.AddRange(groupSelectors.Select(selector => DynamicExpression.ParseLambda(typeof (TElement), typeof (object), selector)).Select(l => (Func<TElement, object>) l.Compile()));
    return elements.GroupByMany(selectors.ToArray());
}

public static IEnumerable<IGrouping<object, TElement>> GroupByMany<TElement>(this IEnumerable<TElement> elements, params Func<TElement, object>[] groupSelectors)
{
    if (groupSelectors.Length > 0)
    {
        Func<TElement, object> selector = groupSelectors.First();
        return elements.GroupBy(selector);
    }
    return null;
}

阅读了该文章的人还浏览了...

本文永久链接码友网 » C#/.NET应用程序编程开发中如何对一个泛型集合进行LINQ动态分组(GroupBy)?

发布于: 2019-07-01 15:17:55
分享扩散:

发表评论

登录用户才能发表评论, 请 登 录 或者 注册