编辑
2025-09-22
C#
00

目录

🔥 性能痛点:传统序列化为何慢如蜗牛?
反射开销巨大
JSON格式冗余
运行时类型检查
💡 Source Generators:编译时的魔法师
🛠️ 实战:打造你的专属序列化器
第一步:创建Source Generator项目
第二步:核心Generator代码
第三步:业务项目配置
第四步:定义业务对象
第五步:极速序列化调用
📊 性能对比:数据说话
⚠️ 实战避坑指南
坑点1:命名空间问题
坑点2:项目引用配置
坑点3:支持的数据类型
🔮 进阶扩展思路
🎯 总结与思考

在.NET开发中,你是否经常为序列化性能头疼?传统的JSON序列化慢如蜗牛,反射序列化更是性能杀手。今天,我将带你走进C# Source Generators的世界,用编译时代码生成技术打造一款比System.Text.Json快10倍的极速序列化器!

这不是纸上谈兵,而是经过10万次测试验证的真实性能提升。如果你的项目对序列化性能有极致要求,或者你想深入了解编译时代码生成的奥秘,这篇文章将为你揭开所有答案。

🔥 性能痛点:传统序列化为何慢如蜗牛?

在深入解决方案之前,让我们先看看传统序列化的性能瓶颈:

反射开销巨大

C#
// 传统反射序列化 - 每次都要获取类型信息 var properties = typeof(Person).GetProperties(); foreach(var prop in properties) { var value = prop.GetValue(obj); // 反射调用,性能杀手 }

JSON格式冗余

  • System.Text.Json虽然快,但JSON格式本身存在大量冗余字符
  • 网络传输和存储成本居高不下

运行时类型检查

每次序列化都要进行类型判断和转换,无法在编译时优化。

关键问题:能否在编译时就生成好所有序列化代码,运行时零反射调用?

💡 Source Generators:编译时的魔法师

C# 9.0引入的Source Generators就是我们的救星!它能在编译时自动生成代码,实现:

  • 零反射开销 - 编译时生成静态方法调用
  • 类型安全 - 编译时类型检查,运行时无需转换
  • 极致性能 - 直接属性访问,无中间层损耗
  • 二进制紧凑 - 自定义二进制格式,体积最小

🛠️ 实战:打造你的专属序列化器

第一步:创建Source Generator项目

XML
<!-- AppFastSerializerGenerator.csproj --> <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <IncludeBuildOutput>false</IncludeBuildOutput> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" /> </ItemGroup> </Project>

第二步:核心Generator代码

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis; #pragma warning disable RS1035 namespace AppFastSerializerGenerator { [Generator] public class FastSerializerGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { } public void Execute(GeneratorExecutionContext context) { var syntaxTrees = context.Compilation.SyntaxTrees; foreach (var syntaxTree in syntaxTrees) { var root = syntaxTree.GetRoot(); var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>(); foreach (var @class in classes) { var properties = @class.Members .OfType<PropertyDeclarationSyntax>() .Where(prop => prop.Modifiers.All(m => m.Text != "static")) .ToArray(); if (properties.Length == 0) continue; var ns = (@class.Parent as NamespaceDeclarationSyntax)?.Name.ToString() ?? (@class.Parent is FileScopedNamespaceDeclarationSyntax fns ? fns.Name.ToString() : ""); var className = @class.Identifier.Text; var fullClassName = string.IsNullOrEmpty(ns) ? className : ns + "." + className; // 构建序列化代码 - 注意这里使用 fullClassName var sb = new StringBuilder($@"using System.IO; namespace GeneratedSerialization {{ public static class {className}Serializer {{ public static void Serialize({fullClassName} obj, System.IO.Stream stream) {{ var bw = new System.IO.BinaryWriter(stream, System.Text.Encoding.UTF8, true); "); foreach (var prop in properties) { var propType = prop.Type.ToString(); var propName = prop.Identifier.Text; // 可根据类型扩展更多类型适配 if (propType == "int") sb.AppendLine($" bw.Write(obj.{propName});"); else if (propType == "float") sb.AppendLine($" bw.Write(obj.{propName});"); else if (propType == "string") { sb.AppendLine($@" if (obj.{propName} == null) bw.Write(-1); else {{ var bytes = System.Text.Encoding.UTF8.GetBytes(obj.{propName}); bw.Write(bytes.Length); bw.Write(bytes); }}"); } // 这里可以处理更多类型... } sb.AppendLine(" }"); // 反序列化方法 - 注意这里也使用 fullClassName sb.AppendLine($@" public static {fullClassName} Deserialize(System.IO.Stream stream) {{ var br = new System.IO.BinaryReader(stream, System.Text.Encoding.UTF8, true); var obj = new {fullClassName}(); "); foreach (var prop in properties) { var propType = prop.Type.ToString(); var propName = prop.Identifier.Text; if (propType == "int") sb.AppendLine($" obj.{propName} = br.ReadInt32();"); else if (propType == "float") sb.AppendLine($" obj.{propName} = br.ReadSingle();"); else if (propType == "string") { sb.AppendLine($@" int length_{propName} = br.ReadInt32(); if (length_{propName} == -1) obj.{propName} = null; else obj.{propName} = System.Text.Encoding.UTF8.GetString(br.ReadBytes(length_{propName})); "); } } sb.AppendLine(" return obj;\n }\n }\n}"); // 添加到生成器输出 context.AddSource($"{className}Serializer.g.cs", sb.ToString()); } } } } }

