PolyType is a practical datatype-generic programming library for .NET types. It is a direct adaptation of the TypeShape library for F#, adapted to patterns and idioms available in C#. See the project website for additional background and API documentation.
The project is named after polytypic programming, another term used to describe datatype-generic programming.
You can try the library by installing the PolyType
NuGet package:
$ dotnet add package PolyType
which includes the core types and source generator for generating type shapes:
using PolyType;
[GenerateShape]
public partial record Person(string name, int age);
Doing this will augment Person
with an implementation of the IShapeable<Person>
interface. This suffices to make Person
usable with any library that targets the PolyType core abstractions. You can try this out by installing the built-in example libraries:
$ dotnet add package PolyType.Examples
Here's how the same value can be serialized to three separate formats.
using PolyType.Examples.JsonSerializer;
using PolyType.Examples.CborSerializer;
using PolyType.Examples.XmlSerializer;
Person person = new("Pete", 70);
JsonSerializerTS.Serialize(person); // {"Name":"Pete","Age":70}
XmlSerializer.Serialize(person); // <value><Name>Pete</Name><Age>70</Age></value>
CborSerializer.EncodeToHex(person); // A2644E616D656450657465634167651846
Since the application uses a source generator to produce the shape for Person
, it is fully compatible with Native AOT. See the shape providers article for more details on how to use the library with your types.
PolyType is a meta-library that facilitates rapid development of high performance datatype-generic programs. It exposes a simplified model for .NET types that makes it easy for library authors to publish production-ready components in just a few lines of code. The built-in source generator ensures that any library built on top of the PolyType abstractions gets Native AOT support for free.
As a library author, PolyType lets you write high performance, feature complete generic components that target its core abstractions. For example, a parser API using PolyType might look as follows:
public static class MyFancyParser
{
public static T? Parse<T>(string myFancyFormat) where T : IShapeable<T>;
}
As an end user, PolyType lets you generate shape models for your own types that can be used with one or more supported libraries:
Person? person = MyFancyParser.Parse<Person>(format); // Compiles
[GenerateShape] // Generate an IShapeable<TPerson> implementation
partial record Person(string name, int age, List<Person> children);
For more information see:
PolyType.Examples
project for advanced examples of libraries built on top of PolyType.The repo includes a JSON serializer built on top of the Utf8JsonWriter
/Utf8JsonReader
primitives provided by System.Text.Json. At the time of writing, the full implementation is just under 1200 lines of code but exceeds STJ's built-in JsonSerializer
both in terms of supported types and performance.
Here's a benchmark comparing System.Text.Json
with the included PolyType implementation:
Method | Mean | Ratio | Allocated | Alloc Ratio |
---|---|---|---|---|
Serialize_StjReflection | 491.9 ns | 1.00 | 312 B | 1.00 |
Serialize_StjSourceGen | 467.0 ns | 0.95 | 312 B | 1.00 |
Serialize_StjSourceGen_FastPath | 227.2 ns | 0.46 | - | 0.00 |
Serialize_PolyTypeReflection | 277.9 ns | 0.57 | - | 0.00 |
Serialize_PolyTypeSourceGen | 273.6 ns | 0.56 | - | 0.00 |
Method | Mean | Ratio | Allocated | Alloc Ratio |
---|---|---|---|---|
Deserialize_StjReflection | 1,593.0 ns | 1.00 | 1024 B | 1.00 |
Deserialize_StjSourceGen | 1,530.3 ns | 0.96 | 1000 B | 0.98 |
Deserialize_PolyTypeReflection | 773.1 ns | 0.49 | 440 B | 0.43 |
Deserialize_PolyTypeSourceGen | 746.7 ns | 0.47 | 440 B | 0.43 |
Even though both serializers target the same underlying reader and writer types, the PolyType implementation is ~75% faster for serialization and ~100% faster for deserialization, when compared with System.Text.Json's metadata serializer. As expected, fast-path serialization is still fastest since its implementation is fully inlined.
The following code bases are based upon PolyType and may be worth checking out.
The repo consists of the following projects:
PolyType
library containing:
PolyType.SourceGenerator
project contains the built-in source generator implementation.PolyType.Roslyn
library exposes a set of components for extracting data models from Roslyn type symbols. Used as the foundation for the built-in source generator.PolyType.Examples
containing library examples:
ConfigurationBinder
like implementation,System.Random
,IEqualityComparer<T>
generator for POCOs and collections,applications
folder contains sample Native AOT console applications.