public static int?Max(this IEnumerable <int?> source) { if (source == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } int?value = null; using (IEnumerator <int?> e = source.GetEnumerator()) { do { if (!e.MoveNext()) { return(value); } value = e.Current; }while (!value.HasValue); int valueVal = value.GetValueOrDefault(); if (valueVal >= 0) { // We can fast-path this case where we know HasValue will // never affect the outcome, without constantly checking // if we're in such a state. Similar fast-paths could // be done for other cases, but as all-positive // or mostly-positive integer values are quite common in real-world // uses, it's only been done in this direction for int? and long?. while (e.MoveNext()) { int?cur = e.Current; int x = cur.GetValueOrDefault(); if (x > valueVal) { valueVal = x; value = cur; } } } else { while (e.MoveNext()) { int?cur = e.Current; int x = cur.GetValueOrDefault(); // Do not replace & with &&. The branch prediction cost outweighs the extra operation // unless nulls either never happen or always happen. if (cur.HasValue & x > valueVal) { valueVal = x; value = cur; } } } } return(value); }
public static TSource?Min <TSource>(this IEnumerable <TSource> source, IComparer <TSource>?comparer) { if (source == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } comparer ??= Comparer <TSource> .Default; TSource?value = default; using (IEnumerator <TSource> e = source.GetEnumerator()) { if (value == null) { do { if (!e.MoveNext()) { return(value); } value = e.Current; }while (value == null); while (e.MoveNext()) { TSource next = e.Current; if (next != null && comparer.Compare(next, value) < 0) { value = next; } } } else { if (!e.MoveNext()) { ThrowHelper.ThrowNoElementsException(); } value = e.Current; if (comparer == Comparer <TSource> .Default) { while (e.MoveNext()) { TSource next = e.Current; if (Comparer <TSource> .Default.Compare(next, value) < 0) { value = next; } } } else { while (e.MoveNext()) { TSource next = e.Current; if (comparer.Compare(next, value) < 0) { value = next; } } } } } return(value); }
public static TSource?MaxBy <TSource, TKey>(this IEnumerable <TSource> source, Func <TSource, TKey> keySelector, IComparer <TKey>?comparer) { if (source == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } if (keySelector == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector); } comparer ??= Comparer <TKey> .Default; TKey? key = default; TSource?value = default; using (IEnumerator <TSource> e = source.GetEnumerator()) { if (key == null) { do { if (!e.MoveNext()) { return(value); } value = e.Current; key = keySelector(value); }while (key == null); while (e.MoveNext()) { TSource nextValue = e.Current; TKey nextKey = keySelector(nextValue); if (nextKey != null && comparer.Compare(nextKey, key) > 0) { key = nextKey; value = nextValue; } } } else { if (!e.MoveNext()) { ThrowHelper.ThrowNoElementsException(); } value = e.Current; key = keySelector(value); if (comparer == Comparer <TSource> .Default) { while (e.MoveNext()) { TSource nextValue = e.Current; TKey nextKey = keySelector(nextValue); if (Comparer <TKey> .Default.Compare(nextKey, key) > 0) { key = nextKey; value = nextValue; } } } else { while (e.MoveNext()) { TSource nextValue = e.Current; TKey nextKey = keySelector(nextValue); if (comparer.Compare(nextKey, key) > 0) { key = nextKey; value = nextValue; } } } } } return(value); }
public static TResult?Max <TSource, TResult>(this IEnumerable <TSource> source, Func <TSource, TResult> selector) { if (source == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } if (selector == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.selector); } Comparer <TResult> comparer = Comparer <TResult> .Default; TResult? value = default; if (value == null) { using (IEnumerator <TSource> e = source.GetEnumerator()) { do { if (!e.MoveNext()) { return(value); } value = selector(e.Current); }while (value == null); while (e.MoveNext()) { TResult x = selector(e.Current); if (x != null && comparer.Compare(x, value) > 0) { value = x; } } } } else { using (IEnumerator <TSource> e = source.GetEnumerator()) { if (!e.MoveNext()) { ThrowHelper.ThrowNoElementsException(); } value = selector(e.Current); while (e.MoveNext()) { TResult x = selector(e.Current); if (comparer.Compare(x, value) > 0) { value = x; } } } } return(value); }
public static bool SequenceEqual <TSource>(this IEnumerable <TSource> first, IEnumerable <TSource> second, IEqualityComparer <TSource>?comparer) { if (first == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first); } if (second == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second); } if (comparer == null) { // It's relatively common to see code (especially in tests and testing frameworks) that ends up // using Enumerable.SequenceEqual to compare two byte arrays. Using ReadOnlySpan.SequenceEqual // is significantly faster than accessing each byte via the array's IList<byte> interface // implementation. So, we special-case byte[] here. It would be nice to be able to delegate // to ReadOnlySpan.SequenceEqual for all TSource[] arrays where TSource is a value type and // implements IEquatable<TSource>, but there's no good way without reflection to convince // the C# compiler to let us delegate, as ReadOnlySpan.SequenceEqual requires an IEquatable<T> // constraint on its type parameter, and Enumerable.SequenceEqual lacks one on its type parameter. if (typeof(TSource) == typeof(byte) && first is byte[] firstArr && second is byte[] secondArr) { return(((ReadOnlySpan <byte>)firstArr).SequenceEqual(secondArr)); } comparer = EqualityComparer <TSource> .Default; } if (first is ICollection <TSource> firstCol && second is ICollection <TSource> secondCol) { if (firstCol.Count != secondCol.Count) { return(false); } if (firstCol is IList <TSource> firstList && secondCol is IList <TSource> secondList) { int count = firstCol.Count; for (int i = 0; i < count; i++) { if (!comparer.Equals(firstList[i], secondList[i])) { return(false); } } return(true); } } using (IEnumerator <TSource> e1 = first.GetEnumerator()) using (IEnumerator <TSource> e2 = second.GetEnumerator()) { while (e1.MoveNext()) { if (!(e2.MoveNext() && comparer.Equals(e1.Current, e2.Current))) { return(false); } } return(!e2.MoveNext()); } }