OneOf Documentation

repository·master·Indexed 26 days ago

https://github.com/mcintyre321/oneof

OneOf provides F#-style discriminated unions for C#, allowing developers to represent a value that can be one of several types. It enables exhaustive pattern matching via Match() and Switch(), safer error handling without exceptions, and the creation of reusable types using OneOfBase and the OneOf.SourceGenerator.

Tokens
1.2K
Snippets
8
Records
9
Agent score
38%

What's inside OneOf

  1. Use OneOf as an 'Option' type

    master
    You can implement an Option pattern by declaring OneOf<Something, None>. The OneOf.Types namespace provides several useful types for this purpose, including Yes, No, Maybe, Unknown, True, False, All, Some, and None.
  2. Use OneOf as a method return value

    master

    OneOf is frequently used as a return type to represent multiple possible outcomes (e.g., a success value or specific error types) without using exceptions for control flow. This provides a strongly typed method signature that forces the consumer to handle all possible cases.

    public OneOf<User, InvalidName, NameTaken> CreateUser(string username)
    {
        if (!IsValid(username)) return new InvalidName();
        var user = _repo.FindByUsername(username);
        if(user != null) return new NameTaken();
        var user = new User(username);
        _repo.Save(user);
        return user;
    }
  3. Use OneOf.SourceGenerator for OneOfBase

    master

    You can automate the boilerplate for OneOfBase using the OneOf.SourceGenerator package. Apply the [GenerateOneOf] attribute to a partial class that inherits from OneOfBase.

    Install-Package OneOf.SourceGenerator
    [GenerateOneOf]
    public partial class StringOrNumber : OneOfBase<string, int> { }
  4. Extract specific types using TryPickX methods

    master

    The .TryPickX methods (where X is the index of the generic type, e.g., TryPickT0, TryPickT1) allow you to attempt to extract a specific type. If the OneOf contains that type, the method returns true, sets the out value, and provides the remainder (a OneOf containing all other types).

    // For OneOf<Thing, NotFound, Error>
    if (thingOrNotFoundOrError.TryPickT1(out NotFound notFound, out var thingOrError))
      return StatusCode(404);
  5. Match values using Match()

    master

    The Match method allows you to extract the value from a OneOf instance by providing a handler function for every generic type argument. The number of handlers must match the number of generic arguments. This ensures exhaustive handling of all possible types.

    OneOf<string, ColorName, Color> backgroundColor = ...;
    Color c = backgroundColor.Match(
        str => CssHelper.GetColorFromString(str),
        name => new Color(name),
        col => col
    );
  6. Create reusable OneOf types with OneOfBase

    master

    Inherit from OneOfBase<T0, ... Tn> to create a named, reusable type. This allows you to add custom members and define implicit conversions to make the type easier to work with.

    public class StringOrNumber : OneOfBase<string, int>
    {
        StringOrNumber(OneOf<string, int> _) : base(_) { }
    
        public static implicit operator StringOrNumber(string _) => new StringOrNumber(_);
        public static implicit operator StringOrNumber(int _) => new StringOrNumber(_);
    
        public (bool isNumber, int number) TryGetNumber() =>
            Match(
                s => (int.TryParse(s, out var n), n),
                i => (true, i)
            );
    }
  7. Execute side effects using Switch()

    master

    Use the .Switch method when you want to perform an action for each possible type without returning a value.

    OneOf<string, DateTime> dateValue = ...;
    dateValue.Switch(
        str => AddEntry(DateTime.Parse(str), foo),
        int => AddEntry(int, foo)
    );