namespace System.Extensions { public static class Optional { public static Optional None() { return new Optional.None(); } public static Optional FromValue(T value) { if (value == null) { return new Optional.None(); } return new Optional.Some(value); } } public abstract class Optional : IEquatable> { public Optional(T value) { this.Value = value; } private T Value { get; } public T ExtractValue() { if (this is None) { return default; } else { return Value; } } public Optional Do(Action onSome, Action onNone) { if (onSome is null) throw new ArgumentNullException(nameof(onSome)); if (onNone is null) throw new ArgumentNullException(nameof(onNone)); if (this is Some) { onSome.Invoke(this.Value); } else { onNone.Invoke(); } return this; } public Optional DoAny(Action onSome = null, Action onNone = null) { if (this is Some) { onSome?.Invoke(this.Value); } else { onNone?.Invoke(); } return this; } public Optional Switch(Func onSome, Func onNone) { if (onSome is null) throw new ArgumentNullException($"{nameof(onSome)}"); if (onNone is null) throw new ArgumentNullException($"{nameof(onNone)}"); if (this is Some) { return Optional.FromValue(onSome.Invoke(this.Value)); } else { return Optional.FromValue(onNone.Invoke()); } } public Optional SwitchAny(Func onSome = null, Func onNone = null) { if (this is Some) { return onSome != null ? Optional.FromValue(onSome.Invoke(this.Value)) : Optional.FromValue(default(V)); } else { return onNone != null ? Optional.FromValue(onNone.Invoke()) : Optional.FromValue(default(V)); } } public override bool Equals(object obj) { if (obj is Optional) { return this.Equals(obj); } return base.Equals(obj); } public bool Equals(Optional other) { if (this is Some && other is Some) { return this.As().Equals(other.As()); } return false; } public static implicit operator Optional(T value) { return Optional.FromValue(value); } public override int GetHashCode() { return this.Value == null ? base.GetHashCode() : this.Value.GetHashCode(); } internal class Some : Optional, IEquatable { public Some(T value) : base(value) { } public override bool Equals(object obj) { if (obj is Some) { return this.Equals(obj.As()); } return base.Equals(obj); } public bool Equals(Some other) { if (other is None) { return false; } if (other is Some) { return this.Value.Equals(other.Value); } return false; } public static bool operator ==(Some left, Some right) { return left?.Equals(right) == true; } public static bool operator !=(Some left, Some right) { return left?.Equals(right) != true; } public override int GetHashCode() { return this.Value is object ? this.Value.GetHashCode() : this.As().GetHashCode(); } } internal class None : Optional { public None() : base(default) { } } } }