PolyType 0.18.1
See the version list below for details.
dotnet add package PolyType --version 0.18.1
NuGet\Install-Package PolyType -Version 0.18.1
<PackageReference Include="PolyType" Version="0.18.1" />
<PackageVersion Include="PolyType" Version="0.18.1" />
<PackageReference Include="PolyType" />
paket add PolyType --version 0.18.1
#r "nuget: PolyType, 0.18.1"
#addin nuget:?package=PolyType&version=0.18.1
#tool nuget:?package=PolyType&version=0.18.1
PolyType
Contains a port of the TypeShape F# library, adapted to patterns and idioms available in C#. It provides a type model that facilitates development of high-performance datatype-generic components such as serializers, loggers, transformers and validators. At its core, the programming model employs a variation on the visitor pattern that enables strongly-typed traversal of arbitrary type graphs: it can be used to generate object traversal algorithms that incur zero allocation cost. See the project website for additional guides and API documentation.
The project includes two shape model providers: one reflection derived and one source generated. It follows that any datatype-generic application built on top of the shape model gets trim safety/NativeAOT support for free once it targets source generated models.
Using the library
Users can extract the shape model for a given type either using the built-in source generator:
ITypeShape<MyPoco> shape = TypeShapeProvider.Resolve<MyPoco>();
[GenerateShape] // Auto-generates a static abstract factory for ITypeShape<MyPoco>
public partial record MyPoco(string x, string y);
For types not accessible in the current compilation, the implementation can be generated using a separate witness type:
ITypeShape<MyPoco[]> shape = TypeShapeProvider.Resolve<MyPoco[], Witness>();
ITypeShape<MyPoco[][]> shape = TypeShapeProvider.Resolve<MyPoco[][], Witness>();
// Generates factories for both ITypeShape<MyPoco[]> and ITypeShape<MyPoco[][]>
[GenerateShape<MyPoco[]>]
[GenerateShape<MyPoco[][]>]
public partial class Witness;
The library also provides a reflection-based provider:
using PolyType.ReflectionProvider;
ITypeShape<MyPoco> shape = ReflectionTypeShapeProvider.Default.GetShape<MyPoco>();
public record MyPoco(string x, string y);
In both cases the providers will generate a strongly typed datatype model for MyPoco
. Models for types can be fed into datatype-generic consumers that can be declared using PolyType's visitor pattern.
Example: Writing a datatype-generic counter
The simplest possible example of a datatype-generic programming is counting the number of nodes that exist in a given object graph. This can be implemented by extending the TypeShapeVisitor
class:
public sealed partial class CounterVisitor : TypeShapeVisitor
{
// For the sake of simplicity, ignore collection types and just focus on properties/fields.
public override object? VisitObject<T>(IObjectTypeShape<T> objectShape, object? state)
{
// Recursively generate counters for each individual property/field:
Func<T, int>[] propertyCounters = objectShape.GetProperties()
.Where(prop => prop.HasGetter)
.Select(prop => (Func<T, int>)prop.Accept(this)!)
.ToArray();
// Compose into a counter for the current type.
return new Func<T?, int>(value =>
{
if (value is null)
return 0;
int count = 1; // the current node itself
foreach (Func<T, int> propertyCounter in propertyCounters)
count += propertyCounter(value);
return count;
});
}
public override object? VisitProperty<TDeclaringType, TPropertyType>(IPropertyShape<TDeclaringType, TPropertyType> propertyShape, object? state)
{
Getter<TDeclaringType, TPropertyType> getter = propertyShape.GetGetter(); // extract the getter delegate
var propertyTypeCounter = (Func<TPropertyType, int>)propertyShape.PropertyType.Accept(this)!; // extract the counter for the property type
return new Func<TDeclaringType, int>(obj => propertyTypeCounter(getter(ref obj))); // compose to a property-specific counter
}
}
We can now define a counter factory using the visitor:
public static class Counter
{
private readonly static CounterVisitor s_visitor = new();
public static Func<T?, int> CreateCounter<T>() where T : IShapeable<T>
{
ITypeShape<T> typeShape = T.GetShape();
return (Func<T?, int>)typeShape.Accept(s_visitor)!;
}
}
That we can then apply to the shape of our POCO like so:
Func<MyPoco?, int> pocoCounter = Counter.CreateCounter<MyPoco>();
[GenerateShape]
public partial record MyPoco(string? x, string? y);
In essence, PolyType uses the visitor to fold a strongly typed Func<MyPoco?, int>
counter delegate,
but the delegate itself doesn't depend on the visitor once invoked: it only defines a chain of strongly typed
delegate invocations that are cheap to invoke once constructed:
pocoCounter(new MyPoco("x", "y")); // 3
pocoCounter(new MyPoco("x", null)); // 2
pocoCounter(new MyPoco(null, null)); // 1
pocoCounter(null); // 0
For more details, please consult the README file at the project page.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | 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 was computed. 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 was computed. 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. |
-
net8.0
- No dependencies.
NuGet packages (3)
Showing the top 3 NuGet packages that depend on PolyType:
Package | Downloads |
---|---|
PolyType.TestCases
Practical generic programming for C# |
|
PolyType.Examples
Practical generic programming for C# |
|
Nerdbank.MessagePack
A fast and more user-friendly MessagePack serialization library for .NET and .NET Framework. This package is brought to you by one of the two major contributors to MessagePack-CSharp. As its natural successor, this library comes packed with features that its predecessor lacks, and has ongoing support. Premium support for trimming and Native AOT, secure deserialization of untrusted data, async serialization, streaming deserialization, skip serializing of default values, reference preservation, and support for reference cycles. Also features an automatic structural equality API. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
0.45.1 | 195 | 6/27/2025 |
0.44.1 | 176 | 6/26/2025 |
0.43.1 | 174 | 6/26/2025 |
0.42.1 | 2,390 | 6/10/2025 |
0.41.1 | 405 | 6/3/2025 |
0.40.1 | 354 | 5/23/2025 |
0.39.1 | 190 | 5/22/2025 |
0.38.1 | 196 | 5/20/2025 |
0.37.1 | 595 | 4/30/2025 |
0.36.1 | 326 | 4/21/2025 |
0.35.1 | 228 | 4/20/2025 |
0.34.1 | 309 | 3/31/2025 |
0.33.1 | 292 | 3/28/2025 |
0.32.1 | 537 | 3/25/2025 |
0.31.1 | 318 | 3/18/2025 |
0.30.1 | 177 | 3/14/2025 |
0.29.3 | 385 | 3/5/2025 |
0.29.1 | 372 | 3/4/2025 |
0.28.5 | 159 | 3/1/2025 |
0.28.1 | 202 | 3/1/2025 |
0.27.1 | 343 | 2/13/2025 |
0.26.5 | 802 | 2/3/2025 |
0.26.1 | 363 | 1/23/2025 |
0.25.1 | 10,638 | 1/19/2025 |
0.24.1 | 501 | 12/27/2024 |
0.23.1 | 1,355 | 12/12/2024 |
0.22.1 | 354 | 12/5/2024 |
0.21.1 | 257 | 11/26/2024 |
0.20.1 | 223 | 11/23/2024 |
0.19.3 | 127 | 11/19/2024 |
0.19.2 | 119 | 11/19/2024 |
0.19.1 | 117 | 11/19/2024 |
0.18.2 | 117 | 11/18/2024 |
0.18.1 | 187 | 11/17/2024 |
0.17.1 | 179 | 11/16/2024 |
0.16.10 | 129 | 11/15/2024 |
0.16.1 | 305 | 11/8/2024 |