typeshape-csharp
0.3.0
See the version list below for details.
dotnet add package typeshape-csharp --version 0.3.0
NuGet\Install-Package typeshape-csharp -Version 0.3.0
<PackageReference Include="typeshape-csharp" Version="0.3.0" />
paket add typeshape-csharp --version 0.3.0
#r "nuget: typeshape-csharp, 0.3.0"
// Install typeshape-csharp as a Cake Addin #addin nuget:?package=typeshape-csharp&version=0.3.0 // Install typeshape-csharp as a Cake Tool #tool nuget:?package=typeshape-csharp&version=0.3.0
typeshape-csharp
Contains a proof-of-concept port of the TypeShape library, adapted to patterns and idioms available in C#. The library provides a .NET datatype model that facilitates developing 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.
The project includes two typeshape model providers: one reflection-derived and one source generated. It follows that any datatype-generic application built on top of the typeshape model gets trim safety/NativeAOT support for free once it targets source generated models.
Using the library
Users can extract the typeshape model for a given type either using the built-in source generator:
ITypeShape<MyPoco> shape = SourceGenProvider.Default.MyPoco;
public record MyPoco(string x, string y);
[GenerateShape(typeof(MyPoco))]
public partial class SourceGenProvider { }
or by using the reflection provider:
using TypeShape.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 TypeShape'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
{
public override object? VisitType<T>(ITypeShape<T> typeShape, object? state)
{
// For the sake of simplicity, ignore collection types and just focus on properties/fields.
// Recursive generate counters for each individual property/field:
Func<T, int>[] propertyCounters = typeShape.GetProperties(nonPublic: false, includeFields: true)
.Where(prop => prop.HasGetter)
.Select(prop => (Func<T, int>)prop.Accept(this, null)!)
.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, null)!; // 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>(ITypeShape<T> typeShape)
=> (Func<T, int>)typeShape.Accept(s_visitor, null)!;
}
That we can then apply to the shape of our POCO like so:
ITypeShape<MyPoco> shape = SourceGenProvider.Default.MyPoco;
Func<MyPoco, int> pocoCounter = Counter.CreateCounter(shape);
pocoCounter(new MyPoco("x","y")); // 3
public record MyPoco(string? x, string? y);
[GenerateShape(typeof(MyPoco))]
public partial class SourceGenProvider { }
In essence, TypeShape 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 | net6.0 is compatible. 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 was computed. 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. |
-
net6.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.11.0 | 28 | 10/5/2024 |
0.10.2 | 90 | 9/13/2024 |
0.10.0 | 104 | 5/31/2024 |
0.9.2 | 118 | 5/19/2024 |
0.9.1 | 96 | 5/15/2024 |
0.9.0 | 92 | 5/13/2024 |
0.8.0 | 128 | 4/20/2024 |
0.7.1 | 125 | 4/4/2024 |
0.7.0 | 90 | 4/4/2024 |
0.6.1 | 108 | 2/1/2024 |
0.6.0 | 124 | 1/16/2024 |
0.5.2 | 139 | 12/12/2023 |
0.5.1 | 128 | 12/7/2023 |
0.5.0 | 132 | 12/7/2023 |
0.4.0 | 142 | 11/13/2023 |
0.3.3 | 135 | 11/4/2023 |
0.3.2 | 148 | 10/21/2023 |
0.3.1 | 145 | 10/7/2023 |
0.3.0 | 116 | 9/25/2023 |
0.2.1 | 141 | 9/23/2023 |
0.2.0 | 134 | 9/22/2023 |
0.1.3 | 124 | 9/18/2023 |
0.1.2 | 115 | 9/18/2023 |
0.1.1 | 111 | 9/17/2023 |
0.1.0 | 120 | 9/17/2023 |