serde-generate/runtime/csharp/Serde/Option.cs (60 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0
using System;
using System.Collections.Generic;
namespace Serde
{
    public readonly struct Option<T> : IEquatable<Option<T>> where T : IEquatable<T>
    {
        public static Option<T> None => default;
        public static Option<T> Some(T value)
        {
            if (value == null) throw new ArgumentNullException(nameof(value));
            return new Option<T>(value);
        }
        readonly bool isSome;
        readonly T value;
        Option(T val)
        {
            isSome = val != null;
            value = val;
        }
        public bool IsSome(out T value)
        {
            value = this.value;
            return isSome;
        }
        public override bool Equals(object obj) => obj is Option<T> option && Equals(option);
        public bool Equals(Option<T> other)
        {
            if (isSome != other.isSome) return false;
            if (!isSome) return true;
            return value.Equals(other.value);
        }
        public override int GetHashCode()
        {
            var hashCode = -934799437;
            hashCode = hashCode * -1521134295 + isSome.GetHashCode();
            hashCode = hashCode * -1521134295 + EqualityComparer<T>.Default.GetHashCode(value);
            return hashCode;
        }
        public static bool operator ==(Option<T> left, Option<T> right) => Equals(left, right);
        public static bool operator !=(Option<T> left, Option<T> right) => !Equals(left, right);
    }
    public static class OptionExtensions
    {
        public static U Match<T, U>(this Option<T> option, Func<T, U> onIsSome, Func<U> onIsNone)
        where T : IEquatable<T> where U : IEquatable<U> =>
            option.IsSome(out var value) ? onIsSome(value) : onIsNone();
        public static Option<U> Bind<T, U>(this Option<T> option, Func<T, Option<U>> binder)
        where T : IEquatable<T> where U : IEquatable<U> =>
            option.Match(onIsSome: binder, onIsNone: () => Option<U>.None);
        public static Option<U> Map<T, U>(this Option<T> option, Func<T, U> mapper)
        where T : IEquatable<T> where U : IEquatable<U> =>
            option.Bind(value => Option<U>.Some(mapper(value)));
        public static Option<T> Filter<T>(this Option<T> option, Predicate<T> predicate)
        where T : IEquatable<T> =>
            option.Bind(value => predicate(value) ? option : Option<T>.None);
        public static T DefaultValue<T>(this Option<T> option, T defaultValue)
        where T : IEquatable<T> =>
            option.Match(onIsSome: value => value, onIsNone: () => defaultValue);
    }
}