Created
August 19, 2022 14:12
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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