最稳方案是 GetType()+GetProperties()反射遍历,需判空、过滤静态/索引器/只读属性、try/catch防异常;JsonConvert序列化虽快但有类型丢失、null省略等坑;优先尝试IDictionary转型可零成本优化。

用 GetType() + GetProperties() 遍历属性是最稳的起点
直接反射取属性值,不依赖第三方库,兼容 .NET Framework 4.5+ 和 .NET Core 2.0+。关键不是“能不能转”,而是“怎么避免空引用和只读属性崩掉”。
常见错误现象:NullReferenceException(对象为 null)、TargetInvocationException(调用 get_XXX 抛异常)、字典里漏掉 private set 字段。
- 必须先判空:
if (obj == null) return new Dictionary<string object>();</string> - 只处理
public实例属性,跳过索引器、静态属性、方法:prop.GetAccessors(true).Length > 0不够,得用prop.CanRead && !prop.GetIndexParameters().Any() - 对每个属性加
try/catch包裹读值逻辑,单个属性失败不影响整体转换 - 值类型自动装箱,引用类型保持原引用——这点影响后续修改判断,要注意
JsonConvert.SerializeObject() + JsonConvert.DeserializeObject<dictionary object>>()</dictionary> 是最省事但有坑
适合快速原型或配置类转换,但默认行为会把 DateTime 变成字符串、把 null 字段丢掉、嵌套对象变 JObject 而非 Dictionary。
使用场景:你只要“结构近似”的字典,不关心类型保真,也不打算反向写回对象。
- 必须配
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Include },否则null字段直接消失 -
DateTime默认序列化为 ISO8601 字符串,若需保留DateTime类型,得自定义JsonConverter,否则下游用as DateTime?会是null - 嵌套对象默认变成
JObject,要变成Dictionary<string object></string>得递归处理,或改用JsonSerializer.Deserialize<object>()</object>再手动展平 - 性能比纯反射慢 3–5 倍,大数据量别在热路径用
带泛型约束的扩展方法能兼顾类型安全和复用性
写一次,所有实体类都能用 obj.ToDictionary(),但得小心泛型推导失败和值类型 boxing 后的比较陷阱。
参数差异:要不要包含只读属性?要不要忽略特定特性(如 [JsonIgnore])?这些都得作为可选参数暴露。
- 签名建议:
public static Dictionary<string object> ToDictionary(this object obj, bool includeReadOnly = false, params string[] ignoreProperties)</string> - 用
Attribute.IsDefined(prop, typeof(JsonIgnoreAttribute))判断忽略,比硬编码字符串更可靠 - 对
includeReadOnly == false时,除了检查CanRead,还要确认GetSetMethod() != null(因为private set的属性CanRead为 true 但仍是“只读语义”) - 返回的
object值不建议做==比较,尤其int和long装箱后Equals()成立但==不成立
别忘了 IDictionary 接口本身可能提供更轻量的转换路径
有些实体类(比如继承自 ExpandoObject 或实现 IDictionary<string object></string>)根本不用反射——直接强制转型就行,快且零异常风险。
容易踩的坑是“以为所有对象都得走反射”,结果把本可 as IDictionary 的对象扔进慢路径。
- 优先尝试:
if (obj is IDictionary<string object> dict) return new Dictionary<string object>(dict);</string></string> -
ExpandoObject是IDictionary<string object></string>,但AnonymousType不是,别混淆 - 自定义类若手动实现了
IDictionary,注意其Keys是否包含全部属性名(有些实现只暴露部分键) - 这个分支应放在反射逻辑之前,属于“早返回”优化点
DateTime?、string、int、object 和 null 的混合实例跑一遍,比看十篇教程都管用。










