OneOf for C# (.NET): Complete Guide with Examples
The OneOf library brings discriminated unions to C#. It solves a fundamental problem: how to elegantly represent a value that can be one of several known types in a statically-typed language. Instead of using object with type casting, Nullable, or building class hierarchies, OneOf provides a type-safe and expressive way to work with such values.
Why use it? First, it boosts code readability: a method returning OneOf<Success, Error> immediately signals the operation can succeed or fail. Second, it eliminates a whole class of bugs caused by incorrect type casting. Third, OneOf forces you to handle all possible cases, making your code more robust and predictable. This is especially useful in scenarios where exceptions (for control flow) or returning null are traditionally used.
OneOf fits perfectly into functional programming styles in C#. It lets you model your domain more precisely: instead of a Result class with flags and nested objects, you can use OneOf<T0, T1, T2> where each type represents one possible state. This makes your code more declarative and reduces the number of if-else constructs.
Installation
Install the library via NuGet Package Manager. Run one of the following commands depending on your tool:
# Package Manager Console
Install-Package OneOf
# .NET CLI
dotnet add package OneOf
# PackageReference (manually in .csproj)
<PackageReference Include="OneOf" Version="3.0.271" />
After installation, add using OneOf; at the top of your file.
Quick Start
Let's look at a simple example: a function that divides one number by another. Instead of throwing an exception on division by zero, we return OneOf<double, string> — either the result or an error message.
using OneOf;
public OneOf<double, string> SafeDivide(double a, double b)
{
if (b == 0)
return "Error: division by zero";
return a / b;
}
// Usage
var result = SafeDivide(10, 2);
result.Switch(
value => Console.WriteLine($"Result: {value}"),
error => Console.WriteLine($"Error: {error}")
);
// Output: Result: 5
Notice: no out parameters, no null checks, no exception handling. All you need is to call Switch or Match and provide handlers for each possible case.
Key Methods and Classes
The library provides several core mechanisms for working with discriminated unions. Let's dive into each one.
1. OneOf<T0, T1, ...>
Signature: public class OneOf<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> : IOneOf
What it does: The base class representing a value that can be one of the specified types. Supports up to 9 types. Implicit conversion from any of the specified types to OneOf.
OneOf<int, string, bool> value = 42; // implicit conversion
value = "hello";
value = true;
// Explicit creation
var explicitValue = new OneOf<int, string>(100);
2. Switch
Signature: public void Switch(Action<T0> f0, Action<T1> f1, ...)
What it does: Executes one of the provided actions depending on which value the OneOf holds. Returns nothing.