diff --git a/src/Lumina/Extensions/LinqExtensions.cs b/src/Lumina/Extensions/LinqExtensions.cs new file mode 100644 index 00000000..087e344c --- /dev/null +++ b/src/Lumina/Extensions/LinqExtensions.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; + +namespace Lumina.Extensions; + +/// +/// Utility class for additional struct-based enumerable operations. +/// +/// Most useful for easily looping over and types. +public static class LinqExtensions +{ + /// Returns the first element of a sequence, or if the sequence contains no elements. + /// The type of the elements of . + /// The to return the first element of. + /// if is empty; otherwise, the first element in . + /// is . + public static T? FirstOrNull( this IEnumerable source ) where T : struct + { + ArgumentNullException.ThrowIfNull( source ); + + var e = source.GetEnumerator(); + if( e.MoveNext() ) + { + return e.Current; + } + + return null; + } + + /// Returns the first element of the sequence that satisfies a condition or if no such element is found. + /// The type of the elements of . + /// An to return an element from. + /// A function to test each element for a condition. + /// if is empty or if no element passes the test specified by ; otherwise, the first element in that passes the test specified by . + /// or is . + public static T? FirstOrNull( this IEnumerable source, Predicate predicate ) where T : struct + { + ArgumentNullException.ThrowIfNull( source ); + ArgumentNullException.ThrowIfNull( predicate ); + + foreach( var element in source ) + { + if( predicate( element ) ) + { + return element; + } + } + + return null; + } + + // https://github.com/dotnet/runtime/blob/96ae7cfefb0ae0ad621edc56bf690da174d9c7ab/src/libraries/System.Linq/src/System/Linq/First.cs#L65 + /// Attempts to get the first element of a sequence. + /// The type of the elements of . + /// The to return the first element of. + /// if is empty; otherwise, the first element in . + /// Whether or not the first element was found. + /// is . + public static bool TryGetFirst( this IEnumerable source, out T result ) where T : struct + { + ArgumentNullException.ThrowIfNull( source ); + + using( var e = source.GetEnumerator() ) + { + if( e.MoveNext() ) + { + result = e.Current; + return true; + } + } + + result = default; + return false; + } + + // https://github.com/dotnet/runtime/blob/96ae7cfefb0ae0ad621edc56bf690da174d9c7ab/src/libraries/System.Linq/src/System/Linq/First.cs#L105 + /// Attempts to get the first element of the sequence that satisfies a condition. + /// The type of the elements of . + /// An to return an element from. + /// A function to test each element for a condition. + /// if is empty or if no element passes the test specified by ; otherwise, the first element in that passes the test specified by . + /// Whether or not an element was found that passes . + /// or is . + public static bool TryGetFirst( this IEnumerable source, Predicate predicate, out T result ) where T : struct + { + ArgumentNullException.ThrowIfNull( source ); + ArgumentNullException.ThrowIfNull( predicate ); + + foreach( var element in source ) + { + if( predicate( element ) ) + { + result = element; + return true; + } + } + + result = default; + return false; + } +} \ No newline at end of file