如何在运行时动态定义泛型类型时将DataTable转换为泛型列表?
动态生成类型并将DataTable转换为泛型List
方法一:生成强类型泛型List(运行时创建类型)
这种方法会根据DataTable的列结构,动态生成一个带string类型属性的.NET类,再将DataTable行转换为该类的实例,最终生成List<动态类型>。
实现代码
using System; using System.Data; using System.Reflection; using System.Reflection.Emit; using System.Collections.Generic; public static class DataTableHelper { // 根据DataTable列动态创建类型 public static Type CreateDynamicType(DataTable dt) { var assemblyName = new AssemblyName($"DynamicTypes_{Guid.NewGuid()}"); var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule"); var typeBuilder = moduleBuilder.DefineType($"DynamicRow_{Guid.NewGuid()}", TypeAttributes.Public | TypeAttributes.Class); // 为每列添加string类型属性 foreach (DataColumn col in dt.Columns) { // 私有字段 var field = typeBuilder.DefineField($"_{col.ColumnName}", typeof(string), FieldAttributes.Private); // 属性定义 var property = typeBuilder.DefineProperty(col.ColumnName, PropertyAttributes.None, typeof(string), null); // Get方法 var getMethod = typeBuilder.DefineMethod($"get_{col.ColumnName}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(string), Type.EmptyTypes); var getIl = getMethod.GetILGenerator(); getIl.Emit(OpCodes.Ldarg_0); getIl.Emit(OpCodes.Ldfld, field); getIl.Emit(OpCodes.Ret); // Set方法 var setMethod = typeBuilder.DefineMethod($"set_{col.ColumnName}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(void), new[] { typeof(string) }); var setIl = setMethod.GetILGenerator(); setIl.Emit(OpCodes.Ldarg_0); setIl.Emit(OpCodes.Ldarg_1); setIl.Emit(OpCodes.Stfld, field); setIl.Emit(OpCodes.Ret); property.SetGetMethod(getMethod); property.SetSetMethod(setMethod); } return typeBuilder.CreateType(); } // 转换DataTable为动态类型泛型列表 public static IList ConvertToDynamicList(DataTable dt) { var dynamicType = CreateDynamicType(dt); var listType = typeof(List<>).MakeGenericType(dynamicType); var list = Activator.CreateInstance(listType) as IList; var properties = dynamicType.GetProperties(); foreach (DataRow row in dt.Rows) { var instance = Activator.CreateInstance(dynamicType); foreach (var prop in properties) { var value = row[prop.Name] != DBNull.Value ? row[prop.Name].ToString() : null; prop.SetValue(instance, value); } list.Add(instance); } return list; } }
调用示例
// 你的原有SQL查询代码 string sqlQuery = "SELECT Z.Column2, A.Column1, C.* FROM Z LEFT JOIN A ON A.ID = Z.AId bla bla bla"; var con = new SqlConnection(); con.ConnectionString = "你的连接字符串"; con.Open(); SqlDataAdapter da = new SqlDataAdapter(sqlQuery, con); DataTable resultDt = new DataTable(); da.Fill(resultDt); con.Close(); // 转换为动态泛型列表 var dynamicList = DataTableHelper.ConvertToDynamicList(resultDt); // 遍历访问(用dynamic关键字) foreach (var item in dynamicList) { dynamic rowItem = item; // 根据实际查询的列名访问属性 Console.WriteLine($"Column2: {rowItem.Column2}, Column1: {rowItem.Column1}"); }
优化建议
如果同一结构的DataTable需要多次转换,可以缓存生成的类型,避免重复创建提升性能:
// 缓存字典,键用列名+类型的组合标识 private static readonly Dictionary<string, Type> _typeCache = new Dictionary<string, Type>(); public static Type GetOrCreateDynamicType(DataTable dt) { // 生成唯一标识键:列名+类型拼接 var key = string.Join("|", dt.Columns.Cast<DataColumn>().Select(c => $"{c.ColumnName}:{c.DataType}")); if (_typeCache.TryGetValue(key, out Type type)) { return type; } type = CreateDynamicType(dt); _typeCache[key] = type; return type; }
方法二:使用ExpandoObject简化实现
如果不需要真正的强类型,只是需要动态访问属性的列表,可以直接用ExpandoObject,代码更简洁:
实现代码
using System; using System.Data; using System.Collections.Generic; using System.Dynamic; public static List<dynamic> ConvertToExpandoList(DataTable dt) { var list = new List<dynamic>(); foreach (DataRow row in dt.Rows) { dynamic expando = new ExpandoObject(); var expandoDict = (IDictionary<string, object>)expando; foreach (DataColumn col in dt.Columns) { expandoDict[col.ColumnName] = row[col] != DBNull.Value ? row[col].ToString() : null; } list.Add(expando); } return list; }
调用示例
var expandoList = ConvertToExpandoList(resultDt); foreach (var item in expandoList) { Console.WriteLine($"Column2: {item.Column2}, Column1: {item.Column1}"); }
内容的提问来源于stack exchange,提问作者Shai
相关产品推荐
相关产品推荐

