在C#开发中,我们经常需要将Plain Old CLR Object (POCO)转换为Dictionary<string, object>。这种需求在与第三方API交互、序列化数据、动态数据处理等场景中尤为常见。本文将深入探讨五种不同的转换方法,分析它们的特点、适用场景,并提供详细的代码示例和性能比较。

使用反射(Reflection)

特点 

  • 灵活性高,可以处理任何POCO对象
  • 不需要额外的依赖库
  • 性能较好,适合频繁使用的场景

应用场景 

  • 动态处理未知类型的对象
  • 需要自定义属性过滤或转换逻辑
  • 框架开发,需要通用的对象处理机制

代码示例 

using System;
using System.Reflection;namespace AppPOCO
{publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Alice", Age = 30 };var dict = ConvertToDict(person);Console.WriteLine(dict["Name"]);Console.WriteLine(dict["Age"]);Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDict(object obj){return obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToDictionary(prop => prop.Name, prop => prop.GetValue(obj));}}
}

C#中POCO对象转Dictionary的高级技巧与应用_System

高级应用:属性过滤 

using System;
using System.Reflection;namespace AppPOCO
{publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Alice", Age = 30 };var filteredDict = ConvertToDictFiltered(person, prop => prop.PropertyType == typeof(string));Console.WriteLine(filteredDict["Name"]);Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictFiltered(object obj, Func<PropertyInfo, bool> filter){return obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(filter).ToDictionary(prop => prop.Name, prop => prop.GetValue(obj));}}
}

C#中POCO对象转Dictionary的高级技巧与应用_System_02

使用Newtonsoft.Json

特点 

  • 利用成熟的JSON序列化库
  • 可以处理复杂的对象结构,包括嵌套对象和集合
  • 配置灵活,可以自定义序列化行为

应用场景 

  • 处理包含复杂数据结构的对象
  • 需要在序列化和反序列化之间保持一致性
  • 已经在项目中使用Newtonsoft.Json库

代码示例 

using System;
using System.Net;
using System.Reflection;
using Newtonsoft.Json;
usingstatic AppPOCO.Person;namespace AppPOCO
{publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var complexPerson = new Person{Name = "Bob",Age = 35,address = new Address { Street = "123 Main St", City = "Anytown" }};var dict = ConvertToDictNewtonsoft(complexPerson);foreach (var item in dict){Console.WriteLine($"{item.Key}: {item.Value}");}Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictNewtonsoft(object obj){var json = JsonConvert.SerializeObject(obj);return JsonConvert.DeserializeObject<Dictionary<string, object>>(json);}}
}

C#中POCO对象转Dictionary的高级技巧与应用_代码示例_03

高级应用:自定义序列化 

using System;
using System.Net;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
usingstatic AppPOCO.Person;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Charlie", Gender = Gender.Male };var dict = ConvertToDictNewtonsoftCustom(person);Console.WriteLine(JsonConvert.SerializeObject(dict));Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictNewtonsoftCustom(object obj){var settings = new JsonSerializerSettings{NullValueHandling = NullValueHandling.Ignore,Converters = new List<JsonConverter> { new StringEnumConverter() }};var json = JsonConvert.SerializeObject(obj, settings);return JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);}}
}

C#中POCO对象转Dictionary的高级技巧与应用_System_04

使用System.Text.Json(C# 8.0+)

特点 

  • .NET Core和.NET 5+的内置JSON库
  • 性能优于Newtonsoft.Json
  • 与.NET生态系统深度集成

应用场景 

  • 新的.NET
     Core或.NET 5+项目
  • 需要高性能JSON序列化
  • 不需要Newtonsoft.Json的特定功能

代码示例 

using System;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "David", Age = 40 };var dict = ConvertToDictSystemTextJson(person);Console.WriteLine(dict["Name"]);Console.WriteLine(dict["Age"]);Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictSystemTextJson(object obj){var json = JsonSerializer.Serialize(obj);return JsonSerializer.Deserialize<Dictionary<string, object>>(json);}}
}

C#中POCO对象转Dictionary的高级技巧与应用_System_05

高级应用:自定义JsonSerializerOptions 

public static Dictionary<string, object> ConvertToDictSystemTextJsonCustom(object obj)
{var options = new JsonSerializerOptions {PropertyNamingPolicy = JsonNamingPolicy.CamelCase,WriteIndented = true};var json = JsonSerializer.Serialize(obj, options);return JsonSerializer.Deserialize<Dictionary<string, object>>(json, options);
}

