C#中如何实现List行转列的通用方法

来源:IPIPP.com作者:头衔:全栈工程师
导读:本期聚焦于小伙伴创作的《C#中如何实现List行转列的通用方法》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#中如何实现List行转列的通用方法》有用,将其分享出去将是对创作者最好的鼓励。

在C#数据处理场景中,行转列是常见需求,比如将多行用户属性记录转换为按属性分列的表结构。下面我们来看具体的通用实现方式。

C#中如何实现List行转列的通用方法

行转列需求分析

假设我们有一个用户实体类,包含用户ID、属性名、属性值三个字段,原有的List数据是按行存储的,每行对应一个用户的一个属性,现在需要转换为每行对应一个用户,每个属性作为单独的列。

比如原始数据结构如下:

UserIdPropertyNamePropertyValue
1Name张三
1Age20
2Name李四
2Age22

转换后需要的表结构为:

UserIdNameAge
1张三20
2李四22

通用实现思路

实现通用行转列方法需要解决几个核心问题:

  • 如何动态识别需要作为列的属性名,这里我们可以通过传入的属性名列和属性值列来指定
  • 如何根据分组键(比如UserId)对原始数据进行分组
  • 如何动态创建转换后的结果对象,并赋值对应的属性

我们使用泛型和反射来实现通用逻辑,这样不管实体类型是什么,都可以复用同一套转换方法。

完整代码示例

首先定义原始数据实体类:

public class SourceData
{
    public int UserId { get; set; }
    public string PropertyName { get; set; }
    public string PropertyValue { get; set; }
}

然后定义行转列通用方法:

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

public static class ListRowToColumnHelper
{
    /// <summary>
    /// List行转列通用方法
    /// </summary>
    /// <typeparam name="TSource">原始数据类型</typeparam>
    /// <typeparam name="TResult">转换后结果类型</typeparam>
    /// <param name="sourceList">原始数据集合</param>
    /// <param name="groupKeySelector">分组键选择委托</param>
    /// <param name="propertyNameSelector">属性名选择委托</param>
    /// <param name="propertyValueSelector">属性值选择委托</param>
    /// <returns>转换后的结果集合</returns>
    public static List<TResult> RowToColumn<TSource, TResult>(
        List<TSource> sourceList,
        Func<TSource, object> groupKeySelector,
        Func<TSource, string> propertyNameSelector,
        Func<TSource, object> propertyValueSelector)
    {
        // 按分组键分组
        var groups = sourceList.GroupBy(groupKeySelector).ToList();
        // 获取结果类型的所有属性
        var resultProperties = typeof(TResult).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        List<TResult> resultList = new List<TResult>();

        foreach (var group in groups)
        {
            // 创建结果对象实例
            TResult resultObj = Activator.CreateInstance<TResult>();
            // 先设置分组键对应的属性,假设结果类型有和分组键同名的属性
            var groupKeyProp = resultProperties.FirstOrDefault(p => p.Name == "UserId");
            if (groupKeyProp != null && groupKeyProp.CanWrite)
            {
                // 类型转换,这里简单处理,实际使用时可以根据需要完善类型转换逻辑
                var groupKeyValue = Convert.ChangeType(group.Key, groupKeyProp.PropertyType);
                groupKeyProp.SetValue(resultObj, groupKeyValue);
            }

            // 遍历分组内的所有数据,设置对应的属性值
            foreach (var item in group)
            {
                string propertyName = propertyNameSelector(item);
                object propertyValue = propertyValueSelector(item);
                var prop = resultProperties.FirstOrDefault(p => p.Name == propertyName);
                if (prop != null && prop.CanWrite)
                {
                    // 类型转换,处理属性值类型和目标属性类型不匹配的情况
                    try
                    {
                        var typedValue = Convert.ChangeType(propertyValue, prop.PropertyType);
                        prop.SetValue(resultObj, typedValue);
                    }
                    catch
                    {
                        // 转换失败可以记录日志或者设置默认值,这里简单跳过
                    }
                }
            }
            resultList.Add(resultObj);
        }
        return resultList;
    }
}

接着定义转换后的结果实体类:

public class TargetData
{
    public int UserId { get; set; }
    public string Name { get; set; }
    public string Age { get; set; }
}

最后是使用方法的示例:

class Program
{
    static void Main(string[] args)
    {
        // 准备原始数据
        List<SourceData> sourceList = new List<SourceData>
        {
            new SourceData { UserId = 1, PropertyName = "Name", PropertyValue = "张三" },
            new SourceData { UserId = 1, PropertyName = "Age", PropertyValue = "20" },
            new SourceData { UserId = 2, PropertyName = "Name", PropertyValue = "李四" },
            new SourceData { UserId = 2, PropertyName = "Age", PropertyValue = "22" }
        };

        // 调用行转列方法
        List<TargetData> resultList = ListRowToColumnHelper.RowToColumn<SourceData, TargetData>(
            sourceList,
            item => item.UserId,
            item => item.PropertyName,
            item => item.PropertyValue
        );

        // 输出结果验证
        foreach (var item in resultList)
        {
            Console.WriteLine($"UserId:{item.UserId}, Name:{item.Name}, Age:{item.Age}");
        }
    }
}

注意事项

这个通用方法目前做了简单的类型转换,实际使用中如果遇到复杂的类型转换场景,可以根据需求扩展类型转换的逻辑。另外如果结果类型的属性名和原始数据的属性名不是完全匹配,也可以传入额外的映射关系来处理,让方法更灵活。

通过这种泛型加反射的方式,我们只需要维护这一个通用方法,就可以适配各种不同实体的行转列需求,不需要每次遇到新的实体都重新写一套转换逻辑,大大提升了开发效率。

C#List行转列泛型方法反射修改时间:2026-05-29 04:40:24

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。