mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-22 19:58:49 +00:00
Add MinMax and MinMaxBy (resolves #72)
This commit is contained in:
parent
e00a673a04
commit
3b85419da3
@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- X10D: Added `IEnumerable<T>.FirstWhereNotOrDefault(Func<T, bool>)`.
|
||||
- X10D: Added `IEnumerable<T>.LastWhereNot(Func<T, bool>)`.
|
||||
- X10D: Added `IEnumerable<T>.LastWhereNotOrDefault(Func<T, bool>)`.
|
||||
- X10D: Added `IEnumerable<T>.MinMax()` and `IEnumerable<T>.MinMaxBy()`.
|
||||
- X10D: Added `IEnumerable<T>.WhereNot(Func<T, bool>)`.
|
||||
- X10D: Added `IEnumerable<T>.WhereNotNull()`.
|
||||
- X10D: Added `IList<T>.RemoveRange(Range)`.
|
||||
|
159
X10D.Tests/src/Linq/EnumerableTests.cs
Normal file
159
X10D.Tests/src/Linq/EnumerableTests.cs
Normal file
@ -0,0 +1,159 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using X10D.Linq;
|
||||
|
||||
namespace X10D.Tests.Linq;
|
||||
|
||||
[TestClass]
|
||||
public class EnumerableTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void MinMax_ShouldReturnCorrectValues_UsingDefaultComparer()
|
||||
{
|
||||
IEnumerable<int> source = Enumerable.Range(1, 10);
|
||||
(int minimum, int maximum) = source.MinMax();
|
||||
Assert.AreEqual(1, minimum);
|
||||
Assert.AreEqual(10, maximum);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMax_ShouldReturnCorrectSelectedValues_UsingDefaultComparer()
|
||||
{
|
||||
IEnumerable<Person> source = Enumerable.Range(1, 10).Select(i => new Person {Age = i});
|
||||
(int minimum, int maximum) = source.MinMax(p => p.Age);
|
||||
Assert.AreEqual(1, minimum);
|
||||
Assert.AreEqual(10, maximum);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMax_ShouldReturnOppositeSelectedValues_UsingInverseComparer()
|
||||
{
|
||||
IEnumerable<Person> source = Enumerable.Range(1, 10).Select(i => new Person {Age = i});
|
||||
(int minimum, int maximum) = source.MinMax(p => p.Age, new InverseComparer<int>());
|
||||
Assert.AreEqual(10, minimum);
|
||||
Assert.AreEqual(1, maximum);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMax_ShouldReturnOppositeValues_UsingInverseComparer()
|
||||
{
|
||||
IEnumerable<int> source = Enumerable.Range(1, 10);
|
||||
(int minimum, int maximum) = source.MinMax(new InverseComparer<int>());
|
||||
Assert.AreEqual(10, minimum);
|
||||
Assert.AreEqual(1, maximum);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMax_ShouldThrowArgumentNullException_GivenNullSelector()
|
||||
{
|
||||
IEnumerable<int> source = Enumerable.Range(1, 10);
|
||||
Assert.ThrowsException<ArgumentNullException>(() => source.MinMax((Func<int, int>?)null!));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMax_ShouldThrowArgumentNullException_GivenNullSource()
|
||||
{
|
||||
IEnumerable<int>? source = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => source!.MinMax());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMax_ShouldThrowInvalidOperationException_GivenEmptySource()
|
||||
{
|
||||
IEnumerable<int> source = ArraySegment<int>.Empty;
|
||||
Assert.ThrowsException<InvalidOperationException>(() => source.MinMax());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMaxBy_ShouldReturnCorrectSelectedValues_UsingDefaultComparer()
|
||||
{
|
||||
IEnumerable<Person> source = Enumerable.Range(1, 10).Select(i => new Person {Age = i});
|
||||
(Person minimum, Person maximum) = source.MinMaxBy(p => p.Age);
|
||||
Assert.AreEqual(1, minimum.Age);
|
||||
Assert.AreEqual(10, maximum.Age);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMaxBy_ShouldReturnOppositeSelectedValues_UsingInverseComparer()
|
||||
{
|
||||
IEnumerable<Person> source = Enumerable.Range(1, 10).Select(i => new Person {Age = i});
|
||||
(Person minimum, Person maximum) = source.MinMaxBy(p => p.Age, new InverseComparer<int>());
|
||||
Assert.AreEqual(10, minimum.Age);
|
||||
Assert.AreEqual(1, maximum.Age);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMaxBy_ShouldThrowArgumentNullException_GivenNullSelector()
|
||||
{
|
||||
IEnumerable<Person> source = Enumerable.Range(1, 10).Select(i => new Person {Age = i});
|
||||
Assert.ThrowsException<ArgumentNullException>(() => source.MinMaxBy((Func<Person, int>?)null!));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMaxBy_ShouldThrowArgumentNullException_GivenNullSource()
|
||||
{
|
||||
IEnumerable<Person>? source = null;
|
||||
Assert.ThrowsException<ArgumentNullException>(() => source!.MinMaxBy(p => p.Age));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MinMaxBy_ShouldThrowInvalidOperationException_GivenEmptySource()
|
||||
{
|
||||
IEnumerable<Person> source = ArraySegment<Person>.Empty;
|
||||
Assert.ThrowsException<InvalidOperationException>(() => source.MinMaxBy(p => p.Age));
|
||||
}
|
||||
|
||||
private struct InverseComparer<T> : IComparer<T> where T : IComparable<T>
|
||||
{
|
||||
public int Compare(T? x, T? y)
|
||||
{
|
||||
if (x is null)
|
||||
{
|
||||
return y is null ? 0 : 1;
|
||||
}
|
||||
|
||||
return y is null ? -1 : y.CompareTo(x);
|
||||
}
|
||||
}
|
||||
|
||||
private struct Person : IComparable<Person>, IComparable
|
||||
{
|
||||
public int Age { get; set; }
|
||||
|
||||
public static bool operator <(Person left, Person right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator >(Person left, Person right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(Person left, Person right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(Person left, Person right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
public int CompareTo(Person other)
|
||||
{
|
||||
return Age.CompareTo(other.Age);
|
||||
}
|
||||
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return obj is Person other
|
||||
? CompareTo(other)
|
||||
: throw new ArgumentException($"Object must be of type {nameof(Person)}");
|
||||
}
|
||||
}
|
||||
}
|
399
X10D/src/Linq/EnumerableExtensions.cs
Normal file
399
X10D/src/Linq/EnumerableExtensions.cs
Normal file
@ -0,0 +1,399 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace X10D.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// LINQ-inspired extension methods for <see cref="IEnumerable{T}" />.
|
||||
/// </summary>
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the minimum and maximum values in a sequence of values.
|
||||
/// </summary>
|
||||
/// <param name="source">A sequence of values to determine the minimum and maximum values of.</param>
|
||||
/// <typeparam name="T">The type of the elements in <paramref name="source" />.</typeparam>
|
||||
/// <returns>A tuple containing the minimum and maximum values in <paramref name="source" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
|
||||
public static (T? Minimum, T? Maximum) MinMax<T>(this IEnumerable<T> source)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
#else
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
#endif
|
||||
|
||||
return MinMax(source, Comparer<T>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum and maximum values in a sequence of values, using a specified comparer.
|
||||
/// </summary>
|
||||
/// <param name="source">A sequence of values to determine the minimum and maximum values of.</param>
|
||||
/// <param name="comparer">The comparer which shall be used to compare each element in the sequence.</param>
|
||||
/// <typeparam name="T">The type of the elements in <paramref name="source" />.</typeparam>
|
||||
/// <returns>A tuple containing the minimum and maximum values in <paramref name="source" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
|
||||
public static (T? Minimum, T? Maximum) MinMax<T>(this IEnumerable<T> source, IComparer<T>? comparer)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
#else
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
#endif
|
||||
|
||||
comparer ??= Comparer<T>.Default;
|
||||
T? minValue;
|
||||
T? maxValue;
|
||||
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
if (source.TryGetSpan(out ReadOnlySpan<T> span))
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
throw new InvalidOperationException("Source contains no elements");
|
||||
}
|
||||
|
||||
minValue = span[0];
|
||||
maxValue = minValue;
|
||||
|
||||
for (var index = 1; (uint)index < (uint)span.Length; index++)
|
||||
{
|
||||
T current = span[index];
|
||||
|
||||
if (comparer.Compare(current, minValue) < 0)
|
||||
{
|
||||
minValue = current;
|
||||
}
|
||||
|
||||
if (comparer.Compare(current, maxValue) > 0)
|
||||
{
|
||||
maxValue = current;
|
||||
}
|
||||
}
|
||||
|
||||
return (minValue, maxValue);
|
||||
}
|
||||
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
using (IEnumerator<T> enumerator = source.GetEnumerator())
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
throw new InvalidOperationException("Source contains no elements");
|
||||
}
|
||||
|
||||
minValue = enumerator.Current;
|
||||
maxValue = minValue;
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
T current = enumerator.Current;
|
||||
|
||||
if (minValue is null || comparer.Compare(current, minValue) < 0)
|
||||
{
|
||||
minValue = current;
|
||||
}
|
||||
|
||||
if (maxValue is null || comparer.Compare(current, maxValue) > 0)
|
||||
{
|
||||
maxValue = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (minValue, maxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a transform function on each element of a sequence of elements and returns the minimum and maximum values.
|
||||
/// </summary>
|
||||
/// <param name="source">A sequence of values to determine the minimum and maximum values of.</param>
|
||||
/// <param name="selector">A transform function to apply to each element.</param>
|
||||
/// <typeparam name="TSource">The type of the elements in <paramref name="source" />.</typeparam>
|
||||
/// <typeparam name="TResult">The type of the elements to compare.</typeparam>
|
||||
/// <returns>A tuple containing the minimum and maximum values in <paramref name="source" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
|
||||
public static (TResult? Minimum, TResult? Maximum) MinMax<TSource, TResult>(this IEnumerable<TSource> source,
|
||||
Func<TSource, TResult> selector)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
ArgumentNullException.ThrowIfNull(selector);
|
||||
#else
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
if (selector is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(selector));
|
||||
}
|
||||
#endif
|
||||
|
||||
return MinMax(source, selector, Comparer<TResult>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes a transform function on each element of a sequence of elements and returns the minimum and maximum values,
|
||||
/// using a specified comparer.
|
||||
/// </summary>
|
||||
/// <param name="source">A sequence of values to determine the minimum and maximum values of.</param>
|
||||
/// <param name="selector">A transform function to apply to each element.</param>
|
||||
/// <param name="comparer">The comparer which shall be used to compare each element in the sequence.</param>
|
||||
/// <typeparam name="TSource">The type of the elements in <paramref name="source" />.</typeparam>
|
||||
/// <typeparam name="TResult">The type of the elements to compare.</typeparam>
|
||||
/// <returns>A tuple containing the minimum and maximum values in <paramref name="source" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
|
||||
public static (TResult? Minimum, TResult? Maximum) MinMax<TSource, TResult>(this IEnumerable<TSource> source,
|
||||
Func<TSource, TResult> selector,
|
||||
IComparer<TResult>? comparer)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
ArgumentNullException.ThrowIfNull(selector);
|
||||
#else
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
if (selector is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(selector));
|
||||
}
|
||||
#endif
|
||||
|
||||
comparer ??= Comparer<TResult>.Default;
|
||||
TResult? minValue;
|
||||
TResult? maxValue;
|
||||
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
if (source.TryGetSpan(out ReadOnlySpan<TSource> span))
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
throw new InvalidOperationException("Source contains no elements");
|
||||
}
|
||||
|
||||
minValue = selector(span[0]);
|
||||
maxValue = minValue;
|
||||
|
||||
for (var index = 1; (uint)index < (uint)span.Length; index++)
|
||||
{
|
||||
TResult current = selector(span[index]);
|
||||
|
||||
if (minValue is null || comparer.Compare(current, minValue) < 0)
|
||||
{
|
||||
minValue = current;
|
||||
}
|
||||
|
||||
if (maxValue is null || comparer.Compare(current, maxValue) > 0)
|
||||
{
|
||||
maxValue = current;
|
||||
}
|
||||
}
|
||||
|
||||
return (minValue, maxValue);
|
||||
}
|
||||
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
throw new InvalidOperationException("Source contains no elements");
|
||||
}
|
||||
|
||||
minValue = selector(enumerator.Current);
|
||||
maxValue = minValue;
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
TResult current = selector(enumerator.Current);
|
||||
|
||||
if (minValue is null || comparer.Compare(current, minValue) < 0)
|
||||
{
|
||||
minValue = current;
|
||||
}
|
||||
|
||||
if (maxValue is null || comparer.Compare(current, maxValue) > 0)
|
||||
{
|
||||
maxValue = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (minValue, maxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum and maximum values in a sequence according to a specified key selector function.
|
||||
/// </summary>
|
||||
/// <param name="source">A sequence of values to determine the minimum and maximum values of.</param>
|
||||
/// <param name="keySelector">A function to extract the key for each element.</param>
|
||||
/// <typeparam name="TSource">The type of the elements in <paramref name="source" />.</typeparam>
|
||||
/// <typeparam name="TResult">The type of the elements to compare.</typeparam>
|
||||
/// <returns>A tuple containing the minimum and maximum values in <paramref name="source" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
|
||||
public static (TSource? Minimum, TSource? Maximum) MinMaxBy<TSource, TResult>(this IEnumerable<TSource> source,
|
||||
Func<TSource, TResult> keySelector)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
ArgumentNullException.ThrowIfNull(keySelector);
|
||||
#else
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
if (keySelector is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(keySelector));
|
||||
}
|
||||
#endif
|
||||
|
||||
return MinMaxBy(source, keySelector, Comparer<TResult>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the minimum and maximum values in a sequence according to a specified key selector function.
|
||||
/// </summary>
|
||||
/// <param name="source">A sequence of values to determine the minimum and maximum values of.</param>
|
||||
/// <param name="keySelector">A function to extract the key for each element.</param>
|
||||
/// <param name="comparer">The comparer which shall be used to compare each element in the sequence.</param>
|
||||
/// <typeparam name="TSource">The type of the elements in <paramref name="source" />.</typeparam>
|
||||
/// <typeparam name="TResult">The type of the elements to compare.</typeparam>
|
||||
/// <returns>A tuple containing the minimum and maximum values in <paramref name="source" />.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="InvalidOperationException"><paramref name="source" /> contains no elements.</exception>
|
||||
public static (TSource? Minimum, TSource? Maximum) MinMaxBy<TSource, TResult>(this IEnumerable<TSource> source,
|
||||
Func<TSource, TResult> keySelector,
|
||||
IComparer<TResult>? comparer)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
ArgumentNullException.ThrowIfNull(keySelector);
|
||||
#else
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
if (keySelector is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(keySelector));
|
||||
}
|
||||
#endif
|
||||
|
||||
comparer ??= Comparer<TResult>.Default;
|
||||
TSource? minValue;
|
||||
TSource? maxValue;
|
||||
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
if (source.TryGetSpan(out ReadOnlySpan<TSource> span))
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
throw new InvalidOperationException("Source contains no elements");
|
||||
}
|
||||
|
||||
minValue = span[0];
|
||||
maxValue = minValue;
|
||||
|
||||
for (var index = 1; (uint)index < (uint)span.Length; index++)
|
||||
{
|
||||
TSource current = span[index];
|
||||
TResult transformedCurrent = keySelector(current);
|
||||
|
||||
if (minValue is null || comparer.Compare(transformedCurrent, keySelector(minValue)) < 0)
|
||||
{
|
||||
minValue = current;
|
||||
}
|
||||
|
||||
if (maxValue is null || comparer.Compare(transformedCurrent, keySelector(maxValue)) > 0)
|
||||
{
|
||||
maxValue = current;
|
||||
}
|
||||
}
|
||||
|
||||
return (minValue, maxValue);
|
||||
}
|
||||
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
throw new InvalidOperationException("Source contains no elements");
|
||||
}
|
||||
|
||||
minValue = enumerator.Current;
|
||||
maxValue = minValue;
|
||||
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
TSource current = enumerator.Current;
|
||||
TResult transformedCurrent = keySelector(current);
|
||||
|
||||
if (minValue is null || comparer.Compare(transformedCurrent, keySelector(minValue)) < 0)
|
||||
{
|
||||
minValue = current;
|
||||
}
|
||||
|
||||
if (maxValue is null || comparer.Compare(transformedCurrent, keySelector(maxValue)) > 0)
|
||||
{
|
||||
maxValue = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (minValue, maxValue);
|
||||
}
|
||||
|
||||
private static bool TryGetSpan<TSource>(this IEnumerable<TSource> source, out ReadOnlySpan<TSource> span)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
#else
|
||||
if (source is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
#endif
|
||||
|
||||
var result = true;
|
||||
|
||||
switch (source)
|
||||
{
|
||||
case TSource[] array:
|
||||
span = array;
|
||||
break;
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
case List<TSource> list:
|
||||
span = CollectionsMarshal.AsSpan(list);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
span = default;
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user