Serialization Generator
On this page
SerializeFormatterGenerator (analyzers/Nalix.Analyzers.Generators/SerializeFormatterGenerator.cs) is an incremental Roslyn source generator that emits IFormatter<T> and IFillableFormatter<T> implementations at compile time for classes and structs annotated with [GenerateFormatter].
Source Mapping
analyzers/Nalix.Analyzers.Generators/SerializeFormatterGenerator.csanalyzers/Nalix.Analyzers.Generators/Internal/GeneratorDiagnosticDescriptors.cs
How It Works
- Target Discovery & Validation:
- Scans all classes and structs marked with
[GenerateFormatter](inherited automatically byPacketBase<TSelf>subclasses). - For reference types (
class), verifies that an accessible staticCreate()method exists (PacketBase<TSelf>.Create()satisfies this automatically). If a custom[GenerateFormatter]class does not inheritPacketBase<TSelf>and lacksstatic T Create(), the generator reports errorNALIX059.
- Scans all classes and structs marked with
- Layout & Member Ordering:
- Inspects
[SerializePackable(SerializeLayout.Explicit)]vs.[SerializePackable(SerializeLayout.Sequential)]. - Orders fields and properties across the inheritance hierarchy (starting with
PacketBase.Headerat[SerializeHeader(PacketHeaderOffset.Header)]order0). - Excludes
[SerializeIgnore]members.
- Inspects
- Formatter Generation:
- For reference types (
class), implements bothIFormatter<T>andIFillableFormatter<T>, renting pooled instances viaT.Create()duringDeserialize(ref DataReader)and populating them in-place viaFill(ref DataReader, T value). - For value types (
struct), implementsIFormatter<T>.
- For reference types (
- Automatic
[ModuleInitializer]Registration:- Emits
GeneratedFormattersBootstrap.InitializeGeneratedFormatters()which calls:FormatterProvider.RegisterGenerated<T>(new T_Formatter())(orFormatterProvider.RegisterComplex<T>(...))FormatterProvider.RegisterCollectionFormatters<TElement>()for anyTElement[]orList<TElement>memberFormatterProvider.RegisterDictionary<TKey, TValue>()for anyDictionary<TKey, TValue>memberFormatterProvider.RegisterTuple<T1, ...>()for anyValueTuple<...>member
- Emits
Example
1. Source Packet Definition
using Nalix.Abstractions.Networking.Packets;
using Nalix.Abstractions.Serialization;
using Nalix.Codec.DataFrames;
[Packet(0x1001)]
[GenerateFormatter]
[SerializePackable(SerializeLayout.Sequential)]
public sealed partial class DynamicHintStringPacket : PacketBase<DynamicHintStringPacket>, IPacketStaticOpcode
{
public static ushort StaticOpCode => 0x1001;
[SerializeDynamicSize(64)]
public string Message { get; set; } = string.Empty;
}
2. Generated Formatter Code
[StackTraceHidden]
[DebuggerStepThrough]
[SkipLocalsInit]
internal sealed class DynamicHintStringPacketFormatter :
IFillableFormatter<DynamicHintStringPacket>,
IFormatter<DynamicHintStringPacket>
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Serialize(ref DataWriter writer, in DynamicHintStringPacket value)
{
ArgumentNullException.ThrowIfNull(value, "value");
// 6-byte PacketHeader (Order 0) is written first
(ref writer).WriteUnmanaged(value.Header);
IFormatter<string> formatter = FormatterProvider.Get<string>();
string message = value.Message;
formatter.Serialize(ref writer, in message);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public DynamicHintStringPacket Deserialize(ref DataReader reader)
{
// Rents an instance from ObjectPoolManager via PacketBase<TSelf>.Create()
DynamicHintStringPacket instance = PacketBase<DynamicHintStringPacket>.Create();
this.Fill(ref reader, instance);
return instance;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public void Fill(ref DataReader reader, DynamicHintStringPacket value)
{
ArgumentNullException.ThrowIfNull(value, "value");
value.Header = (ref reader).ReadUnmanaged<PacketHeader>();
value.Message = FormatterProvider.Get<string>().Deserialize(ref reader);
}
}
Diagnostic Emitted
| ID | Title | Severity | Cause |
|---|---|---|---|
NALIX059 | Missing static Create() method | Error | A reference type marked [GenerateFormatter] does not define a static Create() factory method. |