From 03a160ceaa0942f46cf585894a3c6e53df040076 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Mon, 23 Sep 2024 08:30:42 +0200 Subject: [PATCH] Add generic version of "Sequence" for any number type This is a squashed merge of PR #893 that closes #892. --- MoreLinq.Test/NullArgumentTest.cs | 27 +++-- MoreLinq/MoreLinq.csproj | 6 +- .../PublicAPI/net8.0/PublicAPI.Unshipped.txt | 3 + MoreLinq/Sequence.cs | 100 +++++++++++++++++- 4 files changed, 116 insertions(+), 20 deletions(-) diff --git a/MoreLinq.Test/NullArgumentTest.cs b/MoreLinq.Test/NullArgumentTest.cs index 13a9aa364..22b3c2a99 100644 --- a/MoreLinq.Test/NullArgumentTest.cs +++ b/MoreLinq.Test/NullArgumentTest.cs @@ -92,20 +92,19 @@ static MethodInfo InstantiateMethod(MethodInfo definition) { if (!definition.IsGenericMethodDefinition) return definition; - var typeArguments = definition.GetGenericArguments().Select(t => InstantiateType(t.GetTypeInfo())).ToArray(); - return definition.MakeGenericMethod(typeArguments); - } - - static Type InstantiateType(TypeInfo typeParameter) - { - var constraints = typeParameter.GetGenericParameterConstraints(); - - return constraints.Length switch - { - 0 => typeof(int), - 1 => constraints.Single(), - _ => throw new NotImplementedException("NullArgumentTest.InstantiateType") - }; + var typeArguments = + from t in definition.GetGenericArguments() + select t.GetGenericParameterConstraints() switch + { + { Length: 0 } => typeof(int), +#if NET7_0_OR_GREATER + var constraints when constraints.Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(System.Numerics.INumber<>)) => typeof(int), +#endif + { Length: 1 } constraints => constraints.Single(), + _ => throw new NotImplementedException("NullArgumentTest.InstantiateType") + }; + + return definition.MakeGenericMethod(typeArguments.ToArray()); } static bool IsReferenceType(ParameterInfo parameter) => diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 1ec1d5123..4da96aafa 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -213,8 +213,12 @@ $(DefineConstants);MORELINQ + + $(DefineConstants);NO_STATIC_ABSTRACTS + + - $(DefineConstants);NO_ASYNC_STREAMS;NO_BUFFERS + $(DefineConstants);NO_STATIC_ABSTRACTS;NO_ASYNC_STREAMS;NO_BUFFERS diff --git a/MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt index 7dc5c5811..5b1195af3 100644 --- a/MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -1 +1,4 @@ #nullable enable +static MoreLinq.MoreEnumerable.Sequence(T start) -> System.Collections.Generic.IEnumerable! +static MoreLinq.MoreEnumerable.Sequence(T start, T stop) -> System.Collections.Generic.IEnumerable! +static MoreLinq.MoreEnumerable.Sequence(T start, T stop, T step) -> System.Collections.Generic.IEnumerable! diff --git a/MoreLinq/Sequence.cs b/MoreLinq/Sequence.cs index 6b7f94502..41dd663cf 100644 --- a/MoreLinq/Sequence.cs +++ b/MoreLinq/Sequence.cs @@ -15,6 +15,90 @@ // limitations under the License. #endregion +#if !NO_STATIC_ABSTRACTS + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Numerics; + + static partial class MoreEnumerable + { + /// + /// Generates a sequence of numbers starting with the given value and in steps of 1. + /// + /// + /// A type that represents a number and defines its minimum and maximum representable value. + /// + /// The value of the first number in the sequence. + /// + /// A sequence of sequential numbers starting with and up to the + /// maximum representable value, in increments of 1. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Sequence(T start) + where T : INumber, IMinMaxValue => + Sequence(start, T.MaxValue, T.One); + + /// + /// Generates a sequence of numbers within the (inclusive) specified range. + /// If sequence is ascending the step is +1, otherwise -1. + /// + /// A type that represents a number. + /// The value of the first number in the sequence. + /// The value of the last number in the sequence. + /// A sequence of sequential numbers. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Sequence(T start, T stop) + where T : INumber => + Sequence(start, stop, stop < start ? -T.One : T.One); + + /// + /// Generates a sequence of numbers within the (inclusive) specified range. An additional + /// parameter specifies the steps in which the numbers of the sequence increase or decrease. + /// + /// A type that represents a number. + /// The value of the first number in the sequence. + /// The value of the last number in the sequence. + /// The step to define the next number. + /// A sequence of sequential numbers. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + /// When is equal to zero, this operator returns an infinite + /// sequence where all elements are equal to . + /// + + public static IEnumerable Sequence(T start, T stop, T step) + where T : INumber + { + var current = start; + + while (step >= T.Zero ? stop >= current : stop <= current) + { + yield return current; + try + { + current = checked(current + step); + } + catch (OverflowException) + { + yield break; + } + } + } + } +} + +#endif // !NO_STATIC_ABSTRACTS + namespace MoreLinq { using System.Collections.Generic; @@ -38,10 +122,12 @@ static partial class MoreEnumerable /// The result variable will contain { 6, 5, 4, 3, 2, 1, 0 }. /// - public static IEnumerable Sequence(int start, int stop) - { - return Sequence(start, stop, start < stop ? 1 : -1); - } + public static IEnumerable Sequence(int start, int stop) => +#if !NO_STATIC_ABSTRACTS + Sequence(start, stop); +#else + Sequence(start, stop, start < stop ? 1 : -1); +#endif /// /// Generates a sequence of integral numbers within the (inclusive) specified range. @@ -53,7 +139,7 @@ public static IEnumerable Sequence(int start, int stop) /// An that contains a range of sequential integral numbers. /// /// When is equal to zero, this operator returns an - /// infinite sequence where all elements are equals to . + /// infinite sequence where all elements are equal to . /// This operator uses deferred execution and streams its results. /// /// @@ -65,6 +151,9 @@ public static IEnumerable Sequence(int start, int stop) public static IEnumerable Sequence(int start, int stop, int step) { +#if !NO_STATIC_ABSTRACTS + return Sequence(start, stop, step); +#else long current = start; while (step >= 0 ? stop >= current @@ -73,6 +162,7 @@ public static IEnumerable Sequence(int start, int stop, int step) yield return (int)current; current += step; } +#endif } } }