LightProto 0.9.0
See the version list below for details.
dotnet add package LightProto --version 0.9.0
NuGet\Install-Package LightProto -Version 0.9.0
<PackageReference Include="LightProto" Version="0.9.0" />
<PackageVersion Include="LightProto" Version="0.9.0" />
<PackageReference Include="LightProto" />
paket add LightProto --version 0.9.0
#r "nuget: LightProto, 0.9.0"
#:package LightProto@0.9.0
#addin nuget:?package=LightProto&version=0.9.0
#tool nuget:?package=LightProto&version=0.9.0
LightProto 🚀
A high‑performance, Native AOT–friendly Protocol Buffers implementation for C#/.NET.
Warnings ⚠️
This project is under active development and may introduce breaking changes. Use in production at your own risk.
Why LightProto? 🤔
protobuf-net is a popular Protocol Buffers implementation in .NET, but some scenarios (especially Native AOT) can be challenging due to runtime reflection and dynamic generation. LightProto addresses this with compile-time code generation and a protobuf-net–style API.
Features ✨
- Source generator–powered serializers/parsers at compile time
- protobuf-net–style Serializer API and familiar attributes
- Target frameworks: netstandard2.0, net8.0, net9.0, net10.0
- Performance is about 20%-50% better than protobuf-net
- AOT-friendly by design
- Stream and IBufferWriter<byte> serialization or ToByteArray
- ReadOnlySpan<byte>/ReadOnlySequence<byte>/Stream deserialization
Performance & Benchmarks 📊
The following benchmarks compare serialization performance between LightProto, protobuf-net, and Google.Protobuf.
You can reproduce these by cloning the repo and running tests/Benchmark.
BenchmarkDotNet v0.15.3, Windows 11 (10.0.26100.4652/24H2/2024Update/HudsonValley)
AMD Ryzen 7 5800X 3.80GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 10.0.100-rc.1.25451.107
[Host] : .NET 8.0.20 (8.0.20, 8.0.2025.41914), X64 RyuJIT x86-64-v3
.NET 10.0 : .NET 10.0.0 (10.0.0-rc.1.25451.107, 10.0.25.45207), X64 RyuJIT x86-64-v3
.NET 8.0 : .NET 8.0.20 (8.0.20, 8.0.2025.41914), X64 RyuJIT x86-64-v3
.NET 9.0 : .NET 9.0.9 (9.0.9, 9.0.925.41916), X64 RyuJIT x86-64-v3
| Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|
| Serialize_ProtoBuf_net | .NET 10.0 | .NET 10.0 | 645.6 μs | 12.70 μs | 11.88 μs | 1.39 | 0.03 | 526.41 KB | 1.03 |
| Serialize_GoogleProtoBuf | .NET 10.0 | .NET 10.0 | 539.9 μs | 10.71 μs | 12.75 μs | 1.16 | 0.03 | 512.95 KB | 1.00 |
| Serialize_LightProto | .NET 10.0 | .NET 10.0 | 465.1 μs | 7.88 μs | 6.99 μs | 1.00 | 0.02 | 512.95 KB | 1.00 |
| Serialize_ProtoBuf_net | .NET 8.0 | .NET 8.0 | 757.0 μs | 12.80 μs | 11.98 μs | 1.42 | 0.04 | 526.41 KB | 1.03 |
| Serialize_GoogleProtoBuf | .NET 8.0 | .NET 8.0 | 553.9 μs | 10.97 μs | 9.72 μs | 1.04 | 0.03 | 512.95 KB | 1.00 |
| Serialize_LightProto | .NET 8.0 | .NET 8.0 | 531.9 μs | 10.52 μs | 14.04 μs | 1.00 | 0.04 | 512.95 KB | 1.00 |
| Serialize_ProtoBuf_net | .NET 9.0 | .NET 9.0 | 712.6 μs | 13.61 μs | 12.73 μs | 1.39 | 0.04 | 526.41 KB | 1.03 |
| Serialize_GoogleProtoBuf | .NET 9.0 | .NET 9.0 | 546.7 μs | 10.70 μs | 16.33 μs | 1.07 | 0.04 | 512.95 KB | 1.00 |
| Serialize_LightProto | .NET 9.0 | .NET 9.0 | 513.6 μs | 10.15 μs | 13.89 μs | 1.00 | 0.04 | 512.95 KB | 1.00 |
| Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio |
|---|---|---|---|---|---|---|---|---|---|
| Deserialize_ProtoBuf_net | .NET 10.0 | .NET 10.0 | 569.2 μs | 10.88 μs | 12.53 μs | 1.38 | 0.04 | 562 KB | 0.84 |
| Deserialize_GoogleProtoBuf | .NET 10.0 | .NET 10.0 | 441.4 μs | 8.67 μs | 10.64 μs | 1.07 | 0.04 | 648.7 KB | 0.97 |
| Deserialize_LightProto | .NET 10.0 | .NET 10.0 | 411.5 μs | 8.08 μs | 9.92 μs | 1.00 | 0.03 | 665.95 KB | 1.00 |
| Deserialize_ProtoBuf_net | .NET 8.0 | .NET 8.0 | 688.0 μs | 13.51 μs | 15.56 μs | 1.55 | 0.05 | 562 KB | 0.84 |
| Deserialize_GoogleProtoBuf | .NET 8.0 | .NET 8.0 | 595.5 μs | 11.51 μs | 16.14 μs | 1.34 | 0.04 | 648.7 KB | 0.97 |
| Deserialize_LightProto | .NET 8.0 | .NET 8.0 | 444.8 μs | 8.88 μs | 9.12 μs | 1.00 | 0.03 | 665.95 KB | 1.00 |
| Deserialize_ProtoBuf_net | .NET 9.0 | .NET 9.0 | 662.3 μs | 12.60 μs | 11.17 μs | 1.53 | 0.04 | 562 KB | 0.84 |
| Deserialize_GoogleProtoBuf | .NET 9.0 | .NET 9.0 | 491.7 μs | 9.64 μs | 13.52 μs | 1.14 | 0.04 | 648.7 KB | 0.97 |
| Deserialize_LightProto | .NET 9.0 | .NET 9.0 | 431.9 μs | 8.33 μs | 9.25 μs | 1.00 | 0.03 | 665.95 KB | 1.00 |
Note: Results vary by hardware, runtime, and data model. Please run the benchmarks on your environment for the most relevant numbers.
Quick Start ⚡
Install from NuGet:
dotnet add package LightProto
Define your contracts (partial classes) using LightProto attributes:
using LightProto;
[ProtoContract]
public partial class Person
{
[ProtoMember(1)]
public string Name { get; set; } = string.Empty;
[ProtoMember(2)]
public int Age { get; set; }
}
var person = new Person { Name = "Alice", Age = 30 };
// Serialize to a byte[]
byte[] bytes = person.ToByteArray();
// person.ToByteArray(Person.ProtoWriter); // use this overload when .netstandard2.0
// Or serialize to a Stream
using var stream = new MemoryStream();
Serializer.Serialize(stream, person);
// Serializer.Serialize(stream, person, Person.ProtoWriter); // use this overload when .netstandard2.0
byte[] data = stream.ToArray();
// Deserialize from byte[] (ReadOnlySpan<byte> overload will be used)
Person fromBytes = Serializer.Deserialize<Person>(bytes);
// Person fromBytes = Serializer.Deserialize<Person>(bytes,Person.ProtoReader); // use this overload when .netstandard2.0
// Or deserialize from Stream
using var input = new MemoryStream(data);
Person fromStream = Serializer.Deserialize<Person>(input);
// Person fromStream = Serializer.Deserialize<Person>(input,Person.ProtoReader); // use this overload when .netstandard2.0
Migration from protobuf-net 🔁
Most code migrates by swapping the namespace and marking your types partial.
- Replace ProtoBuf with LightProto.
- Mark serializable types as partial.
- Remove runtime configuration (e.g., RuntimeTypeModel). LightProto generates code at compile time.
Example:
- using ProtoBuf;
+ using LightProto;
[ProtoContract]
- public class Person
+ public partial class Person
{
[ProtoMember(1)]
public string Name { get; set; } = string.Empty;
[ProtoMember(2)]
public int Age { get; set; }
}
var myObject = new Person { Name = "Alice", Age = 30 };
// Serialization
var stream = new MemoryStream();
Serializer.Serialize(stream, myObject);
byte[] data = stream.ToArray();
// Deserialization
var obj = Serializer.Deserialize<Person>(new ReadOnlySpan<byte>(data));
Common replacements:
- RuntimeTypeModel and runtime surrogates → use compile-time attributes (see Surrogates below).
- Non-partial types → mark as partial to enable generator output.
Need to know 🧠
LightProto aims to minimize differences from protobuf-net; notable ones include:
Partial classes required
protobuf-net: partial not required
LightProto: mark [ProtoContract] types as partial so the generator can emit code.
Generic Serialize/Deserialize type constraint
protobuf-net: Serializer.Serialize<int>(...) and Serializer.Deserialize<int>(...)
LightProto: T must implement IProtoParser<T> (i.e., a generated message type); primitives are not supported directly. Use another method which pass IProtoReader/Writer explicitly.
int a=10;
ArrayBufferWriter<byte> writer = new ArrayBufferWriter<byte>();
LightProto.Serializer.Serialize<int>(writer, a,Int32ProtoParser.Writer); // must pass writer
var bytes = a.ToByteArray(Int32ProtoParser.Writer); // extension method
int result = LightProto.Serializer.Deserialize<int>(bytes,Int32ProtoParser.Reader); // must pass reader
List<int> list=[1,2,3];
var bytes = list.ToByteArray(Int32ProtoParser.Writer);// extension method
ArrayBufferWriter<byte> writer = new ArrayBufferWriter<byte>();
LightProto.Serializer.Serialize(writer, list,Int32ProtoParser.Writer);// must pass element writer
List<int> arr2=LightProto.Serializer.Deserialize<List<int>,int>(bytes,Int32ProtoParser.Reader); // must pass element reader
.netstandard
In .netstandard targeting platform such as .NET framework, we can't use static virtual members in interface to find ProtoReader/Writer.
So LightProto requires user to specify a ProtoWriter when serializing and a ProtoReader when deserializing.
For [ProtoContract] marked MessageType the ProtoReader/Writer is generated by LightProto, just use MessageType.ProtoReader/Writer.
For primitive types, LightProto has predefined in LightProto.Parser namespace, such as LightProto.Parser.DateTimeParser.
IExtensible
protobuf-net: supports IExtensible for dynamic extensions
LightProto: IExtensible is defined for compatibility only and has no effect
Surrogates
protobuf-net: can register surrogates via RuntimeTypeModel at runtime
LightProto: You can specify a custom ProtoParserType for MessageType.
For example, MessageType is Person, custom ProtoParserType is PersonProtoParser, you can use following attribute to specify a custom ProtoParserType, list as precedence order:
- member level:
[ProtoMember(1,ParserType=typeof(PersonProtoParser))] - class level:
[ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))] - module/assembly level:
[ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))](messageType and parserType should not be in same assembly, If so, type level attribute is suggested.) - type level:
[ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))] - default:
LightProto.Parser.PersonProtoParser
The ProtoParserType must implement IProtoParser<MessageType>. The easiest way is defining a SurrogateType with [ProtoContract] and mark [ProtoSurrogateFor<MessageType>].
Example for Person(can be any type):
[ProtoParserType(typeof(PersonProtoParser))] // type level ProtoParser
public class Person
{
public string Name {get; set;}
Person(){}
public static Person FromName(string name) => new Person() { Name = name };
}
[ProtoContract]
[ProtoSurrogateFor<Person>] // mark this to tell source generator generate IProtoParser<Person> instead of `IProtoParser<PersonProtoParser>`
public partial struct PersonProtoParser
{
[ProtoMember(1)]
internal string Name { get; set; }
public static implicit operator Person(PersonProtoParser parser) //must define implicit conversions for surrogate type
{
return Person.FromName(parser.Name);
}
public static implicit operator PersonProtoParser(Person value) //must define implicit conversions for surrogate type
{
return new PersonProtoParser() { Name = value.Name };
}
}
[assembly: ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))] // assembly level ProtoParser
[ProtoParserTypeMap(typeof(Person), typeof(PersonProtoParser))] // class level ProtoParser
public class MessageContract
{
[ProtoMember(1,ParserType=typeof(PersonProtoParser))] //member level ProtoParser
public Person Person {get; set;}
}
You can also read/write raw binary data, for example see:DateOnlyProtoParser
StringIntern
protobuf-net: Use RuntimeTypeModel.Default.StringInterning = true; to enable string interning globally
LightProto: [StringIntern] attribute can apply to individual string members/class/module/assembly.
RuntimeTypeModel
Not supported; all configuration is static via attributes and generated code
If you encounter different behavior versus protobuf-net, please open an issue.
Working with .proto files 📄
LightProto doesn’t ship a .proto → C# generator. You can generate C# using protobuf-net (or other tools), then adapt the output to LightProto (typically replacing the ProtoBuf namespace with LightProto and marking types partial). If something doesn’t work, please file an issue.
Contributing 🤝
Contributions are welcome! Please see CONTRIBUTING for detailed contribution guidelines.
License 📄
MIT License — see LICENSE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Collections.Immutable (>= 9.0.0)
- System.Memory (>= 4.6.3)
- System.Runtime.CompilerServices.Unsafe (>= 6.1.2)
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (8)
Showing the top 5 NuGet packages that depend on LightProto:
| Package | Downloads |
|---|---|
|
Ahsoka.Core
Package Description |
|
|
JK.Mexc.Net
A high-performance .NET client library for the Mexc REST and WebSocket APIs, providing strongly typed models, automatic WebSocket reconnection, client-side rate limiting, local order book support, and seamless integration with other CryptoExchange.Net based clients. |
|
|
EnovationCloud.Core
Package Description |
|
|
Ahsoka.Extensions.Storyboard
Storyboard Extension for OpenPV 5.0 // 60f365e6af3b712659435adb8e2f8c34e4c7c2f5 - OpenPV_Extension_Info { "description": "Storyboard Extension for OpenPV", "extensionName": "Storyboard Extension", "packageName": "Ahsoka.Extensions.Storyboard", "uxpackageName": "Ahsoka.Extensions.Storyboard.Ux", "serviceConfigurations": [] } |
|
|
Ahsoka.Extensions.AudioManager
AudioManager Extension for OpenPV // 60f365e6af3b712659435adb8e2f8c34e4c7c2f5 - OpenPV_Extension_Info { "extensionName": "Audio Manager Extension", "packageName": "Ahsoka.Extensions.AudioManager", "uxpackageName": "Ahsoka.Extensions.AudioManager.UX", "serviceConfigurations": [ { "socketType": "TcpSocket", "serviceName": "AudioManagerService", "tcpListenAddress": "localhost", "tcpConnectionAddress": "localhost", "dataChannel": 6302, "behaviors": "AutoStart" }, { "socketType": "TcpSocket", "serviceName": "A2BService", "tcpListenAddress": "localhost", "tcpConnectionAddress": "localhost", "dataChannel": 6303, "behaviors": "AutoStart" }, { "socketType": "TcpSocket", "serviceName": "TunerService", "tcpListenAddress": "localhost", "tcpConnectionAddress": "localhost", "dataChannel": 6304, "behaviors": "AutoStart" } ] } |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.3.2 | 970 | 5/6/2026 |
| 1.3.1 | 183 | 4/25/2026 |
| 1.3.0 | 131 | 4/13/2026 |
| 1.2.1 | 1,045 | 1/26/2026 |
| 1.2.0 | 141 | 1/21/2026 |
| 1.1.1 | 118 | 1/12/2026 |
| 1.1.0 | 112 | 1/10/2026 |
| 1.0.1 | 123 | 1/6/2026 |
| 1.0.0 | 119 | 1/4/2026 |
| 0.9.5 | 201 | 12/24/2025 |
| 0.9.4 | 288 | 12/18/2025 |
| 0.9.3 | 289 | 12/18/2025 |
| 0.9.2 | 293 | 12/17/2025 |
| 0.9.1 | 291 | 12/16/2025 |
| 0.9.0 | 12,265 | 12/12/2025 |
| 0.8.5 | 403 | 11/17/2025 |
| 0.8.4 | 247 | 10/27/2025 |
| 0.8.3 | 129 | 10/25/2025 |
| 0.8.2 | 138 | 10/25/2025 |
| 0.8.1 | 212 | 10/23/2025 |