ValueUnion 0.1.0
dotnet add package ValueUnion --version 0.1.0
NuGet\Install-Package ValueUnion -Version 0.1.0
<PackageReference Include="ValueUnion" Version="0.1.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="ValueUnion" Version="0.1.0" />
<PackageReference Include="ValueUnion"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add ValueUnion --version 0.1.0
#r "nuget: ValueUnion, 0.1.0"
#:package ValueUnion@0.1.0
#addin nuget:?package=ValueUnion&version=0.1.0
#tool nuget:?package=ValueUnion&version=0.1.0
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;
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 |