如何通过反射区分C#中的类与原子类型(如string、int等)
解决区分原子类型与自定义类的反射方案
针对你在实现ConvertObjectCollectionToParameter泛型方法时遇到的问题——无法通过IsClass区分string和自定义类,这里提供两种基于反射的优雅判断方式:
方法一:明确区分值类型、string与其他引用类型
直接通过类型特性判断,将值类型(包括DateTime、decimal这类非primitive的struct)和string归为原子类型,其余引用类型视为复杂类:
private bool IsAtomicType<T>() { Type type = typeof(T); // 值类型均视为原子类型 if (type.IsValueType) return true; // string是引用类型但属于原子范畴 if (type == typeof(string)) return true; // 剩余引用类型为复杂类 return false; }
方法二:利用TypeCode枚举判断
通过Type.GetTypeCode获取类型的枚举值,排除TypeCode.Object(对应自定义类、数组等复杂类型),其余均为原子类型:
private bool IsAtomicType<T>() { TypeCode typeCode = Type.GetTypeCode(typeof(T)); // TypeCode.Object对应复杂引用类型,其余为原子类型 return typeCode != TypeCode.Object; }
在泛型方法中应用判断
将上述判断整合到你的ConvertObjectCollectionToParameter方法中,分别处理原子类型集合和复杂类集合:
public SqlParameter ConvertObjectCollectionToParameter<T>(IEnumerable<T> collection, string parameterName) { DataTable dataTable = new DataTable(); string tvpTypeName; if (IsAtomicType<T>()) { // 原子类型集合:创建单列DataTable dataTable.Columns.Add("Value", typeof(T)); tvpTypeName = "dbo.AtomicTypeTableType"; // 替换为你的TVP类型名 foreach (T item in collection) { dataTable.Rows.Add(item ?? DBNull.Value); } } else { // 复杂类集合:根据类属性创建多列DataTable PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo prop in properties) { dataTable.Columns.Add(prop.Name, prop.PropertyType); } tvpTypeName = $"dbo.{typeof(T).Name}TableType"; // 替换为你的TVP类型名 foreach (T item in collection) { DataRow row = dataTable.NewRow(); foreach (PropertyInfo prop in properties) { row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; } dataTable.Rows.Add(row); } } return new SqlParameter(parameterName, SqlDbType.Structured) { Value = dataTable, TypeName = tvpTypeName }; }
补充说明
- 方法一逻辑直观,可灵活扩展(比如需要排除自定义struct时,可添加
type.IsPrimitive判断); - 方法二更简洁,自动覆盖所有.NET内置原子类型,无需手动枚举;
- 若你的场景中需要将
Guid、DateTimeOffset等特殊值类型也视为原子类型,两种方法均无需额外修改,因为它们都属于值类型或对应非Object的TypeCode。
内容的提问来源于stack exchange,提问作者Roger Hill
相关产品推荐
相关产品推荐

