ValueUnion 0.1.0

dotnet add package ValueUnion --version 0.1.0
                    
NuGet\Install-Package ValueUnion -Version 0.1.0
                    
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="ValueUnion" Version="0.1.0">
  <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="ValueUnion" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="ValueUnion">
  <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 ValueUnion --version 0.1.0
                    
#r "nuget: ValueUnion, 0.1.0"
                    
#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 ValueUnion@0.1.0
                    
#: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=ValueUnion&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=ValueUnion&version=0.1.0
                    
Install as a Cake Tool

ValueUnion

C# 15 has finally added discrimminated unions! However, using a struct as a union case type results in boxing, with manual implementations requiring a lot of boilerplate.

This library autogenerates that boilerplate so it's easier to use unions without allocating or errors. Simply add the [Union<>] attribute and mark your union struct as partial.

Installation

dotnet add package ValueUnion
using ValueUnion;

CustomerId id = Guid.NewGuid();

string formatted = id switch
{
    int value => $"legacy:{value}",
    Guid value => $"modern:{value:N}"
};

[Union<int, Guid>]
partial struct CustomerId;

<details> <summary>Source generated code</summary>

[Union]
[StructLayout(LayoutKind.Auto)]
partial struct CustomerId : IUnion
{
    private readonly byte _tag;
    private readonly Impl _inner;
    
    [StructLayout(LayoutKind.Explicit)]
    private struct Impl
    {
        [FieldOffset(0)]
        internal int Value1;
        [FieldOffset(0)]
        internal Guid Value2;
    }
    
    public CustomerId(int? value)
    {
        if (value is int v)
        {
            _inner.Value1 = v;
            _tag = 1;
        }
    }

    public CustomerId(Guid? value)
    {
        if (value is Guid v)
        {
            _inner.Value2 = v;
            _tag = 2;
        }
    }
    
    public readonly bool HasValue => _tag != 0;
    
    public readonly object? Value => _tag switch
    {
        1 => _inner.Value1,
        2 => _inner.Value2,
        _ => null,
    };
    
    public readonly bool TryGetValue(out int value)
    {
        value = _inner.Value1;
        return _tag == 1;
    }

    public readonly bool TryGetValue(out Guid value)
    {
        value = _inner.Value2;
        return _tag == 2;
    }
}

</details>

Usage

Simply apply the [Union<>] attribute to a partial struct declaration. You can add up to 8 generic parameters for up to 8 possible types within a union. Types can be struct, primitives, classes, anything e.g. [Union<double, string, int, long>]. When all union cases are unmanaged, the union's cases are overlapped to save memory (this cannot be done if case types are managed due to the GC).

By default, a union also has a null case. Using the union example above, a default(CustomerId) has no active case and may warn if the null case is unhandled. You can get around this by setting a type to use in the default case with the Default property. If [Union<int, Guid>(Default = typeof(int))] was used instead, a default(CustomerId) would have case int with value 0.

CustomerId defaultId = default;

Debug.Assert(defaultId switch
{
    // case is int, as it is default case
    int => true,
    Guid => false,
});

[Union<int, Guid>(Default = typeof(int))]
partial struct CustomerId;
There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has 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.1.0 38 7/31/2026