AnAspect.OneOf 0.1.1-alpha

This is a prerelease version of AnAspect.OneOf.
dotnet add package AnAspect.OneOf --version 0.1.1-alpha
                    
NuGet\Install-Package AnAspect.OneOf -Version 0.1.1-alpha
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AnAspect.OneOf" Version="0.1.1-alpha">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AnAspect.OneOf" Version="0.1.1-alpha" />
                    
Directory.Packages.props
<PackageReference Include="AnAspect.OneOf">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AnAspect.OneOf --version 0.1.1-alpha
                    
#r "nuget: AnAspect.OneOf, 0.1.1-alpha"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AnAspect.OneOf@0.1.1-alpha
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AnAspect.OneOf&version=0.1.1-alpha&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=AnAspect.OneOf&version=0.1.1-alpha&prerelease
                    
Install as a Cake Tool

AnAspect.OneOf - IL-Weaved Discriminated Unions

A high-performance discriminated union library for C# that uses IL weaving (Fody) to eliminate memory overhead while maintaining full type safety at compile time.

๐Ÿš€ Key Innovation

Traditional OneOf implementations use structs or classes with discriminators, causing significant memory overhead. This library provides the same developer experience but rewrites the IL at build time to use plain object references, achieving:

  • 30-57% memory reduction compared to traditional OneOf implementations
  • Zero runtime overhead for type checking (direct is operator)
  • Full IntelliSense support at development time
  • Type safety maintained at compile time

๐Ÿ“Š Benchmark Results (.NET 10)

Test 1: Traditional OneOf<User, Admin> (reference types)
  Allocated: 10,413,984 bytes (104.00 bytes per iteration)
  Per OneOf wrapper: ~40.00 bytes

Test 2: IL-Weaved OneOf<User, Admin> (object reference only)
  Allocated: 7,199,992 bytes (71.00 bytes per iteration)
  Per OneOf wrapper: ~7.00 bytes

Memory Savings: 30.9% reduction

Test 3: Traditional OneOf<Point, int> (value types)
  Allocated: 5,601,560 bytes (56.00 bytes per iteration)

Test 4: IL-Weaved OneOf<Point, int> (value types)
  Allocated: 2,402,840 bytes (24.00 bytes per iteration)

Memory Savings: 57.1% reduction

๐ŸŽฏ How It Works

At Compile Time (What You Write):

OneOf<int, string> value = OneOf<int, string>.From(42);
if (value.IsT1)
{
    int result = value.AsT1();
    Console.WriteLine(result);
}

After IL Weaving (What Gets Executed):

object value = (object)42;  // Direct boxing
if (value is int)           // Direct type check
{
    int result = (int)value; // Direct cast
    Console.WriteLine(result);
}

The weaver automatically:

  1. โœ… Retargets local variables from OneOf<T1,T2> โ†’ object
  2. โœ… Replaces From(value) with direct value (boxed if value type)
  3. โœ… Replaces IsT1 with value is T1
  4. โœ… Replaces AsT1() with (T1)value

๐Ÿ“ฆ Installation

Simple - Just 2 packages:

<ItemGroup>
  <PackageReference Include="Fody" Version="6.8.0" PrivateAssets="all" />
  <PackageReference Include="AnAspect.OneOf" Version="0.1.1-alpha" />
</ItemGroup>

Note: The main package automatically includes the source generator and attributes as dependencies.

2. Add FodyWeavers.xml

<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <OneOf />
</Weavers>

3. Use OneOf

using OneOf;

// Reference types (optimal - no boxing)
OneOf<User, Admin> user = OneOf<User, Admin>.From(new User("Alice", 30));
if (user.IsT1)
{
    var u = user.AsT1();
    Console.WriteLine($"User: {u.Name}, Age: {u.Age}");
}

// Value types (boxing required, but still efficient)
OneOf<int, string> value = OneOf<int, string>.From(42);
if (value.IsT1)
{
    Console.WriteLine($"Got int: {value.AsT1()}");
}

