如何实现通用类数据查询函数?解决对象转类与列表数据提取问题
通用列表属性提取函数实现方案
没问题,我来帮你搞定这个通用属性提取的需求!核心思路是用反射来在运行时动态获取类型信息和属性值——毕竟咱们没法提前知道传入的列表具体类型,反射刚好能解决这种“运行时才明确类型”的场景。
实现思路拆解
- 先验证传入的对象确实是可枚举类型(比如
List<T>都实现了IEnumerable接口,这样才能遍历元素) - 从传入的
Type参数中提取列表的元素类型(比如typeof(List<ABC>)对应的元素类型是ABC) - 通过反射获取目标属性的元信息,确保属性存在且可访问
- 遍历列表中的每个元素,动态提取指定属性的值并收集结果
完整非泛型实现代码
public List<object> GetData(object obj, Type objType, string propertyName) { var result = new List<object>(); // 验证传入对象是否为可枚举类型(List<T>等列表类都实现IEnumerable) if (!(obj is IEnumerable enumerable)) { throw new ArgumentException("传入的对象必须是可枚举类型(如List<T>)", nameof(obj)); } // 提取列表的元素类型(从List<T>的泛型参数中获取) Type elementType = objType.IsGenericType && objType.GetGenericTypeDefinition() == typeof(List<>) ? objType.GetGenericArguments()[0] : throw new ArgumentException("objType必须是List<T>类型", nameof(objType)); // 获取目标属性的元信息,确保是公共实例属性 PropertyInfo property = elementType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); if (property == null) { throw new ArgumentException($"类型 {elementType.Name} 中不存在名为 {propertyName} 的公共实例属性", nameof(propertyName)); } // 遍历列表元素,提取属性值 foreach (var item in enumerable) { if (item == null) { result.Add(null); continue; } // 校验元素类型是否匹配,避免类型不一致的异常 if (!elementType.IsAssignableFrom(item.GetType())) { throw new InvalidCastException($"列表中的元素类型 {item.GetType().Name} 与指定的 {elementType.Name} 不匹配"); } object value = property.GetValue(item); result.Add(value); } return result; }
调用示例
用你提供的ABC类来测试:
// 初始化测试数据 var listABC = new List<ABC> { new ABC { A1 = 10, B1 = true, C1 = "First" }, new ABC { A1 = 20, B1 = false, C1 = "Second" } }; // 提取所有A1属性的值 var a1Values = GetData(listABC, typeof(List<ABC>), "A1"); foreach (var val in a1Values) { Console.WriteLine(val); // 输出:10、20 }
优化方案:泛型版本(更类型安全)
如果调用方可以明确类型,泛型版本会更安全,还能避免object装箱的开销:
public List<TProperty> GetGenericData<T, TProperty>(List<T> list, string propertyName) { var result = new List<TProperty>(); // 获取目标属性元信息 PropertyInfo property = typeof(T).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); if (property == null) { throw new ArgumentException($"类型 {typeof(T).Name} 中不存在名为 {propertyName} 的公共实例属性", nameof(propertyName)); } // 校验属性类型与返回值类型是否兼容 if (!typeof(TProperty).IsAssignableFrom(property.PropertyType)) { throw new ArgumentException($"属性 {propertyName} 的类型 {property.PropertyType.Name} 无法转换为 {typeof(TProperty).Name}", nameof(propertyName)); } // 遍历提取属性值 foreach (var item in list) { if (item == null) { result.Add(default); continue; } TProperty value = (TProperty)property.GetValue(item); result.Add(value); } return result; }
调用泛型版本会更简洁:
var a1Values = GetGenericData<ABC, int>(listABC, "A1");
注意事项
- 反射有一定性能开销,如果是高频调用场景,可以缓存
PropertyInfo对象(比如用静态字典存类型+属性名对应的PropertyInfo) - 如果需要提取私有属性,可以修改
BindingFlags为BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,但要注意访问权限的合规性 - 代码中加入了空值校验和类型校验,避免运行时出现意外异常
内容的提问来源于stack exchange,提问作者Howard Teoh
相关产品推荐
相关产品推荐

