Code/Primitives/Optional.cs
using System;
using System.Text.Json.Serialization;
namespace ExtendedBox.Primitives;
public readonly struct Optional<T>
{
public static readonly Optional<T> Empty = new(default!, false);
public T Value { get; }
public bool HasValue { get; }
[JsonConstructor]
private Optional(T value, bool hasValue)
{
Value = hasValue ? value : default!;
HasValue = hasValue;
}
public Optional(T value) : this(value, true)
{
}
public static Optional<T> FromNullable<V>(V? value) where V : struct, T
{
if(value is null)
return Empty;
return new(value.Value);
}
public static Optional<T> FromNullable(T? value)
{
if(value is null)
return Empty;
return new(value);
}
public T GetValueOrDefault(T defaultValue = default!) => HasValue ? Value : defaultValue;
public static explicit operator T(Optional<T> optional)
{
if(!optional.HasValue)
throw new InvalidCastException($"Given {optional.GetType().Name} has not value.");
return optional.Value;
}
public static implicit operator Optional<T>(T value) => new(value);
public static bool operator ==(Optional<T> a, Optional<T> b) => a.Equals(b);
public static bool operator !=(Optional<T> a, Optional<T> b) => !a.Equals(b);
public static bool operator ==(Optional<T> a, T b) => a.Equals(b);
public static bool operator !=(Optional<T> a, T b) => !a.Equals(b);
public static bool operator ==(T a, Optional<T> b) => b.Equals(a);
public static bool operator !=(T a, Optional<T> b) => !b.Equals(a);
public bool Equals(T other)
{
if(!HasValue)
return false;
return Equals(Value, other);
}
public bool Equals(Optional<T> other)
{
if(HasValue != other.HasValue)
return false;
if(!HasValue)
return true;
return Equals(other.Value);
}
public override bool Equals(object? other)
{
if(other is Optional<T> otherOptional)
return Equals(otherOptional);
if(!HasValue)
return false;
if(other is T || other is null)
return Equals(Value, other);
return false;
}
public override int GetHashCode() => HashCode.Combine(HasValue, Value);
public override string ToString() => HasValue ? (Value?.ToString() ?? "null") : "Empty";
}