// Mixed types
OneOf<Point, string> mixed = OneOf<Point, string>.From(new Point(10, 20));
if (mixed.IsT1)
{
    var p = mixed.AsT1();
    Console.WriteLine($"Point: ({p.X}, {p.Y})");
}

โšก Performance Characteristics

Memory Usage:

  • Reference types: 1 object reference (8 bytes on x64) - OPTIMAL
  • Value types: 1 object reference + boxing overhead - acceptable trade-off
  • Discriminator overhead: ELIMINATED (0 bytes vs 1-8 bytes in traditional)

Performance:

  • Type checking: Direct is operator (no discriminator comparison)
  • Value extraction: Direct cast (no wrapper unwrapping)
  • Creation: Direct reference/box (no wrapper allocation for ref types)

๐Ÿ—๏ธ Architecture

Components:

  1. OneOf.Attributes: Marker attribute for Fody detection
  2. OneOf.SourceGenerator: Generates OneOf<T1,T2> class at compile time
  3. OneOf.Fody: IL weaver that transforms the generated code
  4. OneOf.Analyzers (TODO): Compile-time warnings and best practices

Supported Patterns:

  • โœ… From(T) - Create OneOf from value
  • โœ… IsT1, IsT2 - Type checking
  • โœ… AsT1(), AsT2() - Value extraction with exception on mismatch
  • โœ… Implicit conversions from T1/T2
  • โš ๏ธ TryGetT1(out T) - Not yet optimized (use Is/As pattern instead)

๐ŸŽ“ Best Practices

DO:

  • โœ… Use reference types in OneOf when possible (zero boxing overhead)
  • โœ… Use Is/As pattern for best performance
  • โœ… Store OneOf in local variables for optimal weaving

AVOID:

  • โš ๏ธ Don't use TryGet methods (not yet weaved - use Is/As instead)
  • โš ๏ธ Be aware of boxing overhead with value types
  • โš ๏ธ Debugger shows object after weaving (not OneOf<T1,T2>)

๐Ÿ“‹ Current Limitations

  • Only OneOf<T1, T2> supported (T3, T4, etc. TODO)
  • TryGet methods not yet rewritten (use Is/As pattern)
  • Fields/parameters/returns not yet retargeted to object (locals only)
  • No custom serializer support yet

๐Ÿ”ฌ Technical Details

IL Transformation Examples:

Before:

ldloc.0          // Load OneOf<int,string> local
call get_IsT1    // Call IsT1 property
brfalse.s IL_000d
ldloc.0
call AsT1        // Call AsT1 method

After:

ldloc.0          // Load object local
isinst int32     // Direct type check
brfalse.s IL_000d
ldloc.0
unbox.any int32  // Direct unbox

๐Ÿงช Testing

Run tests:

dotnet test tests/OneOf.Tests/OneOf.Tests.csproj

Run benchmarks:

dotnet run --project tests/OneOf.Benchmarks/OneOf.Benchmarks.csproj -c Release -- --manual

๐Ÿ“ License

MIT License - See LICENSE file for details.

๐Ÿค Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

GitHub: https://github.com/Pouria7/AnAspect.OneOf

๐ŸŽฏ Roadmap

  • Support OneOf<T1, T2, T3, ..., T8>
  • Implement TryGet IL rewriting
  • Retarget fields/parameters/returns to object
  • Custom serialization support
  • Roslyn analyzers for best practices
  • NuGet packaging
  • Documentation site

โš ๏ธ Disclaimer

This is an experimental project demonstrating advanced IL weaving techniques. Use in production at your own risk. The trade-off is:

  • Gain: Significant memory savings, zero-cost abstractions
  • Cost: Boxing for value types, IL weaving complexity, debugger experience

For most applications, the memory savings and performance gains make this trade-off worthwhile, especially when using reference types.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

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.1.1-alpha 137 12/13/2025
0.1.0-alpha 128 12/13/2025