diff --git a/Bearded.Utilities/Algorithms/CoffmanGraham.cs b/Bearded.Utilities/Algorithms/CoffmanGraham.cs index 4bd76938..36304e99 100644 --- a/Bearded.Utilities/Algorithms/CoffmanGraham.cs +++ b/Bearded.Utilities/Algorithms/CoffmanGraham.cs @@ -177,7 +177,7 @@ private static ImmutableList> createLayers( public static ISolver SolverForReducedGraphs(int maxLayerSize) => new ReducedGraphSolver(maxLayerSize); - private struct DecreasingNumberSequence : IComparable, IComparable + private readonly struct DecreasingNumberSequence : IComparable, IComparable { private readonly ImmutableList numbers; @@ -186,7 +186,7 @@ private DecreasingNumberSequence(ImmutableList numbers) this.numbers = numbers; } - public int CompareTo(object obj) => CompareTo((DecreasingNumberSequence) obj); + public int CompareTo(object? obj) => CompareTo((DecreasingNumberSequence) (obj ?? throw new ArgumentNullException())); public int CompareTo(DecreasingNumberSequence other) { diff --git a/Bearded.Utilities/Collections/DeletableObjectList.cs b/Bearded.Utilities/Collections/DeletableObjectList.cs index 24f4a6b6..b521f6f9 100644 --- a/Bearded.Utilities/Collections/DeletableObjectList.cs +++ b/Bearded.Utilities/Collections/DeletableObjectList.cs @@ -16,7 +16,7 @@ public sealed class DeletableObjectList : IEnumerable { #region Fields and Properties - private readonly List list; + private readonly List list; private int enumerators; private int count; @@ -47,7 +47,7 @@ public DeletableObjectList() public DeletableObjectList(int capacity) { - list = new List(capacity); + list = new List(capacity); MaxEmptyFraction = 0.2f; } diff --git a/Bearded.Utilities/Collections/DeletableObjectListEnumerator.cs b/Bearded.Utilities/Collections/DeletableObjectListEnumerator.cs index 7cc6d918..31438d07 100644 --- a/Bearded.Utilities/Collections/DeletableObjectListEnumerator.cs +++ b/Bearded.Utilities/Collections/DeletableObjectListEnumerator.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace Bearded.Utilities.Collections; @@ -10,13 +11,19 @@ internal class DeletableObjectListEnumerator : IEnumerator where T : class, IDeletable { private readonly DeletableObjectList deletableObjectList; - private readonly List list; + private readonly List list; private int i; + private T? current; object System.Collections.IEnumerator.Current => Current; - public T Current { get; private set; } - public DeletableObjectListEnumerator(DeletableObjectList deletableObjectList, List list) + public T Current + { + get => current ?? throw new InvalidOperationException(); + private set => current = value; + } + + public DeletableObjectListEnumerator(DeletableObjectList deletableObjectList, List list) { this.deletableObjectList = deletableObjectList; this.list = list; diff --git a/Bearded.Utilities/Collections/MutableLinkedList.cs b/Bearded.Utilities/Collections/MutableLinkedList.cs index e7c80293..0e250aeb 100644 --- a/Bearded.Utilities/Collections/MutableLinkedList.cs +++ b/Bearded.Utilities/Collections/MutableLinkedList.cs @@ -13,12 +13,11 @@ public sealed class MutableLinkedList : IEnumerable { #region Fields and Properties - private readonly LinkedList> enumerators = - new LinkedList>(); + private readonly LinkedList> enumerators = new(); - public MutableLinkedListNode First { get; private set; } + public MutableLinkedListNode? First { get; private set; } - public MutableLinkedListNode Last { get; private set; } + public MutableLinkedListNode? Last { get; private set; } public int Count { get; private set; } @@ -56,7 +55,7 @@ public void Add(MutableLinkedListNode node) else { node.Prev = Last; - Last.Next = node; + Last!.Next = node; // We know that Last is not null here, as the list is not empty. } Last = node; @@ -147,7 +146,7 @@ public void InsertBefore(MutableLinkedListNode node, MutableLinkedListNode throw new ArgumentException("Object must already be in list before inserting."); if (beforeThis.List != this) throw new ArgumentException("The object to insert before must be in the same list."); - if (node != Last) + if (node != Last || node.Prev == null) throw new ArgumentException("Inserted object must be last object in list."); if (node == beforeThis) throw new ArgumentException("Cannot insert object before itself."); @@ -173,7 +172,7 @@ public IEnumerator GetEnumerator() { var e = new MutableLinkedListEnumerator(this); enumerators.AddFirst(e); - e.SetNode(enumerators.First); + e.SetNode(enumerators.First!); // We know First is not null as we just added e return e; } diff --git a/Bearded.Utilities/Collections/MutableLinkedListEnumerator.cs b/Bearded.Utilities/Collections/MutableLinkedListEnumerator.cs index a6bc1ee2..2f35f80d 100644 --- a/Bearded.Utilities/Collections/MutableLinkedListEnumerator.cs +++ b/Bearded.Utilities/Collections/MutableLinkedListEnumerator.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace Bearded.Utilities.Collections; @@ -21,19 +22,15 @@ public MutableLinkedListEnumerator(MutableLinkedList list) this.list = list; } - private LinkedListNode> node; + private LinkedListNode>? node; public void SetNode(LinkedListNode> node) { - if (this.node == null) - this.node = node; + this.node ??= node; } - public T Current - { - get { return current.Value; } - } + public T Current => current?.Value ?? throw new InvalidOperationException(); - private MutableLinkedListNode current; + private MutableLinkedListNode? current; public void OnObjectRemove(MutableLinkedListNode obj) { @@ -48,6 +45,8 @@ public void OnObjectRemove(MutableLinkedListNode obj) public void Dispose() { + if (node is null) return; + list.ForgetEnumerator(node); } @@ -69,12 +68,14 @@ public bool MoveNext() currentWasDeleted = false; return true; } - current = current.Next; - if (current == null) + + if (current?.Next == null) { done = true; return false; } + + current = current.Next; } return true; } @@ -87,9 +88,5 @@ public void Reset() currentWasDeleted = false; } - object System.Collections.IEnumerator.Current - { - get { return Current; } - } - + object System.Collections.IEnumerator.Current => Current; } diff --git a/Bearded.Utilities/Collections/MutableLinkedListNode.cs b/Bearded.Utilities/Collections/MutableLinkedListNode.cs index 91908e2e..fca382a8 100644 --- a/Bearded.Utilities/Collections/MutableLinkedListNode.cs +++ b/Bearded.Utilities/Collections/MutableLinkedListNode.cs @@ -1,4 +1,6 @@ +using System; + namespace Bearded.Utilities.Collections; /// @@ -25,23 +27,21 @@ public class MutableLinkedListNode #region Fields and Properties - private readonly T value; - /// /// The value stored in the node. /// - public T Value { get { return value; } } + public T? Value { get; } // Next, Prev and List are internally writeable to // simplify addition, removal and insertion code. // Do not mess with them! - internal MutableLinkedListNode Next { get; set; } - internal MutableLinkedListNode Prev { get; set; } + internal MutableLinkedListNode? Next { get; set; } + internal MutableLinkedListNode? Prev { get; set; } /// /// The list the node is part of. /// - public MutableLinkedList List { get; internal set; } + public MutableLinkedList? List { get; internal set; } internal bool ChangingList { get; private set; } @@ -51,12 +51,12 @@ public class MutableLinkedListNode internal MutableLinkedListNode(T value) { - this.value = value; + Value = value; } internal MutableLinkedListNode() { - value = this as T; + Value = this as T; } #endregion @@ -89,7 +89,7 @@ public void AddToListBefore(MutableLinkedList list, MutableLinkedListNode /// The node to add this before. public void InsertBefore(MutableLinkedListNode beforeThis) { - List.InsertBefore(this, beforeThis); + List?.InsertBefore(this, beforeThis); } /// @@ -97,7 +97,7 @@ public void InsertBefore(MutableLinkedListNode beforeThis) /// public void RemoveFromList() { - List.Remove(this); + List?.Remove(this); } #endregion diff --git a/Bearded.Utilities/Collections/PrefixTrie.cs b/Bearded.Utilities/Collections/PrefixTrie.cs index 13f8b381..c9a16406 100644 --- a/Bearded.Utilities/Collections/PrefixTrie.cs +++ b/Bearded.Utilities/Collections/PrefixTrie.cs @@ -13,7 +13,7 @@ private struct Node { private readonly Dictionary? values; - public string Key { get; } + public string? Key { get; } #region creating diff --git a/Bearded.Utilities/Collections/PriorityQueue.cs b/Bearded.Utilities/Collections/PriorityQueue.cs index 3c3fad72..4dbadaec 100644 --- a/Bearded.Utilities/Collections/PriorityQueue.cs +++ b/Bearded.Utilities/Collections/PriorityQueue.cs @@ -9,7 +9,9 @@ namespace Bearded.Utilities.Collections; /// /// /// -public sealed class PriorityQueue : StaticPriorityQueue where TPriority : IComparable +public sealed class PriorityQueue : StaticPriorityQueue + where TPriority : IComparable + where TValue : notnull { private readonly Dictionary valueDict = new Dictionary(); diff --git a/Bearded.Utilities/Core/Environment.cs b/Bearded.Utilities/Core/Environment.cs index eddec0c0..c7dff2ee 100644 --- a/Bearded.Utilities/Core/Environment.cs +++ b/Bearded.Utilities/Core/Environment.cs @@ -43,7 +43,7 @@ private static Platform detectPlatform() #endregion #region User settings directory - private static string userSettingsDirectory; + private static string? userSettingsDirectory; private static string buildUserSettingsDirectory() { @@ -67,8 +67,7 @@ private static string buildUserSettingsDirectory() return linuxConfigDir; var linuxHomeDir = System.Environment.GetEnvironmentVariable("HOME"); - // ReSharper disable once AssignNullToNotNullAttribute - return string.IsNullOrEmpty(linuxConfigDir) ? "." : Path.Combine(linuxHomeDir, ".config"); + return string.IsNullOrEmpty(linuxConfigDir) ? "." : Path.Combine(linuxHomeDir!, ".config"); default: throw new InvalidOperationException("Encountered unknown platform."); } @@ -80,7 +79,7 @@ private static string buildUserSettingsDirectory() /// For OSX: ~/Library/Application Support /// For Linux: ~/.config /// - public static string UserSettingsDirectory => userSettingsDirectory ?? (userSettingsDirectory = buildUserSettingsDirectory()); + public static string UserSettingsDirectory => userSettingsDirectory ??= buildUserSettingsDirectory(); /// /// Gets the default user setting directory for a given game name. diff --git a/Bearded.Utilities/Core/Maybe.cs b/Bearded.Utilities/Core/Maybe.cs index b22871da..cfc787f8 100644 --- a/Bearded.Utilities/Core/Maybe.cs +++ b/Bearded.Utilities/Core/Maybe.cs @@ -5,88 +5,131 @@ namespace Bearded.Utilities; -public readonly struct Maybe : IEquatable> +public abstract class Maybe : IEquatable> + where T : notnull { - private readonly bool hasValue; - private readonly T value; - private Maybe(T value) - { - hasValue = true; - this.value = value; - } + public static Maybe Nothing => new Nothing(); - public static Maybe Nothing => default; + internal static Maybe Just(T value) => new Just(value); - internal static Maybe Just(T value) => new Maybe(value); + public abstract T ValueOrDefault(T @default); - public T ValueOrDefault(T @default) => hasValue ? value : @default; + public abstract T ValueOrDefault(Func defaultProvider); - public T ValueOrDefault(Func defaultProvider) => hasValue ? value : defaultProvider(); + public abstract Result ValueOrFailure(TError error); - public Result ValueOrFailure(TError error) => - hasValue ? (Result) Result.Success(value) : Result.Failure(error); + public abstract Result ValueOrFailure(Func errorProvider); - public Result ValueOrFailure(Func errorProvider) => - hasValue ? (Result) Result.Success(value) : Result.Failure(errorProvider()); + public abstract Maybe Select(Func selector) where TOut : notnull; - public Maybe Select(Func selector) => - hasValue ? Maybe.Just(selector(value)) : Maybe.Nothing; + public abstract Maybe SelectMany(Func> selector) where TOut : notnull; - public Maybe SelectMany(Func> selector) => - hasValue ? selector(value) : Maybe.Nothing; + public abstract Maybe Where(Func predicate); - public Maybe Where(Func predicate) => hasValue && predicate(value) ? this : Nothing; + public abstract void Match(Action onValue); - public void Match(Action onValue) - { - if (hasValue) - onValue(value); - } + public abstract void Match(Action onValue, Action onNothing); - public void Match(Action onValue, Action onNothing) - { - if (hasValue) - { - onValue(value); - } - else - { - onNothing(); - } - } + public abstract TResult Match(Func onValue, Func onNothing); - public TResult Match(Func onValue, Func onNothing) - { - return hasValue ? onValue(value) : onNothing(); - } - - public bool Equals(Maybe other) => - hasValue == other.hasValue && EqualityComparer.Default.Equals(value, other.value); - - public override bool Equals(object? obj) => obj is Maybe other && Equals(other); - - public override int GetHashCode() => hasValue ? EqualityComparer.Default.GetHashCode(value) : 0; - - public override string ToString() => hasValue ? $"just {value}" : "nothing"; + public abstract bool Equals(Maybe? other); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Maybe(NothingMaybe _) => Nothing; + + } public static class Maybe { public static Maybe FromNullable(T? value) where T : class => - value == null ? Nothing : Maybe.Just(value); + value == null ? Nothing() : Maybe.Just(value); public static Maybe FromNullable(T? value) where T : struct => - value.HasValue ? Maybe.Just(value.Value) : Nothing; + value.HasValue ? Maybe.Just(value.Value) : Nothing(); - public static Maybe Just(T value) => Maybe.Just(value); + public static Maybe Just(T value) where T : notnull => Maybe.Just(value); - public static NothingMaybe Nothing => default; + public static Maybe Nothing() where T : notnull => Maybe.Nothing; } public readonly struct NothingMaybe { } + + +public class Nothing : Maybe + where T : notnull +{ + internal Nothing() + { + } + + + public override void Match(Action _) { } + + public override void Match(Action _, Action onNothing) => onNothing(); + public override TResult Match(Func _, Func onNothing) => onNothing(); + + + public override T ValueOrDefault(T @default) => @default; + + public override T ValueOrDefault(Func defaultProvider) => defaultProvider(); + + public override Result ValueOrFailure(TError error) => Result.Failure(error); + + public override Result ValueOrFailure(Func errorProvider) => Result.Failure(errorProvider()); + + public override Maybe Select(Func selector) => Maybe.Nothing; + + public override Maybe SelectMany(Func> selector) => Maybe.Nothing; + + public override Maybe Where(Func predicate) => Nothing; + + + public override int GetHashCode() => 0; + + public override string ToString() => "nothing"; + public override bool Equals(Maybe? other) => other is Nothing; + + public override bool Equals(object? obj) => obj is Maybe other && Equals(other); + +} + +public class Just : Maybe where T : notnull +{ + + protected readonly T value; + + internal Just(T value) + { + this.value = value; + } + public override void Match(Action onValue) => onValue(value!); + public override void Match(Action onValue, Action _) => onValue(value!); + public override TResult Match(Func onValue, Func _) => onValue(value!); + + public override T ValueOrDefault(T @default) => value!; + + public override T ValueOrDefault(Func defaultProvider) => value!; + + public override Result ValueOrFailure(TError _) => Result.Success(value!); + + public override Result ValueOrFailure(Func _) => Result.Success(value!); + + public override Maybe Select(Func selector) => Maybe.Just(selector(value!)); + + public override Maybe SelectMany(Func> selector) => selector(value!); + + public override Maybe Where(Func predicate) => predicate(value!) ? this : Nothing; + + + public override int GetHashCode() => EqualityComparer.Default.GetHashCode(value!); + + public override string ToString() => $"just {value}"; + + public bool Equals(Just other) => EqualityComparer.Default.Equals(value, other.value); + public override bool Equals(Maybe? other) => other is Just just && Equals(just); + public override bool Equals(object? obj) => obj is Maybe other && Equals(other); +} diff --git a/Bearded.Utilities/Core/Singleton.cs b/Bearded.Utilities/Core/Singleton.cs index 832ec751..ab1d6080 100644 --- a/Bearded.Utilities/Core/Singleton.cs +++ b/Bearded.Utilities/Core/Singleton.cs @@ -4,7 +4,7 @@ namespace Bearded.Utilities; public abstract class Singleton where TSelf : Singleton { - public static TSelf Instance { get; private set; } + public static TSelf? Instance { get; private set; } protected Singleton() { diff --git a/Bearded.Utilities/Core/StaticRandom.cs b/Bearded.Utilities/Core/StaticRandom.cs index 9c48643f..b4acdc11 100644 --- a/Bearded.Utilities/Core/StaticRandom.cs +++ b/Bearded.Utilities/Core/StaticRandom.cs @@ -12,13 +12,13 @@ public static class StaticRandom { #region Threadsafe random [ThreadStatic] - private static Random random; + private static Random? random; /// /// The thread safe instance of Random used by StaticRandom /// // is internal for use as default random in Linq.Extensions - internal static Random Random => random ?? (random = new Random()); + internal static Random Random => random ??= new Random(); /// /// Overrides the Random object for the calling thread by one with the given seed. diff --git a/Bearded.Utilities/IO/Logger.cs b/Bearded.Utilities/IO/Logger.cs index fe032de3..c33855c6 100644 --- a/Bearded.Utilities/IO/Logger.cs +++ b/Bearded.Utilities/IO/Logger.cs @@ -234,7 +234,7 @@ private static void ensureListCapacity(List list, int neededCapacity) /// /// If RaiseEvents is true, this event is raised every time an event is added to the log. /// - public event LogEvent Logged; + public event LogEvent? Logged; #region settings diff --git a/Bearded.Utilities/Input/Actions/DigitalAction.cs b/Bearded.Utilities/Input/Actions/DigitalAction.cs index c74618f6..8cbf231a 100644 --- a/Bearded.Utilities/Input/Actions/DigitalAction.cs +++ b/Bearded.Utilities/Input/Actions/DigitalAction.cs @@ -10,6 +10,6 @@ abstract class DigitalAction : IAction public float AnalogAmount => Active ? 1 : 0; public override bool Equals(object? obj) => Equals(obj as IAction); - public bool Equals(IAction other) => other is DigitalAction && this.IsSameAs(other); + public bool Equals(IAction? other) => other is DigitalAction && this.IsSameAs(other); public override int GetHashCode() => ToString()?.GetHashCode() ?? 0; } diff --git a/Bearded.Utilities/Input/Actions/DummyAction.cs b/Bearded.Utilities/Input/Actions/DummyAction.cs index 2f9142b8..6b67089c 100644 --- a/Bearded.Utilities/Input/Actions/DummyAction.cs +++ b/Bearded.Utilities/Input/Actions/DummyAction.cs @@ -18,6 +18,6 @@ public DummyAction(string name) public override string ToString() => name; public override bool Equals(object? obj) => Equals(obj as IAction); - public bool Equals(IAction other) => other is DummyAction && this.IsSameAs(other); + public bool Equals(IAction? other) => other is DummyAction && this.IsSameAs(other); public override int GetHashCode() => ToString().GetHashCode(); } diff --git a/Bearded.Utilities/Input/Actions/LambdaAction.cs b/Bearded.Utilities/Input/Actions/LambdaAction.cs index bd5c9fdf..0136134b 100644 --- a/Bearded.Utilities/Input/Actions/LambdaAction.cs +++ b/Bearded.Utilities/Input/Actions/LambdaAction.cs @@ -28,6 +28,6 @@ public LambdaAction(Func actionSelector) public bool IsAnalog => action.IsAnalog; public float AnalogAmount => action.AnalogAmount; - public bool Equals(IAction other) => this.IsSameAs(other); + public bool Equals(IAction? other) => this.IsSameAs(other); } } diff --git a/Bearded.Utilities/Input/InputAction.cs b/Bearded.Utilities/Input/InputAction.cs index a5d2d9e4..77811d8c 100644 --- a/Bearded.Utilities/Input/InputAction.cs +++ b/Bearded.Utilities/Input/InputAction.cs @@ -9,7 +9,7 @@ namespace Bearded.Utilities.Input; public static class InputAction { - public static bool IsSameAs(this IAction me, IAction other) + public static bool IsSameAs(this IAction me, IAction? other) => other != null && (ReferenceEquals(me, other) || me.ToString() == other.ToString()); public static IAction None { get; } = new DummyAction("unbound"); @@ -99,7 +99,7 @@ protected BinaryAction(IAction child1, IAction child2) public bool IsAnalog => Child1.IsAnalog || Child2.IsAnalog; public float AnalogAmount => FloatOp(Child1.AnalogAmount, Child2.AnalogAmount); - public bool Equals(IAction other) => this.IsSameAs(other); + public bool Equals(IAction? other) => this.IsSameAs(other); } private class OrAction : BinaryAction @@ -130,6 +130,6 @@ public AnyAction(IEnumerable actions) public bool IsAnalog => actions.Any(a => a.IsAnalog); public float AnalogAmount => actions.Max(a => a.AnalogAmount); - public bool Equals(IAction other) => this.IsSameAs(other); + public bool Equals(IAction? other) => this.IsSameAs(other); } } diff --git a/Bearded.Utilities/Input/InputManager.Actions.Keyboard.cs b/Bearded.Utilities/Input/InputManager.Actions.Keyboard.cs index e9d4fb92..70f42254 100644 --- a/Bearded.Utilities/Input/InputManager.Actions.Keyboard.cs +++ b/Bearded.Utilities/Input/InputManager.Actions.Keyboard.cs @@ -33,15 +33,15 @@ public IEnumerable All public IAction FromKey(Keys key) => new KeyboardAction(manager, key); - public IAction FromString(string value) + public IAction? FromString(string value) => TryParse(value, out var action) ? action : throw new FormatException($"Keyboard key '{value}' invalid."); - public bool TryParse(string value, out IAction action) + public bool TryParse(string value, out IAction? action) => TryParseLowerTrimmedString(value.ToLowerInvariant().Trim(), out action); - internal bool TryParseLowerTrimmedString(string value, out IAction action) + internal bool TryParseLowerTrimmedString(string value, out IAction? action) { action = null; diff --git a/Bearded.Utilities/Input/InputManager.Actions.cs b/Bearded.Utilities/Input/InputManager.Actions.cs index 44aca2f0..231a40a7 100644 --- a/Bearded.Utilities/Input/InputManager.Actions.cs +++ b/Bearded.Utilities/Input/InputManager.Actions.cs @@ -15,10 +15,10 @@ public ActionConstructor(InputManager inputManager) public IAction None => InputAction.None; - public IAction FromString(string value) + public IAction? FromString(string value) => fromLowerCaseTrimmedString(value.ToLowerInvariant().Trim()); - private IAction fromLowerCaseTrimmedString(string value) + private IAction? fromLowerCaseTrimmedString(string value) { switch (value) { diff --git a/Bearded.Utilities/Linq/Extensions.cs b/Bearded.Utilities/Linq/Extensions.cs index e00cd088..1aeef174 100644 --- a/Bearded.Utilities/Linq/Extensions.cs +++ b/Bearded.Utilities/Linq/Extensions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Bearded.Utilities.Linq; @@ -152,8 +153,13 @@ public static int AddSorted(this List list, T item, IComparer comparer) /// The resulting transformed value. /// The function transforming the value into the result. /// True if the value was found, false otherwise. - public static bool TryGetTransformedValue(this Dictionary dictionary, TKey key, out TNewValue result, - Func transform) + public static bool TryGetTransformedValue( + this Dictionary dictionary, + TKey key, + [MaybeNullWhen(false)] out TNewValue result, + Func transform + ) + where TKey : notnull { if (dictionary.TryGetValue(key, out var value)) { @@ -171,7 +177,9 @@ public static bool TryGetTransformedValue(this Dictiona /// The dictionary. /// KeyValuePairs to add. public static void AddRange(this IDictionary dictionary, - IEnumerable> other) + IEnumerable> other + ) + where TKey : notnull { foreach (var pair in other) { @@ -181,6 +189,7 @@ public static void AddRange(this IDictionary diction public static TValue? ValueOrNull(this Dictionary dict, TKey key) where TValue : class + where TKey : notnull { dict.TryGetValue(key, out var value); return value; @@ -188,6 +197,7 @@ public static void AddRange(this IDictionary diction public static TValue ValueOrDefault(this Dictionary dict, TKey key) where TValue : struct + where TKey : notnull { dict.TryGetValue(key, out var value); return value; diff --git a/Bearded.Utilities/Monads/Result.cs b/Bearded.Utilities/Monads/Result.cs index 99151153..2f9e3a68 100644 --- a/Bearded.Utilities/Monads/Result.cs +++ b/Bearded.Utilities/Monads/Result.cs @@ -6,15 +6,22 @@ namespace Bearded.Utilities.Monads; public readonly struct Result : IEquatable> + where TResult : notnull { private readonly bool isSuccess; - [AllowNull] - private readonly TResult result; - [AllowNull] - private readonly TError error; + private readonly TResult? result; + private readonly TError? error; - private Result(bool isSuccess, [AllowNull] TResult result, [AllowNull] TError error) + private Result(bool isSuccess, TResult? result, TError? error) { + switch (isSuccess) + { + case true when result is null: + throw new ArgumentNullException(nameof(result), "Result cannot be null when isSuccess is true"); + case false when error is null: + throw new ArgumentNullException(nameof(error), "Error cannot be null when isSuccess is false"); + } + this.isSuccess = isSuccess; this.result = result; this.error = error; @@ -26,26 +33,26 @@ public static Result Success(TResult result) => public static Result Failure(TError error) => new Result(false, default, error); - public TResult ResultOrDefault(TResult @default) => isSuccess ? result : @default; + public TResult ResultOrDefault(TResult @default) => isSuccess ? result! : @default; - public TResult ResultOrDefault(Func defaultProvider) => isSuccess ? result : defaultProvider(); + public TResult ResultOrDefault(Func defaultProvider) => isSuccess ? result! : defaultProvider(); public TResult ResultOrThrow(Func exceptionProvider) => - isSuccess ? result : throw exceptionProvider(error); + isSuccess ? result! : throw exceptionProvider(error!); - public Maybe AsMaybe() => isSuccess ? Maybe.Just(result) : Maybe.Nothing; + public Maybe AsMaybe() => isSuccess ? Maybe.Just(result!) : Maybe.Nothing(); - public Result Select(Func selector) => - isSuccess ? (Result) Result.Success(selector(result)) : Result.Failure(error); + public Result Select(Func selector) where TOut : notnull => + isSuccess ? (Result) Result.Success(selector(result!)) : Result.Failure(error!); - public Result SelectMany(Func> selector) => - isSuccess ? selector(result) : Result.Failure(error); + public Result SelectMany(Func> selector) where TOut : notnull => + isSuccess ? selector(result!) : Result.Failure(error!); public void Match(Action onSuccess) { if (isSuccess) { - onSuccess(result); + onSuccess(result!); } } @@ -53,16 +60,16 @@ public void Match(Action onSuccess, FailureResultCallback onFai { if (isSuccess) { - onSuccess(result); + onSuccess(result!); } else { - onFailure(error); + onFailure(error!); } } public TOut Match(Func onSuccess, FailureResultTransformation onFailure) => - isSuccess ? onSuccess(result) : onFailure(error); + isSuccess ? onSuccess(result!) : onFailure(error!); public bool Equals(Result other) => isSuccess == other.isSuccess @@ -90,20 +97,21 @@ public bool Equals(Result other) => public static class Result { - public static Result Success(TResult result) => + public static Result Success(TResult result) where TResult : notnull => Result.Success(result); public static Success Success(T result) => new Success(result); - public static Result Failure(TError error) => + public static Result Failure(TError error) where TResult : notnull => Result.Failure(error); public static Failure Failure(T error) => new Failure(error); public static TResult ResultOrThrow(this Result result) where TError : Exception + where TResult : notnull { - return result.ResultOrThrow(e => e); + return result.ResultOrThrow(e => e!); } } @@ -129,4 +137,4 @@ internal Failure(T error) public delegate void FailureResultCallback([MaybeNull] TError error); -public delegate TOut FailureResultTransformation([MaybeNull] TError error); \ No newline at end of file +public delegate TOut FailureResultTransformation([MaybeNull] TError error);