使用LINQ和自定义逻辑

特点 

  • 高度可定制
  • 不依赖外部库
  • 可以实现复杂的转换逻辑

应用场景 

  • 需要对属性名或值进行特殊处理
  • 只需要转换特定的属性
  • 需要在转换过程中执行额外的逻辑

代码示例 

using System;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Frank", Age = 0 };var dict = ConvertToDictLinq(person);Console.WriteLine(JsonSerializer.Serialize(dict));Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictLinq(object obj){return obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToDictionary(prop => prop.Name,prop => prop.GetValue(obj) ?? "N/A");}}
}

C#中POCO对象转Dictionary的高级技巧与应用_System_06

高级应用:自定义键值转换 

using System;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Grace", Age = 50 };var dict = ConvertToDictLinqCustom(person);Console.WriteLine(JsonSerializer.Serialize(dict));Console.ReadLine();}publicstatic Dictionary<string, object> ConvertToDictLinqCustom(object obj){return obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToDictionary(prop => prop.Name.ToLower(),prop => prop.PropertyType == typeof(string)? prop.GetValue(obj)?.ToString().ToUpper(): prop.GetValue(obj));}}
}

C#中POCO对象转Dictionary的高级技巧与应用_代码示例_07

使用ExpandoObject

特点 

  • 支持动态添加和删除属性
  • 可以像使用动态对象一样使用字典
  • 适合需要频繁修改结构的场景

应用场景 

  • 动态数据处理
  • 构建灵活的数据结构
  • 与动态语言交互

代码示例 

using System;
using System.Dynamic;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Henry", Age = 45 };dynamic expandoDict = ConvertToExpandoObject(person);Console.WriteLine(expandoDict.Name); // 输出: HenryexpandoDict.Job = "Developer"; // 动态添加属性Console.WriteLine(JsonSerializer.Serialize(expandoDict));Console.ReadLine();}public static dynamic ConvertToExpandoObject(object obj){var expando = new ExpandoObject();var expandoDic = (IDictionary<string, object>)expando;foreach (var prop in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)){expandoDic.Add(prop.Name, prop.GetValue(obj));}return expando;}}
}

C#中POCO对象转Dictionary的高级技巧与应用_代码示例_08

高级应用:结合LINQ和ExpandoObject 

using System;
using System.Dynamic;
using System.Net;
using System.Reflection;
using System.Text.Json;namespace AppPOCO
{publicenum Gender { Male, Female }publicclass Person{publicstring Email { get; set; }publicstring Name { get; set; }publicint Age { get; set; }public Gender Gender { get; set; }publicclass Address{publicstring Street { get; set; }publicstring City { get; set; }}public Address address { get; set; }}internal class Program{static void Main(string[] args){// 使用示例var person = new Person { Name = "Ivy", Age = 0, Email = "ivy@example.com" };dynamic filteredExpando = ConvertToExpandoObjectFiltered(person, prop => prop.GetValue(person) != null && !prop.GetValue(person).Equals(0));Console.WriteLine(JsonSerializer.Serialize(filteredExpando));Console.ReadLine();}public static dynamic ConvertToExpandoObjectFiltered(object obj, Func<PropertyInfo, bool> filter){var expando = new ExpandoObject();var expandoDic = (IDictionary<string, object>)expando;obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(filter).ToList().ForEach(prop => expandoDic.Add(prop.Name, prop.GetValue(obj)));return expando;}}
}

C#中POCO对象转Dictionary的高级技巧与应用_代码示例_09

性能比较

根据文章中提供的基准测试结果,我们可以得出以下结论:

  1. 反射方法和LINQ方法性能最佳,适合高频率调用的场景。
  2. ExpandoObject方法性能适中,在需要动态性的场景下是个不错的选择。
  3. JSON序列化方法(Newtonsoft.Json和System.Text.Json)性能较低,但在处理复杂对象时更为可靠。

总结

选择合适的POCO到Dictionary的转换方法取决于多个因素:

  • 性能需求
  • 对象复杂度
  • 动态性要求
  • 项目依赖
  • 特定功能需求(如自定义序列化)

在实际应用中,建议根据具体场景选择最合适的方法,并进行必要的性能测试和优化。对于大多数简单场景,反射或LINQ方法可能是最佳选择;而对于复杂对象或需要特殊处理的情况,JSON序列化方法或ExpandoObject可能更为合适。

无论选择哪种方法,都要注意处理潜在的异常情况,如空值、循环引用等,以确保代码的健壮性和可靠性。