Skip to content

Commit

Permalink
Improving nullability handling in maybe
Browse files Browse the repository at this point in the history
  • Loading branch information
vkhorikov committed Sep 28, 2024
1 parent c3ade4b commit e2243c1
Showing 1 changed file with 36 additions and 19 deletions.
55 changes: 36 additions & 19 deletions CSharpFunctionalExtensions/Maybe/Maybe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ namespace CSharpFunctionalExtensions
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct Maybe<T> : IEquatable<Maybe<T>>, IEquatable<object>, IMaybe<T>
{

private readonly bool _isValueSet;

private readonly T? _value;
Expand All @@ -26,7 +25,7 @@ namespace CSharpFunctionalExtensions
/// <exception cref="InvalidOperationException">Maybe has no value.</exception>
public T GetValueOrThrow(string? errorMessage = null)
{
if (_value is null || HasNoValue)
if (HasNoValue)
throw new InvalidOperationException(errorMessage ?? Configuration.NoValueException);

return _value;
Expand All @@ -38,20 +37,28 @@ public T GetValueOrThrow(string? errorMessage = null)
/// <exception cref="Exception">Maybe has no value.</exception>
public T GetValueOrThrow(Exception exception)
{
if (_value is null || HasNoValue)
if (HasNoValue)
throw exception;

return _value;
}

public T? GetValueOrDefault(T? defaultValue = default)
public T GetValueOrDefault(T defaultValue)
{
if (HasNoValue)
return defaultValue;

return _value;
}

public T? GetValueOrDefault()
{
if (HasNoValue)
return default;

return _value;
}

/// <summary>
/// Indicates whether the inner value is present and returns the value if it is.
/// </summary>
Expand All @@ -76,7 +83,14 @@ public bool TryGetValue(

public static Maybe<T> None => new Maybe<T>();

#if NET5_0_OR_GREATER
[MemberNotNullWhen(true, "_value")]
#endif
public bool HasValue => _isValueSet;

#if NET5_0_OR_GREATER
[MemberNotNullWhen(false, "_value")]
#endif
public bool HasNoValue => !HasValue;

private Maybe(T? value)
Expand All @@ -102,7 +116,7 @@ public static implicit operator Maybe<T>(T? value)
return Maybe.From(value);
}

public static implicit operator Maybe<T>(Maybe value) => None;
public static implicit operator Maybe<T>(Maybe _) => None;

public static Maybe<T> From(T? value)
{
Expand Down Expand Up @@ -130,13 +144,13 @@ public static async Task<Maybe<T>> From(Func<Task<T?>> valueTaskFunc)
return new Maybe<T>(value);
}

public static bool operator ==(Maybe<T> maybe, T value)
public static bool operator ==(Maybe<T> maybe, T? value)
{
if (value is Maybe<T>)
return maybe.Equals(value);
if (value is Maybe<T> maybeValue)
return maybe.Equals(maybeValue);

if (maybe._value is null || maybe.HasNoValue)
return value is null;
if (maybe.HasNoValue)
return value == null;

return maybe._value.Equals(value);
}
Expand Down Expand Up @@ -168,12 +182,15 @@ public static async Task<Maybe<T>> From(Func<Task<T?>> valueTaskFunc)

public override bool Equals(object? obj)
{
if (obj is null)
if (obj == null)
return false;
if (obj is Maybe<T> other)
return Equals(other);
if (obj is T value)
return Equals(value);

if (obj is Maybe<T> otherMaybe)
return Equals(otherMaybe);

if (obj is T otherValue)
return Equals(otherValue);

return false;
}

Expand All @@ -182,26 +199,26 @@ public bool Equals(Maybe<T> other)
if (HasNoValue && other.HasNoValue)
return true;

if (_value is null || HasNoValue || other._value is null || other.HasNoValue)
if (HasNoValue || other.HasNoValue)
return false;

return EqualityComparer<T>.Default.Equals(_value, other._value);
}

public override int GetHashCode()
{
if (_value is null || HasNoValue)
if (HasNoValue)
return 0;

return _value.GetHashCode();
}

public override string ToString()
{
if (_value is null || HasNoValue)
if (HasNoValue)
return "No value";

return _value.ToString()!;
return _value.ToString() ?? _value.GetType().Name;
}
}

Expand Down

0 comments on commit e2243c1

Please sign in to comment.