using System; using System.Collections.Generic; public struct Optional { public static readonly Optional NoValue = new Optional(); readonly bool hasValue; readonly T value; public Optional(T value) { this.value = value; hasValue = true; } public T Value => hasValue ? value : throw new InvalidOperationException("No value"); public bool HasValue => hasValue; public T GetValueOrDefault() => value; public T GetValueOrDefault(T defaultValue) => hasValue ? value : defaultValue; public TResult Match(Func onValue, Func onNoValue) { return hasValue ? onValue(value) : onNoValue(); } public Optional SelectMany(Func> bind) { return hasValue ? bind(value) : Optional.NoValue; } public Optional Select(Func map) { return hasValue ? new Optional(map(value)) : Optional.NoValue; } public static Optional Combine(Optional first, Optional second, Func combiner) { if (first.HasValue && second.HasValue) { return new Optional(combiner(first.Value, second.Value)); } return Optional.NoValue; } public static Optional Some(T value) => new Optional(value); public static Optional None() => NoValue; public override bool Equals(object obj) => obj is Optional other && Equals(other); public bool Equals(Optional other) => !hasValue ? !other.hasValue : EqualityComparer.Default.Equals(value, other.value); public override int GetHashCode() => (hasValue.GetHashCode() * 397) ^ EqualityComparer.Default.GetHashCode(value); public override string ToString() => hasValue ? $"Some({value})" : "None"; public static implicit operator Optional(T value) => new Optional(value); public static implicit operator bool(Optional value) => value.hasValue; public static explicit operator T(Optional value) => value.Value; }