第三步:业务项目配置

XML
<!-- 主项目.csproj --> <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\AppFastSerializerGenerator\AppFastSerializerGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup> </Project>

第四步:定义业务对象

C#
namespace AppSourceGeneratorsSerializer { [Serializable] public class Person { public int Id { get; set; } public string Name { get; set; } public float Score { get; set; } } }

第五步:极速序列化调用

C#
using System.Diagnostics; using System.Text.Json; using System.Runtime.Serialization.Formatters.Binary; using System.Xml.Serialization; using GeneratedSerialization; namespace AppSourceGeneratorsSerializer { internal class Program { static void Main(string[] args) { // 基本功能测试 Console.WriteLine("=== 基本功能测试 ==="); var person = new Person { Id = 1, Name = "John", Score = 88.5f }; using var ms = new MemoryStream(); PersonSerializer.Serialize(person, ms); ms.Position = 0; var person2 = PersonSerializer.Deserialize(ms); Console.WriteLine($"反序列化结果: Name={person2.Name}, Score={person2.Score}, Id={person2.Id}"); Console.WriteLine(); // 性能测试 Console.WriteLine("=== 性能测试对比 ==="); PerformanceTest(); Console.ReadKey(); } static void PerformanceTest() { const int iterations = 100_000; var testData = GenerateTestData(1000); // 生成1000个测试对象 Console.WriteLine($"测试数据: {testData.Count} 个对象"); Console.WriteLine($"测试迭代: {iterations:N0} 次"); Console.WriteLine(); // 1. Source Generator 序列化器测试 TestSourceGeneratorSerialization(testData, iterations); // 2. System.Text.Json 测试 TestSystemTextJsonSerialization(testData, iterations); // 3. Newtonsoft.Json 测试 (如果安装了包) // TestNewtonsoftJsonSerialization(testData, iterations); // 4. 反射序列化器测试 (自实现) TestReflectionSerialization(testData, iterations); // 5. XML序列化测试 TestXmlSerialization(testData, iterations); } static List<Person> GenerateTestData(int count) { var random = new Random(42); // 固定种子确保一致性 var data = new List<Person>(count); for (int i = 0; i < count; i++) { data.Add(new Person { Id = i, Name = $"Person_{i}_{random.Next(1000, 9999)}", Score = (float)(random.NextDouble() * 100) }); } return data; } static void TestSourceGeneratorSerialization(List<Person> data, int iterations) { Console.WriteLine("1. Source Generator 序列化器:"); var sw = Stopwatch.StartNew(); long totalBytes = 0; for (int i = 0; i < iterations; i++) { using var ms = new MemoryStream(); foreach (var person in data) { PersonSerializer.Serialize(person, ms); } totalBytes += ms.Length; } sw.Stop(); Console.WriteLine($" 序列化时间: {sw.ElapsedMilliseconds:N0} ms"); Console.WriteLine($" 平均大小: {totalBytes / iterations:N0} bytes"); // 反序列化测试 var serializedData = new MemoryStream(); foreach (var person in data) { PersonSerializer.Serialize(person, serializedData); } var serializedBytes = serializedData.ToArray(); sw.Restart(); for (int i = 0; i < iterations; i++) { using var ms = new MemoryStream(serializedBytes); var deserializedData = new List<Person>(); while (ms.Position < ms.Length) { deserializedData.Add(PersonSerializer.Deserialize(ms)); } } sw.Stop(); Console.WriteLine($" 反序列化时间: {sw.ElapsedMilliseconds:N0} ms"); Console.WriteLine(); } static void TestSystemTextJsonSerialization(List<Person> data, int iterations) { Console.WriteLine("2. System.Text.Json:"); var sw = Stopwatch.StartNew(); long totalBytes = 0; for (int i = 0; i < iterations; i++) { var json = JsonSerializer.Serialize(data); totalBytes += System.Text.Encoding.UTF8.GetBytes(json).Length; } sw.Stop(); Console.WriteLine($" 序列化时间: {sw.ElapsedMilliseconds:N0} ms"); Console.WriteLine($" 平均大小: {totalBytes / iterations:N0} bytes"); // 反序列化测试 var jsonData = JsonSerializer.Serialize(data); sw.Restart(); for (int i = 0; i < iterations; i++) { var deserializedData = JsonSerializer.Deserialize<List<Person>>(jsonData); } sw.Stop(); Console.WriteLine($" 反序列化时间: {sw.ElapsedMilliseconds:N0} ms"); Console.WriteLine(); } static void TestReflectionSerialization(List<Person> data, int iterations) { Console.WriteLine("3. 反射序列化器:"); var sw = Stopwatch.StartNew(); long totalBytes = 0; for (int i = 0; i < iterations; i++) { using var ms = new MemoryStream(); foreach (var person in data) { ReflectionSerializer.Serialize(person, ms); } totalBytes += ms.Length; } sw.Stop(); Console.WriteLine($" 序列化时间: {sw.ElapsedMilliseconds:N0} ms"); Console.WriteLine($" 平均大小: {totalBytes / iterations:N0} bytes"); // 反序列化测试 var serializedData = new MemoryStream(); foreach (var person in data) { ReflectionSerializer.Serialize(person, serializedData); } var serializedBytes = serializedData.ToArray(); sw.Restart(); for (int i = 0; i < iterations; i++) { using var ms = new MemoryStream(serializedBytes); var deserializedData = new List<Person>(); while (ms.Position < ms.Length) { deserializedData.Add(ReflectionSerializer.Deserialize<Person>(ms)); } } sw.Stop(); Console.WriteLine($" 反序列化时间: {sw.ElapsedMilliseconds:N0} ms"); Console.WriteLine(); } static void TestXmlSerialization(List<Person> data, int iterations) { Console.WriteLine("4. XML 序列化:"); var sw = Stopwatch.StartNew(); long totalBytes = 0; var serializer = new XmlSerializer(typeof(List<Person>)); for (int i = 0; i < iterations; i++) { using var ms = new MemoryStream(); serializer.Serialize(ms, data); totalBytes += ms.Length; } sw.Stop(); Console.WriteLine($" 序列化时间: {sw.ElapsedMilliseconds:N0} ms"); Console.WriteLine($" 平均大小: {totalBytes / iterations:N0} bytes"); // 反序列化测试 var xmlData = new MemoryStream(); serializer.Serialize(xmlData, data); var xmlBytes = xmlData.ToArray(); sw.Restart(); for (int i = 0; i < iterations; i++) { using var ms = new MemoryStream(xmlBytes); var deserializedData = (List<Person>)serializer.Deserialize(ms); } sw.Stop(); Console.WriteLine($" 反序列化时间: {sw.ElapsedMilliseconds:N0} ms"); Console.WriteLine(); } } // 反射序列化器(用于对比) public static class ReflectionSerializer { public static void Serialize<T>(T obj, Stream stream) { var bw = new BinaryWriter(stream, System.Text.Encoding.UTF8, true); var type = typeof(T); var properties = type.GetProperties(); foreach (var prop in properties) { var value = prop.GetValue(obj); if (prop.PropertyType == typeof(int)) { bw.Write((int)value); } else if (prop.PropertyType == typeof(float)) { bw.Write((float)value); } else if (prop.PropertyType == typeof(string)) { var str = (string)value; if (str == null) { bw.Write(-1); } else { var bytes = System.Text.Encoding.UTF8.GetBytes(str); bw.Write(bytes.Length); bw.Write(bytes); } } } } public static T Deserialize<T>(Stream stream) where T : new() { var br = new BinaryReader(stream, System.Text.Encoding.UTF8, true); var obj = new T(); var type = typeof(T); var properties = type.GetProperties(); foreach (var prop in properties) { if (prop.PropertyType == typeof(int)) { prop.SetValue(obj, br.ReadInt32()); } else if (prop.PropertyType == typeof(float)) { prop.SetValue(obj, br.ReadSingle()); } else if (prop.PropertyType == typeof(string)) { int length = br.ReadInt32(); if (length == -1) { prop.SetValue(obj, null); } else { var bytes = br.ReadBytes(length); prop.SetValue(obj, System.Text.Encoding.UTF8.GetString(bytes)); } } } return obj; } } }

image.png

📊 性能对比:数据说话

让我们用真实测试验证效果(10万次序列化测试):

C#
static void PerformanceTest() { const int iterations = 100_000; var testData = GenerateTestData(1000); // Source Generator序列化器 TestSourceGeneratorSerialization(testData, iterations); // System.Text.Json TestSystemTextJsonSerialization(testData, iterations); // 反射序列化器 TestReflectionSerialization(testData, iterations); }

核心优势总结:

  • 🔥 性能王者:比JSON序列化快10倍
  • 💾 体积最小:比JSON格式小40%+
  • 零反射:编译时生成,运行时直接调用
  • 🛡️ 类型安全:编译期类型检查,杜绝运行时错误

⚠️ 实战避坑指南

坑点1:命名空间问题

C#
// ❌ 错误:生成代码找不到类型 public static void Serialize(Person obj, Stream stream) // ✅ 正确:使用完整类型名 public static void Serialize(AppSourceGeneratorsSerializer.Person obj, Stream stream)

坑点2:项目引用配置

XML
<!-- ❌ 错误配置 --> <ProjectReference Include="Generator.csproj" /> <!-- ✅ 正确配置 --> <ProjectReference Include="Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />

坑点3:支持的数据类型

当前实现支持:intfloatstring,可根据需要扩展更多类型。

🔮 进阶扩展思路

  1. 支持更多类型:DateTime、Guid、枚举、集合等
  2. 版本兼容:字段版本标记,向后兼容
  3. 压缩优化:集成LZ4等压缩算法
  4. 异步支持:异步序列化方法生成

🎯 总结与思考

通过Source Generators,我们成功打造了一款性能碾压传统方案的极速序列化器。三个核心收获:

  1. 编译时代码生成是性能优化的终极武器 - 零运行时开销
  2. 二进制序列化在性能和体积上都有绝对优势 - 适合高性能场景
  3. Source Generators让复杂的元编程变得简单可控 - 是现代C#开发的必备技能

金句分享:

  • "编译时做的事,运行时就不用做"
  • "最快的反射就是不用反射"
  • "性能优化的极致是让问题在编译时消失"

💭 技术讨论:

  1. 你的项目中哪些场景最需要高性能序列化?
  2. 除了序列化,Source Generators还能用在哪些场景?

如果这篇文章帮你解决了性能问题,请转发给更多需要的同行!让我们一起推动C#技术社区的发展!

🔗 想了解更多C#高级技术?关注我,持续分享实战干货!

相关信息

通过网盘分享的文件:AppFastSerializerGenerator.zip 链接: https://pan.baidu.com/s/1O79ZGoN_P4TvOB_Wm3yfsg?pwd=y6ki 提取码: y6ki --来自百度网盘超级会员v9的分享

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!