Skip to content

Instantly share code, notes, and snippets.

@chtenb
Created August 19, 2022 14:12
Show Gist options
  • Save chtenb/c9799bb5844efa99637ecfa4f249f95c to your computer and use it in GitHub Desktop.
Save chtenb/c9799bb5844efa99637ecfa4f249f95c to your computer and use it in GitHub Desktop.
Recipe for nullable wrapper that can handle both value types and reference types, unlike System.Nullable
using System;
namespace Utils;
/// <summary>
/// "Nullable" wrapper. Can wrap both value types and reference types.
/// </summary>
/// <typeparam name="T"></typeparam>
public readonly struct Option<T> : IEquatable<Option<T>>
{
public bool HasValue { get; } = false;
private readonly T _value = default!;
public Option() { }
public Option(T value)
{
_value = value;
HasValue = true;
}
public T Value
{
get {
if (!HasValue)
throw new InvalidOperationException("Cannot access value of None");
return _value;
}
}
public bool Equals(Option<T> other)
{
return (!HasValue && !other.HasValue)
|| (HasValue && other.HasValue && _value!.Equals(other._value));
}
public override bool Equals(object obj)
{
if (obj is Option<T> other)
return Equals(other);
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(HasValue, _value);
}
public static bool operator ==(Option<T> left, Option<T> right)
{
return left.Equals(right);
}
public static bool operator !=(Option<T> left, Option<T> right)
{
return !(left == right);
}
public Option<TResult> Map<TResult>(Func<T, TResult> map)
{
if (HasValue)
return Option.Some(map(_value));
return Option.None<TResult>();
}
public T Or(T otherwise)
{
if (HasValue)
return _value;
return otherwise;
}
public Option<T> Or(Option<T> otherwise)
{
if (HasValue)
return this;
return otherwise;
}
}
public static class Option
{
public static Option<T> None<T>()
=> default;
public static Option<T> Some<T>(T value)
=> new(value);
public static Option<T> FromNullable<T>(T? value)
where T : struct
{
if (!value.HasValue)
return default;
return new(value.Value);
}
public static Option<T> FromObject<T>(T? value)
{
if (value is null)
return default;
return new(value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment