diff --git a/MoreLinq.Test/AggregateRightTest.cs b/MoreLinq.Test/AggregateRightTest.cs index 0d3aa62af..d294d6a50 100644 --- a/MoreLinq.Test/AggregateRightTest.cs +++ b/MoreLinq.Test/AggregateRightTest.cs @@ -103,3 +103,17 @@ public void AggregateRightResultor() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void AggregateRightCanBuildWithSystemLinq() + { + new int[0].AggregateRight((l, r) => r); + } + } +} diff --git a/MoreLinq.Test/AggregateTest.cs b/MoreLinq.Test/AggregateTest.cs index 98b49e8b6..679d75a14 100644 --- a/MoreLinq.Test/AggregateTest.cs +++ b/MoreLinq.Test/AggregateTest.cs @@ -28,6 +28,8 @@ namespace MoreLinq.Test using NUnit.Framework.Interfaces; using static FuncModule; + using static MoreLinq.Extensions.AppendExtension; + [TestFixture] public class AggregateTest { @@ -214,3 +216,20 @@ public void Issue616() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void AggregateCanBuildWithSystemLinq() + { + new int[0].Aggregate( + 1, (l, r) => r, + 1, (l, r) => r, + (l, r) => (l, r)); + } + } +} diff --git a/MoreLinq.Test/AppendTest.cs b/MoreLinq.Test/AppendTest.cs index df46b13c7..9fe656489 100644 --- a/MoreLinq.Test/AppendTest.cs +++ b/MoreLinq.Test/AppendTest.cs @@ -20,6 +20,8 @@ namespace MoreLinq.Test using System.Collections.Generic; using NUnit.Framework; + using static MoreLinq.Extensions.AppendExtension; + [TestFixture] public class AppendTest { @@ -90,3 +92,17 @@ public void AppendWithSharedSource() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void AppendCanBuildWithSystemLinq() + { + new int[0].Append(1); + } + } +} diff --git a/MoreLinq.Test/AtLeastTest.cs b/MoreLinq.Test/AtLeastTest.cs index 691b7ae99..a1599bfad 100644 --- a/MoreLinq.Test/AtLeastTest.cs +++ b/MoreLinq.Test/AtLeastTest.cs @@ -102,3 +102,17 @@ public void AtLeastDoesNotIterateUnnecessaryElements() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void AtLeastCanBuildWithSystemLinq() + { + new int[0].AtLeast(1); + } + } +} diff --git a/MoreLinq.Test/AtMostTest.cs b/MoreLinq.Test/AtMostTest.cs index f16b9e41c..b35f767ab 100644 --- a/MoreLinq.Test/AtMostTest.cs +++ b/MoreLinq.Test/AtMostTest.cs @@ -82,3 +82,17 @@ public void AtMostDoesNotIterateUnnecessaryElements() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void AtMostCanBuildWithSystemLinq() + { + new int[0].AtMost(1); + } + } +} diff --git a/MoreLinq.Test/BacksertTest.cs b/MoreLinq.Test/BacksertTest.cs index 511a2f88f..5b6f712b5 100644 --- a/MoreLinq.Test/BacksertTest.cs +++ b/MoreLinq.Test/BacksertTest.cs @@ -61,3 +61,17 @@ public IEnumerable Backsert(int[] seq1, int index, int[] seq2) } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void BacksertCanBuildWithSystemLinq() + { + new int[0].Backsert(new int[0], 0); + } + } +} diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index bc4bec843..4687acf3e 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -125,3 +125,17 @@ public void BatchEmptySource(SourceKind kind) } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void BatchCanBuildWithSystemLinq() + { + new int[0].Batch(2); + } + } +} diff --git a/MoreLinq.Test/CartesianTest.cs b/MoreLinq.Test/CartesianTest.cs index 36dcaebcb..a88843a28 100644 --- a/MoreLinq.Test/CartesianTest.cs +++ b/MoreLinq.Test/CartesianTest.cs @@ -166,3 +166,18 @@ public void TestEmptyCartesianEvaluation() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void CartesianCanBuildWithSystemLinq() + { + new int[0].Cartesian(new int[0], ValueTuple.Create); + } + } +} diff --git a/MoreLinq.Test/ChooseTest.cs b/MoreLinq.Test/ChooseTest.cs index acc4dd112..d4dd5e4c1 100644 --- a/MoreLinq.Test/ChooseTest.cs +++ b/MoreLinq.Test/ChooseTest.cs @@ -87,3 +87,17 @@ public void ThoseEven() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ChooseCanBuildWithSystemLinq() + { + new int[0].Choose(x => (true, x)); + } + } +} diff --git a/MoreLinq.Test/ConsumeTest.cs b/MoreLinq.Test/ConsumeTest.cs index ebc8f4e88..01ece8a3d 100644 --- a/MoreLinq.Test/ConsumeTest.cs +++ b/MoreLinq.Test/ConsumeTest.cs @@ -32,3 +32,17 @@ public void ConsumeReallyConsumes() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ConsumeCanBuildWithSystemLinq() + { + new int[0].Consume(); + } + } +} diff --git a/MoreLinq.Test/CountBetweenTest.cs b/MoreLinq.Test/CountBetweenTest.cs index 2fc5fe4cc..06936cfa1 100644 --- a/MoreLinq.Test/CountBetweenTest.cs +++ b/MoreLinq.Test/CountBetweenTest.cs @@ -73,3 +73,17 @@ public void CountBetweenDoesNotIterateUnnecessaryElements() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void CountBetweenCanBuildWithSystemLinq() + { + new int[0].CountBetween(1, 2); + } + } +} diff --git a/MoreLinq.Test/CountByTest.cs b/MoreLinq.Test/CountByTest.cs index 26158260d..3b0ef2cdd 100644 --- a/MoreLinq.Test/CountByTest.cs +++ b/MoreLinq.Test/CountByTest.cs @@ -105,3 +105,18 @@ public void CountByWithSomeNullKeys() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void CountByCanBuildWithSystemLinq() + { + new int[0].CountBy(x => 1); + } + } +} diff --git a/MoreLinq.Test/CountDownTest.cs b/MoreLinq.Test/CountDownTest.cs index 1e42f40ea..f06b55855 100644 --- a/MoreLinq.Test/CountDownTest.cs +++ b/MoreLinq.Test/CountDownTest.cs @@ -213,3 +213,18 @@ public ReadOnlyCollection(ICollection collection, } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void CountDownCanBuildWithSystemLinq() + { + new int[0].CountDown(5, ValueTuple.Create); + } + } +} diff --git a/MoreLinq.Test/DistinctByTest.cs b/MoreLinq.Test/DistinctByTest.cs index 91dbf8c94..da81002de 100644 --- a/MoreLinq.Test/DistinctByTest.cs +++ b/MoreLinq.Test/DistinctByTest.cs @@ -20,6 +20,8 @@ namespace MoreLinq.Test using System; using NUnit.Framework; + using static MoreLinq.Extensions.DistinctByExtension; + [TestFixture] public class DistinctByTest { @@ -61,3 +63,18 @@ public void DistinctByIsLazyWithComparer() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void DistinctByCanBuildWithSystemLinq() + { + new int[0].DistinctBy(x => 1); + } + } +} diff --git a/MoreLinq.Test/EndsWithTest.cs b/MoreLinq.Test/EndsWithTest.cs index 7d4c1cb14..9048ce83c 100644 --- a/MoreLinq.Test/EndsWithTest.cs +++ b/MoreLinq.Test/EndsWithTest.cs @@ -102,3 +102,18 @@ public void EndsWithUsesCollectionsCountToAvoidUnnecessaryIteration(SourceKind s } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void EndsWithCanBuildWithSystemLinq() + { + new int[0].EndsWith(new int[0]); + } + } +} diff --git a/MoreLinq.Test/EquiZipTest.cs b/MoreLinq.Test/EquiZipTest.cs index f118f21eb..c0e930278 100644 --- a/MoreLinq.Test/EquiZipTest.cs +++ b/MoreLinq.Test/EquiZipTest.cs @@ -103,3 +103,18 @@ public void ZipDisposesInnerSequencesCaseGetEnumeratorThrows() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void EquiZipCanBuildWithSystemLinq() + { + new int[0].EquiZip(new int[0], ValueTuple.Create); + } + } +} diff --git a/MoreLinq.Test/EvaluateTest.cs b/MoreLinq.Test/EvaluateTest.cs index 7330fce27..8d987118e 100644 --- a/MoreLinq.Test/EvaluateTest.cs +++ b/MoreLinq.Test/EvaluateTest.cs @@ -61,3 +61,18 @@ public void TestEvaluateInvokesMethodsMultipleTimes() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void EvaluateCanBuildWithSystemLinq() + { + new Func[0].Evaluate(); + } + } +} diff --git a/MoreLinq.Test/ExactlyTest.cs b/MoreLinq.Test/ExactlyTest.cs index 938847f14..f465e7507 100644 --- a/MoreLinq.Test/ExactlyTest.cs +++ b/MoreLinq.Test/ExactlyTest.cs @@ -68,3 +68,18 @@ public void ExactlyDoesNotIterateUnnecessaryElements() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ExactlyCanBuildWithSystemLinq() + { + new int[0].Exactly(3); + } + } +} diff --git a/MoreLinq.Test/ExceptByTest.cs b/MoreLinq.Test/ExceptByTest.cs index a72aee232..d8407d809 100644 --- a/MoreLinq.Test/ExceptByTest.cs +++ b/MoreLinq.Test/ExceptByTest.cs @@ -20,6 +20,8 @@ namespace MoreLinq.Test using System; using NUnit.Framework; + using static MoreLinq.Extensions.ExceptByExtension; + [TestFixture] public class ExceptByTest { @@ -74,3 +76,18 @@ public void ExceptByIsLazyWithComparer() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ExceptByCanBuildWithSystemLinq() + { + new int[0].ExceptBy(new int[0], x => 1); + } + } +} diff --git a/MoreLinq.Test/ExcludeTest.cs b/MoreLinq.Test/ExcludeTest.cs index 61cd5241f..8538ff10c 100644 --- a/MoreLinq.Test/ExcludeTest.cs +++ b/MoreLinq.Test/ExcludeTest.cs @@ -162,3 +162,18 @@ public void TestExcludeStartIndexGreaterThanSequenceLength() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ExcludeCanBuildWithSystemLinq() + { + new int[0].Exclude(1, 2); + } + } +} diff --git a/MoreLinq.Test/FallbackIfEmptyTest.cs b/MoreLinq.Test/FallbackIfEmptyTest.cs index b8e6b7516..799f30c6d 100644 --- a/MoreLinq.Test/FallbackIfEmptyTest.cs +++ b/MoreLinq.Test/FallbackIfEmptyTest.cs @@ -62,3 +62,18 @@ public void FallbackIfEmptyPreservesFallbackCollectionIfPossible(SourceKind sour } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void FallbackIfEmptyCanBuildWithSystemLinq() + { + new int[0].FallbackIfEmpty(); + } + } +} diff --git a/MoreLinq.Test/FillBackwardTest.cs b/MoreLinq.Test/FillBackwardTest.cs index 19129b27b..9444f7f49 100644 --- a/MoreLinq.Test/FillBackwardTest.cs +++ b/MoreLinq.Test/FillBackwardTest.cs @@ -63,3 +63,18 @@ public void FillBackwardWithFillSelector() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void FillBackwardCanBuildWithSystemLinq() + { + new int[0].FillBackward(); + } + } +} diff --git a/MoreLinq.Test/FillForwardTest.cs b/MoreLinq.Test/FillForwardTest.cs index 8eb004942..b3f37c058 100644 --- a/MoreLinq.Test/FillForwardTest.cs +++ b/MoreLinq.Test/FillForwardTest.cs @@ -84,3 +84,18 @@ select line.Trim() into line } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void FillForwardCanBuildWithSystemLinq() + { + new int[0].FillForward(); + } + } +} diff --git a/MoreLinq.Test/FoldTest.cs b/MoreLinq.Test/FoldTest.cs index 0c1c7185a..f021462df 100644 --- a/MoreLinq.Test/FoldTest.cs +++ b/MoreLinq.Test/FoldTest.cs @@ -68,3 +68,18 @@ public void Fold() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void FoldCanBuildWithSystemLinq() + { + new int[0].Fold((x, y) => 1); + } + } +} diff --git a/MoreLinq.Test/ForEachTest.cs b/MoreLinq.Test/ForEachTest.cs index ddfd5f1e7..bcc68e684 100644 --- a/MoreLinq.Test/ForEachTest.cs +++ b/MoreLinq.Test/ForEachTest.cs @@ -42,3 +42,17 @@ public void ForEachIndexedWithSequence() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ForEachCanBuildWithSystemLinq() + { + new int[0].ForEach(x => { }); + } + } +} diff --git a/MoreLinq.Test/FullGroupJoinTest.cs b/MoreLinq.Test/FullGroupJoinTest.cs index 40d8778c5..9db2b8199 100644 --- a/MoreLinq.Test/FullGroupJoinTest.cs +++ b/MoreLinq.Test/FullGroupJoinTest.cs @@ -147,3 +147,18 @@ public void FullGroupPreservesOrder(OverloadCase overloadCase) } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void FullGroupJoinCanBuildWithSystemLinq() + { + new int[0].FullGroupJoin(new int[0], x => 1, y => 1, (x, y, z) => 1); + } + } +} diff --git a/MoreLinq.Test/FullJoinTest.cs b/MoreLinq.Test/FullJoinTest.cs index 88d22b6f2..b741e5970 100644 --- a/MoreLinq.Test/FullJoinTest.cs +++ b/MoreLinq.Test/FullJoinTest.cs @@ -204,3 +204,18 @@ public void FullJoinEmptyRight() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void FullJoinCanBuildWithSystemLinq() + { + new int[0].FullJoin(new int[0], x => 1, x => 1, y => 1, (x, y) => 1); + } + } +} diff --git a/MoreLinq.Test/GroupAdjacentTest.cs b/MoreLinq.Test/GroupAdjacentTest.cs index 995405f1d..749ee3c83 100644 --- a/MoreLinq.Test/GroupAdjacentTest.cs +++ b/MoreLinq.Test/GroupAdjacentTest.cs @@ -20,6 +20,7 @@ namespace MoreLinq.Test using System; using System.Collections.Generic; using NUnit.Framework; + using static MoreLinq.Extensions.AppendExtension; [TestFixture] public class GroupAdjacentTest @@ -239,3 +240,18 @@ static void AssertResult(SequenceReader reader, TElement ele } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void GroupAdjacentCanBuildWithSystemLinq() + { + new int[0].GroupAdjacent(x => 1); + } + } +} diff --git a/MoreLinq.Test/IndexByTest.cs b/MoreLinq.Test/IndexByTest.cs index 2a02d7474..5cb895caa 100644 --- a/MoreLinq.Test/IndexByTest.cs +++ b/MoreLinq.Test/IndexByTest.cs @@ -121,3 +121,17 @@ public void IndexBytDoesNotIterateUnnecessaryElements() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void IndexByCanBuildWithSystemLinq() + { + new int[0].IndexBy(x => 1); + } + } +} diff --git a/MoreLinq.Test/IndexTest.cs b/MoreLinq.Test/IndexTest.cs index 6b57ee2c3..120a29a1a 100644 --- a/MoreLinq.Test/IndexTest.cs +++ b/MoreLinq.Test/IndexTest.cs @@ -57,3 +57,17 @@ public void IndexSequenceStartIndex() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void IndexCanBuildWithSystemLinq() + { + new int[0].Index(); + } + } +} diff --git a/MoreLinq.Test/InsertTest.cs b/MoreLinq.Test/InsertTest.cs index b8d51f514..f5dd24854 100644 --- a/MoreLinq.Test/InsertTest.cs +++ b/MoreLinq.Test/InsertTest.cs @@ -88,3 +88,18 @@ public void InsertIsLazy() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void InsertCanBuildWithSystemLinq() + { + new int[0].Insert(new int[0], 2); + } + } +} diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index b201a776e..9660a5ea9 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -184,3 +184,17 @@ public void TestInterleaveDisposesAllIterators() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void InterleaveCanBuildWithSystemLinq() + { + new int[0].Interleave(new int[0]); + } + } +} diff --git a/MoreLinq.Test/LagTest.cs b/MoreLinq.Test/LagTest.cs index 331fae6f7..1f729db4b 100644 --- a/MoreLinq.Test/LagTest.cs +++ b/MoreLinq.Test/LagTest.cs @@ -160,3 +160,18 @@ public void TestLagWithNonNullableReferences() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void LagCanBuildWithSystemLinq() + { + new int[0].Lag(1, ValueTuple.Create); + } + } +} diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index e0d4c592d..b2b588e3e 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -162,3 +162,18 @@ public void TestLagWithNonNullableReferences() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void LeadCanBuildWithSystemLinq() + { + new int[0].Lead(1, ValueTuple.Create); + } + } +} diff --git a/MoreLinq.Test/LeftJoinTest.cs b/MoreLinq.Test/LeftJoinTest.cs index 9bd5d7bdc..fe308fc89 100644 --- a/MoreLinq.Test/LeftJoinTest.cs +++ b/MoreLinq.Test/LeftJoinTest.cs @@ -183,3 +183,18 @@ public void LeftJoinEmptyRight() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void LeftJoinCanBuildWithSystemLinq() + { + new int[0].LeftJoin(new int[0], x => 1, y => 1, (x, y) => 1); + } + } +} diff --git a/MoreLinq.Test/MaxByTest.cs b/MoreLinq.Test/MaxElementsByTest.cs similarity index 73% rename from MoreLinq.Test/MaxByTest.cs rename to MoreLinq.Test/MaxElementsByTest.cs index 737d10e93..2ce037a85 100644 --- a/MoreLinq.Test/MaxByTest.cs +++ b/MoreLinq.Test/MaxElementsByTest.cs @@ -20,45 +20,47 @@ namespace MoreLinq.Test using System; using NUnit.Framework; + using static MoreLinq.Extensions.MaxElementsByExtension; + [TestFixture] - public class MaxByTest + public class MaxElementsByTest { [Test] - public void MaxByIsLazy() + public void MaxElementsByIsLazy() { - new BreakingSequence().MaxBy(BreakingFunc.Of()); + new BreakingSequence().MaxElementsBy(BreakingFunc.Of()); } [Test] - public void MaxByReturnsMaxima() + public void MaxElementsByReturnsMaxima() { Assert.AreEqual(new[] { "hello", "world" }, - SampleData.Strings.MaxBy(x => x.Length)); + SampleData.Strings.MaxElementsBy(x => x.Length)); } [Test] - public void MaxByNullComparer() + public void MaxElementsByNullComparer() { - Assert.AreEqual(SampleData.Strings.MaxBy(x => x.Length), - SampleData.Strings.MaxBy(x => x.Length, null)); + Assert.AreEqual(SampleData.Strings.MaxElementsBy(x => x.Length), + SampleData.Strings.MaxElementsBy(x => x.Length, null)); } [Test] - public void MaxByEmptySequence() + public void MaxElementsByEmptySequence() { - Assert.That(new string[0].MaxBy(x => x.Length), Is.Empty); + Assert.That(new string[0].MaxElementsBy(x => x.Length), Is.Empty); } [Test] - public void MaxByWithNaturalComparer() + public void MaxElementsByWithNaturalComparer() { - Assert.AreEqual(new[] { "az" }, SampleData.Strings.MaxBy(x => x[1])); + Assert.AreEqual(new[] { "az" }, SampleData.Strings.MaxElementsBy(x => x[1])); } [Test] - public void MaxByWithComparer() + public void MaxElementsByWithComparer() { - Assert.AreEqual(new[] { "aa" }, SampleData.Strings.MaxBy(x => x[1], Comparable.DescendingOrderComparer)); + Assert.AreEqual(new[] { "aa" }, SampleData.Strings.MaxElementsBy(x => x[1], Comparable.DescendingOrderComparer)); } public class First @@ -67,7 +69,7 @@ public class First public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length); + var maxima = strings.MaxElementsBy(s => s.Length); Assert.That(MoreEnumerable.First(maxima), Is.EqualTo("hello")); } @@ -75,7 +77,7 @@ public void ReturnsMaximum() public void WithComparerReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + var maxima = strings.MaxElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.First(maxima), Is.EqualTo("ax")); } @@ -84,7 +86,7 @@ public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - MoreEnumerable.First(strings.MaxBy(s => s.Length))); + MoreEnumerable.First(strings.MaxElementsBy(s => s.Length))); } [Test] @@ -92,7 +94,7 @@ public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - MoreEnumerable.First(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer))); + MoreEnumerable.First(strings.MaxElementsBy(s => s.Length, Comparable.DescendingOrderComparer))); } } @@ -102,7 +104,7 @@ public class FirstOrDefault public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length); + var maxima = strings.MaxElementsBy(s => s.Length); Assert.That(MoreEnumerable.FirstOrDefault(maxima), Is.EqualTo("hello")); } @@ -110,7 +112,7 @@ public void ReturnsMaximum() public void WithComparerReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + var maxima = strings.MaxElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.FirstOrDefault(maxima), Is.EqualTo("ax")); } @@ -118,7 +120,7 @@ public void WithComparerReturnsMaximum() public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length); + var maxima = strings.MaxElementsBy(s => s.Length); Assert.That(MoreEnumerable.FirstOrDefault(maxima), Is.Null); } @@ -126,7 +128,7 @@ public void WithEmptySourceReturnsDefault() public void WithEmptySourceWithComparerReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + var maxima = strings.MaxElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.FirstOrDefault(maxima), Is.Null); } } @@ -137,7 +139,7 @@ public class Last public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length); + var maxima = strings.MaxElementsBy(s => s.Length); Assert.That(MoreEnumerable.Last(maxima), Is.EqualTo("world")); } @@ -145,7 +147,7 @@ public void ReturnsMaximum() public void WithComparerReturnsMaximumPerComparer() { using var strings = SampleData.Strings.AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + var maxima = strings.MaxElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.Last(maxima), Is.EqualTo("az")); } @@ -154,7 +156,7 @@ public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - MoreEnumerable.Last(strings.MaxBy(s => s.Length))); + MoreEnumerable.Last(strings.MaxElementsBy(s => s.Length))); } [Test] @@ -162,7 +164,7 @@ public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - MoreEnumerable.Last(strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer))); + MoreEnumerable.Last(strings.MaxElementsBy(s => s.Length, Comparable.DescendingOrderComparer))); } } @@ -172,7 +174,7 @@ public class LastOrDefault public void ReturnsMaximum() { using var strings = SampleData.Strings.AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length); + var maxima = strings.MaxElementsBy(s => s.Length); Assert.That(MoreEnumerable.LastOrDefault(maxima), Is.EqualTo("world")); } @@ -180,7 +182,7 @@ public void ReturnsMaximum() public void WithComparerReturnsMaximumPerComparer() { using var strings = SampleData.Strings.AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + var maxima = strings.MaxElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.LastOrDefault(maxima), Is.EqualTo("az")); } @@ -188,7 +190,7 @@ public void WithComparerReturnsMaximumPerComparer() public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length); + var maxima = strings.MaxElementsBy(s => s.Length); Assert.That(MoreEnumerable.LastOrDefault(maxima), Is.Null); } @@ -196,7 +198,7 @@ public void WithEmptySourceReturnsDefault() public void WithEmptySourceWithComparerReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - var maxima = strings.MaxBy(s => s.Length, Comparable.DescendingOrderComparer); + var maxima = strings.MaxElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.LastOrDefault(maxima), Is.Null); } } @@ -210,7 +212,7 @@ public class Take public string[] ReturnsMaxima(int count) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MaxBy(s => s.Length).Take(count).ToArray(); + return strings.MaxElementsBy(s => s.Length).Take(count).ToArray(); } [TestCase(0, 0, ExpectedResult = new string[0] )] @@ -224,7 +226,7 @@ public string[] ReturnsMaxima(int count) public string[] WithComparerReturnsMaximaPerComparer(int count, int index) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MaxBy(s => s[index], Comparable.DescendingOrderComparer) + return strings.MaxElementsBy(s => s[index], Comparable.DescendingOrderComparer) .Take(count) .ToArray(); } @@ -239,7 +241,7 @@ public class TakeLast public string[] TakeLastReturnsMaxima(int count) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MaxBy(s => s.Length).TakeLast(count).ToArray(); + return strings.MaxElementsBy(s => s.Length).TakeLast(count).ToArray(); } [TestCase(0, 0, ExpectedResult = new string[0] )] @@ -253,10 +255,27 @@ public string[] TakeLastReturnsMaxima(int count) public string[] WithComparerReturnsMaximaPerComparer(int count, int index) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MaxBy(s => s[index], Comparable.DescendingOrderComparer) + return strings.MaxElementsBy(s => s[index], Comparable.DescendingOrderComparer) .TakeLast(count) .ToArray(); } } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void MaxElementsByCanBuildWithSystemLinq() + { +#pragma warning disable CS0618 + new int[0].MaxBy(x => 1); + new int[0].MaxElementsBy(x => 1); +#pragma warning restore CS0618 + } + } +} diff --git a/MoreLinq.Test/MinByTest.cs b/MoreLinq.Test/MinElementsByTest.cs similarity index 72% rename from MoreLinq.Test/MinByTest.cs rename to MoreLinq.Test/MinElementsByTest.cs index 934e49dd5..dbc21a9b2 100644 --- a/MoreLinq.Test/MinByTest.cs +++ b/MoreLinq.Test/MinElementsByTest.cs @@ -20,45 +20,47 @@ namespace MoreLinq.Test using System; using NUnit.Framework; + using static MoreLinq.Extensions.MinElementsByExtension; + [TestFixture] - public class MinByTest + public class MinElementsByTest { [Test] - public void MinByIsLazy() + public void MinElementsByIsLazy() { - new BreakingSequence().MinBy(BreakingFunc.Of()); + new BreakingSequence().MinElementsBy(BreakingFunc.Of()); } [Test] - public void MinByReturnsMinima() + public void MinElementsByReturnsMinima() { Assert.AreEqual(new[] { "ax", "aa", "ab", "ay", "az" }, - SampleData.Strings.MinBy(x => x.Length)); + SampleData.Strings.MinElementsBy(x => x.Length)); } [Test] - public void MinByNullComparer() + public void MinElementsByNullComparer() { - Assert.AreEqual(SampleData.Strings.MinBy(x => x.Length), - SampleData.Strings.MinBy(x => x.Length, null)); + Assert.AreEqual(SampleData.Strings.MinElementsBy(x => x.Length), + SampleData.Strings.MinElementsBy(x => x.Length, null)); } [Test] - public void MinByEmptySequence() + public void MinElementsByEmptySequence() { - Assert.That(new string[0].MinBy(x => x.Length), Is.Empty); + Assert.That(new string[0].MinElementsBy(x => x.Length), Is.Empty); } [Test] - public void MinByWithNaturalComparer() + public void MinElementsByWithNaturalComparer() { - Assert.AreEqual(new[] { "aa" }, SampleData.Strings.MinBy(x => x[1])); + Assert.AreEqual(new[] { "aa" }, SampleData.Strings.MinElementsBy(x => x[1])); } [Test] - public void MinByWithComparer() + public void MinElementsByWithComparer() { - Assert.AreEqual(new[] { "az" }, SampleData.Strings.MinBy(x => x[1], Comparable.DescendingOrderComparer)); + Assert.AreEqual(new[] { "az" }, SampleData.Strings.MinElementsBy(x => x[1], Comparable.DescendingOrderComparer)); } public class First @@ -67,7 +69,7 @@ public class First public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - var minima = MoreEnumerable.First(strings.MinBy(s => s.Length)); + var minima = MoreEnumerable.First(strings.MinElementsBy(s => s.Length)); Assert.That(minima, Is.EqualTo("ax")); } @@ -75,7 +77,7 @@ public void ReturnsMinimum() public void WithComparerReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + var minima = strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.First(minima), Is.EqualTo("hello")); } @@ -84,7 +86,7 @@ public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - MoreEnumerable.First(strings.MinBy(s => s.Length))); + MoreEnumerable.First(strings.MinElementsBy(s => s.Length))); } [Test] @@ -92,7 +94,7 @@ public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - MoreEnumerable.First(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer))); + MoreEnumerable.First(strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer))); } } @@ -102,7 +104,7 @@ public class FirstOrDefault public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - var minima = strings.MinBy(s => s.Length); + var minima = strings.MinElementsBy(s => s.Length); Assert.That(MoreEnumerable.FirstOrDefault(minima), Is.EqualTo("ax")); } @@ -110,7 +112,7 @@ public void ReturnsMinimum() public void WithComparerReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + var minima = strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.FirstOrDefault(minima), Is.EqualTo("hello")); } @@ -118,7 +120,7 @@ public void WithComparerReturnsMinimum() public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - var minima = strings.MinBy(s => s.Length); + var minima = strings.MinElementsBy(s => s.Length); Assert.That(MoreEnumerable.FirstOrDefault(minima), Is.Null); } @@ -126,7 +128,7 @@ public void WithEmptySourceReturnsDefault() public void WithEmptySourceWithComparerReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + var minima = strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.FirstOrDefault(minima), Is.Null); } } @@ -137,7 +139,7 @@ public class Last public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - var minima = strings.MinBy(s => s.Length); + var minima = strings.MinElementsBy(s => s.Length); Assert.That(MoreEnumerable.Last(minima), Is.EqualTo("az")); } @@ -145,7 +147,7 @@ public void ReturnsMinimum() public void WithComparerReturnsMinimumPerComparer() { using var strings = SampleData.Strings.AsTestingSequence(); - var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + var minima = strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.Last(minima), Is.EqualTo("world")); } @@ -154,7 +156,7 @@ public void WithEmptySourceThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - MoreEnumerable.Last(strings.MinBy(s => s.Length))); + MoreEnumerable.Last(strings.MinElementsBy(s => s.Length))); } [Test] @@ -162,7 +164,7 @@ public void WithEmptySourceWithComparerThrows() { using var strings = Enumerable.Empty().AsTestingSequence(); Assert.Throws(() => - MoreEnumerable.Last(strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer))); + MoreEnumerable.Last(strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer))); } } @@ -172,7 +174,7 @@ public class LastOrDefault public void ReturnsMinimum() { using var strings = SampleData.Strings.AsTestingSequence(); - var minima = strings.MinBy(s => s.Length); + var minima = strings.MinElementsBy(s => s.Length); Assert.That(MoreEnumerable.LastOrDefault(minima), Is.EqualTo("az")); } @@ -180,7 +182,7 @@ public void ReturnsMinimum() public void WithComparerReturnsMinimumPerComparer() { using var strings = SampleData.Strings.AsTestingSequence(); - var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + var minima = strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.LastOrDefault(minima), Is.EqualTo("world")); } @@ -188,7 +190,7 @@ public void WithComparerReturnsMinimumPerComparer() public void WithEmptySourceReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - var minima = strings.MinBy(s => s.Length); + var minima = strings.MinElementsBy(s => s.Length); Assert.That(MoreEnumerable.LastOrDefault(minima), Is.Null); } @@ -196,7 +198,7 @@ public void WithEmptySourceReturnsDefault() public void WithEmptySourceWithComparerReturnsDefault() { using var strings = Enumerable.Empty().AsTestingSequence(); - var minima = strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer); + var minima = strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer); Assert.That(MoreEnumerable.LastOrDefault(minima), Is.Null); } } @@ -213,7 +215,7 @@ public class Take public string[] ReturnsMinima(int count) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MinBy(s => s.Length).Take(count).ToArray(); + return strings.MinElementsBy(s => s.Length).Take(count).ToArray(); } [TestCase(0, ExpectedResult = new string[0] )] @@ -223,7 +225,7 @@ public string[] ReturnsMinima(int count) public string[] WithComparerReturnsMinimaPerComparer(int count) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) + return strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer) .Take(count) .ToArray(); } @@ -241,7 +243,7 @@ public class TakeLast public string[] ReturnsMinima(int count) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MinBy(s => s.Length).TakeLast(count).ToArray(); + return strings.MinElementsBy(s => s.Length).TakeLast(count).ToArray(); } [TestCase(0, ExpectedResult = new string[0] )] @@ -251,10 +253,28 @@ public string[] ReturnsMinima(int count) public string[] WithComparerReturnsMinimaPerComparer(int count) { using var strings = SampleData.Strings.AsTestingSequence(); - return strings.MinBy(s => s.Length, Comparable.DescendingOrderComparer) + return strings.MinElementsBy(s => s.Length, Comparable.DescendingOrderComparer) .TakeLast(count) .ToArray(); } } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void MinElementsByCanBuildWithSystemLinq() + { +#pragma warning disable CS0618 + new int[0].MinBy(x => 1); + new int[0].MinElementsBy(x => 1); +#pragma warning restore CS0618 + } + + } +} diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 7558169e9..fa6af186f 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -2,7 +2,7 @@ MoreLinq.Test - net5.0;netcoreapp3.1;netcoreapp2.1;net451 + net6.0;net5.0;netcoreapp3.1;netcoreapp2.1;net451;net461;net472;net48 portable MoreLinq.Test Exe @@ -73,10 +73,11 @@ + - + diff --git a/MoreLinq.Test/MoveTest.cs b/MoreLinq.Test/MoveTest.cs index f001a01b7..08f8bd091 100644 --- a/MoreLinq.Test/MoveTest.cs +++ b/MoreLinq.Test/MoveTest.cs @@ -128,3 +128,18 @@ public void MoveWithCountEqualsZero() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void MoveCanBuildWithSystemLinq() + { + new int[0].Move(1, 1, 1); + } + } +} diff --git a/MoreLinq.Test/OrderByTest.cs b/MoreLinq.Test/OrderByTest.cs index 7cc944c25..ee8fa557d 100644 --- a/MoreLinq.Test/OrderByTest.cs +++ b/MoreLinq.Test/OrderByTest.cs @@ -137,3 +137,17 @@ public void TestThenByComparerPreserved() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void OrderByCanBuildWithSystemLinq() + { + new int[0].OrderBy(x => 1, OrderByDirection.Ascending); + } + } +} diff --git a/MoreLinq.Test/OrderedMergeTest.cs b/MoreLinq.Test/OrderedMergeTest.cs index f2db2acb7..61de9fa7e 100644 --- a/MoreLinq.Test/OrderedMergeTest.cs +++ b/MoreLinq.Test/OrderedMergeTest.cs @@ -70,3 +70,17 @@ public IEnumerable OrderedMerge(IEnumerable first, } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void OrderedMergeCanBuildWithSystemLinq() + { + new int[0].OrderedMerge(new int[0]); + } + } +} diff --git a/MoreLinq.Test/PadStartTest.cs b/MoreLinq.Test/PadStartTest.cs index a1124712c..25dc227b2 100644 --- a/MoreLinq.Test/PadStartTest.cs +++ b/MoreLinq.Test/PadStartTest.cs @@ -146,3 +146,17 @@ static void AssertEqual(ICollection input, Func, IEnumerabl } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PadStartCanBuildWithSystemLinq() + { + new int[0].PadStart(3); + } + } +} diff --git a/MoreLinq.Test/PadTest.cs b/MoreLinq.Test/PadTest.cs index a7d2ccc58..2b63720bd 100644 --- a/MoreLinq.Test/PadTest.cs +++ b/MoreLinq.Test/PadTest.cs @@ -114,3 +114,17 @@ public void PadNarrowSourceSequenceWithNonDefaultPadding() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PadCanBuildWithSystemLinq() + { + new int[0].Pad(5); + } + } +} diff --git a/MoreLinq.Test/PairwiseTest.cs b/MoreLinq.Test/PairwiseTest.cs index d810ea9fa..f15b9294d 100644 --- a/MoreLinq.Test/PairwiseTest.cs +++ b/MoreLinq.Test/PairwiseTest.cs @@ -46,3 +46,18 @@ public void PairwiseWideSourceSequence() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PairwiseCanBuildWithSystemLinq() + { + new int[0].Pairwise(ValueTuple.Create); + } + } +} diff --git a/MoreLinq.Test/PartialSortByTest.cs b/MoreLinq.Test/PartialSortByTest.cs index 9c83dcd7c..3d60c764c 100644 --- a/MoreLinq.Test/PartialSortByTest.cs +++ b/MoreLinq.Test/PartialSortByTest.cs @@ -103,3 +103,17 @@ public void PartialSortByIsStable() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PartialSortByCanBuildWithSystemLinq() + { + new int[0].PartialSortBy(3, x => 1); + } + } +} diff --git a/MoreLinq.Test/PartialSortTest.cs b/MoreLinq.Test/PartialSortTest.cs index 04257a737..32cd92f14 100644 --- a/MoreLinq.Test/PartialSortTest.cs +++ b/MoreLinq.Test/PartialSortTest.cs @@ -20,6 +20,8 @@ namespace MoreLinq.Test using System; using NUnit.Framework; + using static MoreLinq.Extensions.AppendExtension; + [TestFixture] public class PartialSortTests { @@ -108,3 +110,17 @@ public void PartialSortByIsStable() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PartialSortCanBuildWithSystemLinq() + { + new int[0].PartialSort(3); + } + } +} diff --git a/MoreLinq.Test/PartitionTest.cs b/MoreLinq.Test/PartitionTest.cs index cdc4ac19c..425b63a7a 100644 --- a/MoreLinq.Test/PartitionTest.cs +++ b/MoreLinq.Test/PartitionTest.cs @@ -202,3 +202,17 @@ public void PartitionBooleanGroupingWithThreeKeysWithComparer() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PartitionCanBuildWithSystemLinq() + { + new int[0].Partition(x => true); + } + } +} diff --git a/MoreLinq.Test/PermutationsTest.cs b/MoreLinq.Test/PermutationsTest.cs index fe2897556..eb28c16ba 100644 --- a/MoreLinq.Test/PermutationsTest.cs +++ b/MoreLinq.Test/PermutationsTest.cs @@ -198,3 +198,17 @@ public void TestPermutationsAreIndependent() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PermutationsCanBuildWithSystemLinq() + { + new int[0].Permutations(); + } + } +} diff --git a/MoreLinq.Test/PipeTest.cs b/MoreLinq.Test/PipeTest.cs index de119d50c..c4d9e6f2a 100644 --- a/MoreLinq.Test/PipeTest.cs +++ b/MoreLinq.Test/PipeTest.cs @@ -54,3 +54,17 @@ public void PipeActionOccursBeforeYield() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PipeCanBuildWithSystemLinq() + { + new int[0].Pipe(x => { }); + } + } +} diff --git a/MoreLinq.Test/PreScanTest.cs b/MoreLinq.Test/PreScanTest.cs index 1f8c996c6..5eb68706e 100644 --- a/MoreLinq.Test/PreScanTest.cs +++ b/MoreLinq.Test/PreScanTest.cs @@ -72,3 +72,18 @@ public void PreScanFuncIsNotInvokedUnnecessarily() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PreScanCanBuildWithSystemLinq() + { + new int[0].PreScan((x, y) => 1, 0); + } + } +} diff --git a/MoreLinq.Test/PrependTest.cs b/MoreLinq.Test/PrependTest.cs index 381e1deba..bf3221bc4 100644 --- a/MoreLinq.Test/PrependTest.cs +++ b/MoreLinq.Test/PrependTest.cs @@ -21,6 +21,8 @@ namespace MoreLinq.Test using NUnit.Framework; using NUnit.Framework.Interfaces; + using static MoreLinq.Extensions.PrependExtension; + [TestFixture] public class PrependTest { @@ -89,3 +91,17 @@ public void PrependWithSharedSource() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void PrependCanBuildWithSystemLinq() + { + new int[0].Prepend(1); + } + } +} diff --git a/MoreLinq.Test/RankTest.cs b/MoreLinq.Test/RankTest.cs index de3852782..adaaa51ba 100644 --- a/MoreLinq.Test/RankTest.cs +++ b/MoreLinq.Test/RankTest.cs @@ -180,3 +180,19 @@ public void TestRankCustomComparer() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void RankCanBuildWithSystemLinq() + { + new int[0].Rank(); + new int[0].RankBy(x => 1); + } + } +} diff --git a/MoreLinq.Test/RepeatTest.cs b/MoreLinq.Test/RepeatTest.cs index 20399644d..c1fdc3fb7 100644 --- a/MoreLinq.Test/RepeatTest.cs +++ b/MoreLinq.Test/RepeatTest.cs @@ -113,3 +113,17 @@ public void TestRepeatForeverIsLazy() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void RepeatCanBuildWithSystemLinq() + { + new int[0].Repeat(); + } + } +} diff --git a/MoreLinq.Test/RightJoinTest.cs b/MoreLinq.Test/RightJoinTest.cs index 9d7076077..4be48627a 100644 --- a/MoreLinq.Test/RightJoinTest.cs +++ b/MoreLinq.Test/RightJoinTest.cs @@ -183,3 +183,18 @@ public void RightJoinEmptyRight() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void RightJoinCanBuildWithSystemLinq() + { + new int[0].RightJoin(new int[0], x => 1, y => 1, (x, y) => 1); + } + } +} diff --git a/MoreLinq.Test/ScanByTest.cs b/MoreLinq.Test/ScanByTest.cs index ab5098389..fe8a4f2f8 100644 --- a/MoreLinq.Test/ScanByTest.cs +++ b/MoreLinq.Test/ScanByTest.cs @@ -156,3 +156,18 @@ public void ScanByDoesNotIterateUnnecessaryElements() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ScanByCanBuildWithSystemLinq() + { + new int[0].ScanBy(x => 1, x => 1, (x, y, z) => 1); + } + } +} diff --git a/MoreLinq.Test/ScanRightTest.cs b/MoreLinq.Test/ScanRightTest.cs index 3cb334612..36de16a75 100644 --- a/MoreLinq.Test/ScanRightTest.cs +++ b/MoreLinq.Test/ScanRightTest.cs @@ -115,3 +115,18 @@ public void ScanRightSeedIsLazy() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ScanRightCanBuildWithSystemLinq() + { + new int[0].ScanRight((x, y) => 1); + } + } +} diff --git a/MoreLinq.Test/ScanTest.cs b/MoreLinq.Test/ScanTest.cs index ac6abaa32..61bd94cb2 100644 --- a/MoreLinq.Test/ScanTest.cs +++ b/MoreLinq.Test/ScanTest.cs @@ -83,3 +83,18 @@ public void SeededScanDoesNotIterateExtra() } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ScanCanBuildWithSystemLinq() + { + new int[0].Scan((x, y) => 1); + } + } +} diff --git a/MoreLinq.Test/SegmentTest.cs b/MoreLinq.Test/SegmentTest.cs index a9b786568..8f55fa7d9 100644 --- a/MoreLinq.Test/SegmentTest.cs +++ b/MoreLinq.Test/SegmentTest.cs @@ -174,3 +174,18 @@ public IEnumerable> TestSegment(IEnumerable source) } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void SegmentCanBuildWithSystemLinq() + { + new int[0].Segment(x => true); + } + } +} diff --git a/MoreLinq.Test/ShuffleTest.cs b/MoreLinq.Test/ShuffleTest.cs index cd9769d86..b9267a2f1 100644 --- a/MoreLinq.Test/ShuffleTest.cs +++ b/MoreLinq.Test/ShuffleTest.cs @@ -101,3 +101,17 @@ public void ShuffleSeedIsIdempotent() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ShuffleCanBuildWithSystemLinq() + { + new int[0].Shuffle(); + } + } +} diff --git a/MoreLinq.Test/SkipLastTest.cs b/MoreLinq.Test/SkipLastTest.cs index 715f53be4..c5a11c895 100644 --- a/MoreLinq.Test/SkipLastTest.cs +++ b/MoreLinq.Test/SkipLastTest.cs @@ -19,10 +19,12 @@ namespace MoreLinq.Test { using NUnit.Framework; + using static MoreLinq.Extensions.SkipLastExtension; + [TestFixture] public class SkipLastTest { - [TestCase( 0)] + [TestCase(0)] [TestCase(-1)] public void SkipLastWithCountLesserThanOne(int skip) { @@ -58,3 +60,17 @@ public void SkipLastIsLazy() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void SkipLastCanBuildWithSystemLinq() + { + new int[0].SkipLast(1); + } + } +} diff --git a/MoreLinq.Test/SkipUntilTest.cs b/MoreLinq.Test/SkipUntilTest.cs index a1c541b7b..aafa3558c 100644 --- a/MoreLinq.Test/SkipUntilTest.cs +++ b/MoreLinq.Test/SkipUntilTest.cs @@ -81,3 +81,17 @@ public int[] TestSkipUntil(int[] source, int min) } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void SkipUntilRightCanBuildWithSystemLinq() + { + new int[0].SkipUntil(x => true); + } + } +} diff --git a/MoreLinq.Test/SliceTest.cs b/MoreLinq.Test/SliceTest.cs index 18e80eeea..6a6fb3167 100644 --- a/MoreLinq.Test/SliceTest.cs +++ b/MoreLinq.Test/SliceTest.cs @@ -147,3 +147,18 @@ public void TestSliceOptimization(SourceKind sourceKind) } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void SliceCanBuildWithSystemLinq() + { + new int[0].Slice(2, 2); + } + } +} diff --git a/MoreLinq.Test/SortedMergeTest.cs b/MoreLinq.Test/SortedMergeTest.cs index 7c7cae4d3..9996f7396 100644 --- a/MoreLinq.Test/SortedMergeTest.cs +++ b/MoreLinq.Test/SortedMergeTest.cs @@ -191,3 +191,17 @@ public void TestSortedMergeAllSequencesDisposed() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void SortedMergeCanBuildWithSystemLinq() + { + new int[0].SortedMerge(OrderByDirection.Ascending, new int[0]); + } + } +} diff --git a/MoreLinq.Test/SplitTest.cs b/MoreLinq.Test/SplitTest.cs index b7f6044de..e79517fc7 100644 --- a/MoreLinq.Test/SplitTest.cs +++ b/MoreLinq.Test/SplitTest.cs @@ -60,3 +60,18 @@ public void SplitWithSeparatorSelectorUptoMaxCount() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void SplitCanBuildWithSystemLinq() + { + new int[0].Split(2); + } + } +} diff --git a/MoreLinq.Test/StartsWithTest.cs b/MoreLinq.Test/StartsWithTest.cs index 3aeb83e43..0b1bdb241 100644 --- a/MoreLinq.Test/StartsWithTest.cs +++ b/MoreLinq.Test/StartsWithTest.cs @@ -102,3 +102,18 @@ public void StartsWithUsesCollectionsCountToAvoidUnnecessaryIteration(SourceKind } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void StartsWithCanBuildWithSystemLinq() + { + new int[0].StartsWith(new int[0]); + } + } +} diff --git a/MoreLinq.Test/SubsetTest.cs b/MoreLinq.Test/SubsetTest.cs index 439631eaa..4a5da5d03 100644 --- a/MoreLinq.Test/SubsetTest.cs +++ b/MoreLinq.Test/SubsetTest.cs @@ -184,3 +184,17 @@ public void TestKSubsetExpectedResult() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void SubsetsCanBuildWithSystemLinq() + { + new int[0].Subsets(); + } + } +} diff --git a/MoreLinq.Test/TagFirstLastTest.cs b/MoreLinq.Test/TagFirstLastTest.cs index 0dcf9928c..335ecd1cc 100644 --- a/MoreLinq.Test/TagFirstLastTest.cs +++ b/MoreLinq.Test/TagFirstLastTest.cs @@ -56,3 +56,18 @@ public void TagFirstLastWithSourceSequenceOfThree() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void TagFirstLastCanBuildWithSystemLinq() + { + new int[0].TagFirstLast(ValueTuple.Create); + } + } +} diff --git a/MoreLinq.Test/TakeEveryTest.cs b/MoreLinq.Test/TakeEveryTest.cs index a10d9d5e9..34271ff79 100644 --- a/MoreLinq.Test/TakeEveryTest.cs +++ b/MoreLinq.Test/TakeEveryTest.cs @@ -70,3 +70,17 @@ public void TakeEveryIsLazy() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void TakeEveryCanBuildWithSystemLinq() + { + new int[0].TakeEvery(1); + } + } +} diff --git a/MoreLinq.Test/TakeLastTest.cs b/MoreLinq.Test/TakeLastTest.cs index 31548d411..98ec763cb 100644 --- a/MoreLinq.Test/TakeLastTest.cs +++ b/MoreLinq.Test/TakeLastTest.cs @@ -17,9 +17,11 @@ namespace MoreLinq.Test { - using NUnit.Framework; - using System.Collections.Generic; using System; + using System.Collections.Generic; + using NUnit.Framework; + + using static MoreLinq.Extensions.TakeLastExtension; [TestFixture] public class TakeLastTest @@ -80,3 +82,17 @@ static void AssertTakeLast(ICollection input, int count, Action false); + } + } +} diff --git a/MoreLinq.Test/ToArrayByIndexTest.cs b/MoreLinq.Test/ToArrayByIndexTest.cs index f5f06fb89..3bf5190c0 100644 --- a/MoreLinq.Test/ToArrayByIndexTest.cs +++ b/MoreLinq.Test/ToArrayByIndexTest.cs @@ -118,3 +118,17 @@ public void ToArrayByIndexOverwritesAtSameIndex() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ToArrayByIndexCanBuildWithSystemLinq() + { + new int[0].ToArrayByIndex(x => 1); + } + } +} diff --git a/MoreLinq.Test/ToDataTableTest.cs b/MoreLinq.Test/ToDataTableTest.cs index cfc95a224..aa112518a 100644 --- a/MoreLinq.Test/ToDataTableTest.cs +++ b/MoreLinq.Test/ToDataTableTest.cs @@ -205,3 +205,18 @@ public void ToDataTableIgnoresStaticMembers() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ToDataTableCanBuildWithSystemLinq() + { + new int[0].ToDataTable(); + } + } +} diff --git a/MoreLinq.Test/ToDelimitedStringTest.cs b/MoreLinq.Test/ToDelimitedStringTest.cs index 8d7eb1f0a..0397bf4bb 100644 --- a/MoreLinq.Test/ToDelimitedStringTest.cs +++ b/MoreLinq.Test/ToDelimitedStringTest.cs @@ -45,3 +45,17 @@ public void ToDelimitedStringWithNonEmptySequenceContainingNullsAtStart() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ToDelimitedStringCanBuildWithSystemLinq() + { + new int[0].ToDelimitedString(","); + } + } +} diff --git a/MoreLinq.Test/ToDictionaryTest.cs b/MoreLinq.Test/ToDictionaryTest.cs index e4a1f8ad6..27be3d7de 100644 --- a/MoreLinq.Test/ToDictionaryTest.cs +++ b/MoreLinq.Test/ToDictionaryTest.cs @@ -92,3 +92,19 @@ public void ToDictionaryWithCouplesWithComparer() } } } + +namespace Linq +{ + using System.Collections.Generic; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ToDictionaryCanBuildWithSystemLinq() + { + new (int, int)[0].ToDictionary(); + new KeyValuePair[0].ToDictionary(); + } + } +} diff --git a/MoreLinq.Test/ToHashSet.cs b/MoreLinq.Test/ToHashSet.cs new file mode 100644 index 000000000..6838db464 --- /dev/null +++ b/MoreLinq.Test/ToHashSet.cs @@ -0,0 +1,62 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2017 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq.Test +{ + using System; + using NUnit.Framework; + + using static MoreLinq.Extensions.ToHashSetExtension; + + [TestFixture] + public class ToHashSetTest + { + [Test] + public void ToHashSetBasic() + { + var hs = Enumerable.Range(1, 10) + .ToHashSet(); + + Assert.That(hs.Count, Is.EqualTo(10)); + } + + [Test] + public void ToHashSetComparer() + { + var hs = Enumerable.Range(1, 10) + .ToHashSet(EqualityComparer.Create((x, y) => x == y)); + + Assert.That(hs.Count, Is.EqualTo(10)); + } + } +} + +namespace Linq +{ + using System; + using System.Collections.Generic; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ToHashSetCanBuildWithSystemLinq() + { + new int[0].ToHashSet(); + } + } +} diff --git a/MoreLinq.Test/ToLookupTest.cs b/MoreLinq.Test/ToLookupTest.cs index c7403d0b5..bb9ed8a71 100644 --- a/MoreLinq.Test/ToLookupTest.cs +++ b/MoreLinq.Test/ToLookupTest.cs @@ -108,3 +108,20 @@ public void ToLookupWithCouplesWithComparer() } } } + +namespace Linq +{ + using System; + using System.Collections.Generic; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ToLookupCanBuildWithSystemLinq() + { + new (int, int)[0].ToLookup(); + new KeyValuePair[0].ToLookup(); + } + } +} diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index 10a49cfe1..d7597f2ac 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -140,3 +140,17 @@ public void WindowLeftWithWindowSizeSmallerThanSequence() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void WindowLeftCanBuildWithSystemLinq() + { + new int[0].WindowLeft(2); + } + } +} diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index 6f2aa27df..36adf81c0 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -138,3 +138,17 @@ public void WindowRightWithWindowSizeSmallerThanSequence() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void WindowRightCanBuildWithSystemLinq() + { + new int[0].WindowRight(2); + } + } +} diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index b9ae4bf18..45862a64f 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -177,3 +177,17 @@ public void TestWindowWindowsImmutability() } } } + +namespace Linq +{ + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void WindowCanBuildWithSystemLinq() + { + new int[0].Window(1); + } + } +} diff --git a/MoreLinq.Test/ZipLongestTest.cs b/MoreLinq.Test/ZipLongestTest.cs index f61470013..d950a3821 100644 --- a/MoreLinq.Test/ZipLongestTest.cs +++ b/MoreLinq.Test/ZipLongestTest.cs @@ -83,3 +83,18 @@ public void ZipLongestDisposesInnerSequencesCaseGetEnumeratorThrows() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ZipLongestCanBuildWithSystemLinq() + { + new int[0].ZipLongest(new int[0], ValueTuple.Create); + } + } +} diff --git a/MoreLinq.Test/ZipShortestTest.cs b/MoreLinq.Test/ZipShortestTest.cs index 3d2d8f80e..53143424a 100644 --- a/MoreLinq.Test/ZipShortestTest.cs +++ b/MoreLinq.Test/ZipShortestTest.cs @@ -114,3 +114,18 @@ public void ZipShortestDisposesInnerSequencesCaseGetEnumeratorThrows() } } } + +namespace Linq +{ + using System; + using System.Linq; + using MoreLinq; + + public static partial class BuildTest + { + public static void ZipShortestCanBuildWithSystemLinq() + { + new int[0].ZipShortest(new int[0], ValueTuple.Create); + } + } +} diff --git a/MoreLinq/Append.cs b/MoreLinq/Append.cs index e0800b364..b6aa4782d 100644 --- a/MoreLinq/Append.cs +++ b/MoreLinq/Append.cs @@ -31,7 +31,11 @@ static partial class MoreEnumerable /// A sequence consisting of the head elements and the given tail element. /// This operator uses deferred execution and streams its results. +#if NET471_OR_GREATER || NETSTANDARD1_6_OR_GREATER || NETCOREAPP2_0_OR_GREATER + public static IEnumerable Append(IEnumerable head, T tail) +#else public static IEnumerable Append(this IEnumerable head, T tail) +#endif { if (head == null) throw new ArgumentNullException(nameof(head)); return head is PendNode node @@ -50,6 +54,6 @@ public static IEnumerable Append(this IEnumerable head, T tail) [Obsolete("Use " + nameof(Append) + " instead.")] public static IEnumerable Concat(this IEnumerable head, T tail) => - head.Append(tail); + Append(head, tail); } } diff --git a/MoreLinq/Collections/Dictionary.cs b/MoreLinq/Collections/Dictionary.cs index 6366554ab..d6c61c34d 100644 --- a/MoreLinq/Collections/Dictionary.cs +++ b/MoreLinq/Collections/Dictionary.cs @@ -30,6 +30,8 @@ namespace MoreLinq.Collections sealed class Dictionary { + // Actual Dictionary<,> class won't allow TKey without notnull +#pragma warning disable 8714 readonly System.Collections.Generic.Dictionary _dict; (bool, TValue) _null; @@ -38,6 +40,7 @@ public Dictionary(IEqualityComparer comparer) _dict = new System.Collections.Generic.Dictionary(comparer); _null = default; } +#pragma warning restore 8714 public TValue this[TKey key] { @@ -68,4 +71,5 @@ public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) return _dict.TryGetValue(key, out value); } } + } diff --git a/MoreLinq/CountBy.cs b/MoreLinq/CountBy.cs index e671fee63..a1ac58308 100644 --- a/MoreLinq/CountBy.cs +++ b/MoreLinq/CountBy.cs @@ -91,7 +91,6 @@ void Loop(IEqualityComparer cmp) if (// key same as the previous? then re-use the index prevKey is (true, {} pk) - && cmp.GetHashCode(pk) == cmp.GetHashCode(key) && cmp.Equals(pk, key) // otherwise try & find index of the key || dic.TryGetValue(key, out index)) diff --git a/MoreLinq/DistinctBy.cs b/MoreLinq/DistinctBy.cs index a05faab82..1079ff814 100644 --- a/MoreLinq/DistinctBy.cs +++ b/MoreLinq/DistinctBy.cs @@ -38,10 +38,15 @@ static partial class MoreEnumerable /// A sequence consisting of distinct elements from the source sequence, /// comparing them by the specified key projection. +#if NET6_0_OR_GREATER + public static IEnumerable DistinctBy(IEnumerable source, + Func keySelector) +#else public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) +#endif { - return source.DistinctBy(keySelector, null); + return DistinctBy(source, keySelector, null); } /// @@ -62,8 +67,13 @@ public static IEnumerable DistinctBy(this IEnumerableA sequence consisting of distinct elements from the source sequence, /// comparing them by the specified key projection. +#if NET6_0_OR_GREATER + public static IEnumerable DistinctBy(IEnumerable source, + Func keySelector, IEqualityComparer? comparer) +#else public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector, IEqualityComparer? comparer) +#endif { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); diff --git a/MoreLinq/ExceptBy.cs b/MoreLinq/ExceptBy.cs index d3faa6ea8..0772338c5 100644 --- a/MoreLinq/ExceptBy.cs +++ b/MoreLinq/ExceptBy.cs @@ -42,9 +42,15 @@ static partial class MoreEnumerable /// A sequence of elements from whose key was not also a key for /// any element in . +#if NET6_0_OR_GREATER + public static IEnumerable ExceptBy(IEnumerable first, + IEnumerable second, + Func keySelector) +#else public static IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func keySelector) +#endif { return ExceptBy(first, second, keySelector, null); } @@ -70,10 +76,17 @@ public static IEnumerable ExceptBy(this IEnumerableA sequence of elements from whose key was not also a key for /// any element in . +#if NET6_0_OR_GREATER + public static IEnumerable ExceptBy(IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer? keyComparer) +#else public static IEnumerable ExceptBy(this IEnumerable first, IEnumerable second, Func keySelector, IEqualityComparer? keyComparer) +#endif { if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); diff --git a/MoreLinq/Experimental/Await.cs b/MoreLinq/Experimental/Await.cs index 018818f02..66107547f 100644 --- a/MoreLinq/Experimental/Await.cs +++ b/MoreLinq/Experimental/Await.cs @@ -529,8 +529,8 @@ void PostNotice(Notice notice, { var (error1, error2) = lastCriticalErrors; throw new Exception("One or more critical errors have occurred.", - error2 != null ? new AggregateException(error1, error2) - : new AggregateException(error1)); + error2 != null ? new AggregateException(error1!, error2) + : new AggregateException(error1!)); } var (kind, result, error) = notice.Current; diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index bfae169ac..4f7413482 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -3330,7 +3330,7 @@ public static partial class MaxByExtension public static IExtremaEnumerable MaxBy(this IEnumerable source, Func selector) - => MoreEnumerable.MaxBy(source, selector); + => MoreEnumerable.MaxElementsBy(source, selector); /// /// Returns the maximal elements of the given sequence, based on @@ -3351,7 +3351,56 @@ public static IExtremaEnumerable MaxBy(this IEnumerable< public static IExtremaEnumerable MaxBy(this IEnumerable source, Func selector, IComparer? comparer) - => MoreEnumerable.MaxBy(source, selector, comparer); + => MoreEnumerable.MaxElementsBy(source, selector, comparer); + + } + + /// MaxElementsBy extension. + + [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] + public static partial class MaxElementsByExtension + { + + /// + /// Returns the maximal elements of the given sequence, based on + /// the given projection. + /// + /// + /// This overload uses the default comparer for the projected type. + /// This operator uses deferred execution. The results are evaluated + /// and cached on first use to returned sequence. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// The sequence of maximal elements, according to the projection. + /// or is null + + public static IExtremaEnumerable MaxElementsBy(this IEnumerable source, + Func selector) + => MoreEnumerable.MaxElementsBy(source, selector); + + /// + /// Returns the maximal elements of the given sequence, based on + /// the given projection and the specified comparer for projected values. + /// + /// + /// This operator uses deferred execution. The results are evaluated + /// and cached on first use to returned sequence. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// Comparer to use to compare projected values + /// The sequence of maximal elements, according to the projection. + /// , + /// or is null + + public static IExtremaEnumerable MaxElementsBy(this IEnumerable source, + Func selector, IComparer? comparer) + => MoreEnumerable.MaxElementsBy(source, selector, comparer); } @@ -3378,7 +3427,7 @@ public static partial class MinByExtension public static IExtremaEnumerable MinBy(this IEnumerable source, Func selector) - => MoreEnumerable.MinBy(source, selector); + => MoreEnumerable.MinElementsBy(source, selector); /// /// Returns the minimal elements of the given sequence, based on @@ -3399,7 +3448,55 @@ public static IExtremaEnumerable MinBy(this IEnumerable< public static IExtremaEnumerable MinBy(this IEnumerable source, Func selector, IComparer? comparer) - => MoreEnumerable.MinBy(source, selector, comparer); + => MoreEnumerable.MinElementsBy(source, selector, comparer); + + } + + /// MinElementsBy extension. + + [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] + public static partial class MinElementsByExtension + { + /// + /// Returns the minimal elements of the given sequence, based on + /// the given projection. + /// + /// + /// This overload uses the default comparer for the projected type. + /// This operator uses deferred execution. The results are evaluated + /// and cached on first use to returned sequence. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// The sequence of minimal elements, according to the projection. + /// or is null + + public static IExtremaEnumerable MinElementsBy(this IEnumerable source, + Func selector) + => MoreEnumerable.MinElementsBy(source, selector); + + /// + /// Returns the minimal elements of the given sequence, based on + /// the given projection and the specified comparer for projected values. + /// + /// + /// This operator uses deferred execution. The results are evaluated + /// and cached on first use to returned sequence. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// Comparer to use to compare projected values + /// The sequence of minimal elements, according to the projection. + /// , + /// or is null + + public static IExtremaEnumerable MinElementsBy(this IEnumerable source, + Func selector, IComparer? comparer) + => MoreEnumerable.MinElementsBy(source, selector, comparer); } @@ -3937,7 +4034,7 @@ public static partial class PartialSortExtension { /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// /// Type of elements in the sequence. @@ -3954,7 +4051,7 @@ public static IEnumerable PartialSort(this IEnumerable source, int coun /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// An additional parameter specifies the direction of the sort /// @@ -3974,7 +4071,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. An additional parameter specifies how the /// elements compare to each other. /// @@ -3994,7 +4091,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// Additional parameters specify how the elements compare to each other and /// the direction of the sort. @@ -4024,7 +4121,7 @@ public static partial class PartialSortByExtension /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// /// Type of elements in the sequence. /// Type of keys. @@ -4044,7 +4141,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// An additional parameter specifies the direction of the sort /// /// Type of elements in the sequence. @@ -4066,7 +4163,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// An additional parameter specifies how the keys compare to each other. /// /// Type of elements in the sequence. @@ -4089,7 +4186,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// Additional parameters specify how the elements compare to each other and /// the direction of the sort. /// @@ -4547,6 +4644,7 @@ public static partial class RankExtension /// A sequence of position integers representing the ranks of the corresponding items in the sequence public static IEnumerable Rank(this IEnumerable source) + where TSource : notnull => MoreEnumerable.Rank(source); /// @@ -4558,6 +4656,7 @@ public static IEnumerable Rank(this IEnumerable source) /// A sequence of position integers representing the ranks of the corresponding items in the sequence public static IEnumerable Rank(this IEnumerable source, IComparer comparer) + where TSource : notnull => MoreEnumerable.Rank(source, comparer); } @@ -4578,6 +4677,7 @@ public static partial class RankByExtension /// A sequence of position integers representing the ranks of the corresponding items in the sequence public static IEnumerable RankBy(this IEnumerable source, Func keySelector) + where TSource : notnull => MoreEnumerable.RankBy(source, keySelector); /// @@ -4591,6 +4691,7 @@ public static IEnumerable RankBy(this IEnumerable s /// A sequence of position integers representing the ranks of the corresponding items in the sequence public static IEnumerable RankBy(this IEnumerable source, Func keySelector, IComparer? comparer) + where TSource : notnull => MoreEnumerable.RankBy(source, keySelector, comparer); } @@ -6338,7 +6439,8 @@ public static partial class ToDictionaryExtension /// mapped to their keys. /// - public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source) => MoreEnumerable.ToDictionary(source); + public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source) + where TKey : notnull => MoreEnumerable.ToDictionary(source); /// /// Creates a from a sequence of /// elements. @@ -6351,7 +6453,8 @@ public static partial class ToDictionaryExtension /// mapped to their keys. /// - public static Dictionary ToDictionary(this IEnumerable> source) => MoreEnumerable.ToDictionary(source); + public static Dictionary ToDictionary(this IEnumerable> source) + where TKey : notnull => MoreEnumerable.ToDictionary(source); /// /// Creates a from a sequence of @@ -6369,6 +6472,7 @@ public static partial class ToDictionaryExtension public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source, IEqualityComparer? comparer) + where TKey : notnull => MoreEnumerable.ToDictionary(source, comparer); /// @@ -6387,6 +6491,7 @@ public static Dictionary ToDictionary(this IEnumerab public static Dictionary ToDictionary(this IEnumerable> source, IEqualityComparer? comparer) + where TKey : notnull => MoreEnumerable.ToDictionary(source, comparer); } diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index 098c15f00..43d21f393 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -22,154 +22,10 @@ namespace MoreLinq using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; - - /// - /// Exposes the enumerator, which supports iteration over a sequence of - /// some extremum property (maximum or minimum) of a specified type. - /// - /// The type of objects to enumerate. - - public interface IExtremaEnumerable : IEnumerable - { - /// - /// Returns a specified number of contiguous elements from the start of - /// the sequence. - /// - /// The number of elements to return. - /// - /// An that contains the specified number - /// of elements from the start of the input sequence. - /// - - IEnumerable Take(int count); - - /// - /// Returns a specified number of contiguous elements at the end of the - /// sequence. - /// - /// The number of elements to return. - /// - /// An that contains the specified number - /// of elements at the end of the input sequence. - /// - - IEnumerable TakeLast(int count); - } + using System.Runtime.CompilerServices; static partial class MoreEnumerable { - /// - /// Returns the first element of a sequence. - /// - /// - /// The type of the elements of . - /// The input sequence. - /// - /// The input sequence is empty. - /// - /// The first element of the input sequence. - /// - - public static T First(this IExtremaEnumerable source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Take(1).AsEnumerable().First(); - } - - /// - /// Returns the first element of a sequence, or a default value if the - /// sequence contains no elements. - /// - /// - /// The type of the elements of . - /// The input sequence. - /// - /// Default value of type if source is empty; - /// otherwise, the first element in source. - /// - - [return: MaybeNull] - public static T FirstOrDefault(this IExtremaEnumerable source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Take(1).AsEnumerable().FirstOrDefault(); - } - - /// - /// Returns the last element of a sequence. - /// - /// - /// The type of the elements of . - /// The input sequence. - /// - /// The input sequence is empty. - /// - /// The last element of the input sequence. - /// - - public static T Last(this IExtremaEnumerable source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.TakeLast(1).AsEnumerable().Last(); - } - - /// - /// Returns the last element of a sequence, or a default value if the - /// sequence contains no elements. - /// - /// - /// The type of the elements of . - /// The input sequence. - /// - /// Default value of type if source is empty; - /// otherwise, the last element in source. - /// - - [return: MaybeNull] - public static T LastOrDefault(this IExtremaEnumerable source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.TakeLast(1).AsEnumerable().LastOrDefault(); - } - - /// - /// Returns the only element of a sequence, and throws an exception if - /// there is not exactly one element in the sequence. - /// - /// - /// The type of the elements of . - /// The input sequence. - /// - /// The input sequence contains more than one element. - /// - /// The single element of the input sequence. - /// - - public static T Single(this IExtremaEnumerable source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Take(2).AsEnumerable().Single(); - } - - /// - /// Returns the only element of a sequence, or a default value if the - /// sequence is empty; this method throws an exception if there is more - /// than one element in the sequence. - /// - /// - /// The type of the elements of . - /// The input sequence. - /// - /// The single element of the input sequence, or default value of type - /// if the sequence contains no elements. - /// - - [return: MaybeNull] - public static T SingleOrDefault(this IExtremaEnumerable source) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - return source.Take(2).AsEnumerable().SingleOrDefault(); - } /// /// Returns the maximal elements of the given sequence, based on @@ -187,11 +43,17 @@ public static T SingleOrDefault(this IExtremaEnumerable source) /// The sequence of maximal elements, according to the projection. /// or is null + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#if NET6_0_OR_GREATER + [Obsolete("MaxBy() conflicts with new .NET Core method. Use MaxElementsBy() instead.")] + public static IExtremaEnumerable MaxBy(IEnumerable source, + Func selector) +#else + [Obsolete("MaxBy() conflicts with new .NET Core method. Use MaxElementsBy() instead.")] public static IExtremaEnumerable MaxBy(this IEnumerable source, Func selector) - { - return source.MaxBy(selector, null); - } +#endif + => MaxElementsBy(source, selector); /// /// Returns the maximal elements of the given sequence, based on @@ -210,159 +72,16 @@ public static IExtremaEnumerable MaxBy(this IEnumerable< /// , /// or is null + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#if NET6_0_OR_GREATER + [Obsolete("MaxBy() conflicts with new .NET Core method. Use MaxElementsBy() instead.")] + public static IExtremaEnumerable MaxBy(IEnumerable source, + Func selector, IComparer? comparer) +#else + [Obsolete("MaxBy() conflicts with new .NET Core method. Use MaxElementsBy() instead.")] public static IExtremaEnumerable MaxBy(this IEnumerable source, Func selector, IComparer? comparer) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (selector == null) throw new ArgumentNullException(nameof(selector)); - - comparer ??= Comparer.Default; - return new ExtremaEnumerable(source, selector, (x, y) => comparer.Compare(x, y)); - } - - sealed class ExtremaEnumerable : IExtremaEnumerable - { - readonly IEnumerable _source; - readonly Func _selector; - readonly Func _comparer; - - public ExtremaEnumerable(IEnumerable source, Func selector, Func comparer) - { - _source = source; - _selector = selector; - _comparer = comparer; - } - - public IEnumerator GetEnumerator() => - ExtremaBy(_source, Extrema.First, null, _selector, _comparer).GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => - GetEnumerator(); - - public IEnumerable Take(int count) => - count switch - { - 0 => Enumerable.Empty(), - 1 => ExtremaBy(_source, Extremum.First, 1 , _selector, _comparer), - _ => ExtremaBy(_source, Extrema.First , count, _selector, _comparer) - }; - - public IEnumerable TakeLast(int count) => - count switch - { - 0 => Enumerable.Empty(), - 1 => ExtremaBy(_source, Extremum.Last, 1 , _selector, _comparer), - _ => ExtremaBy(_source, Extrema.Last , count, _selector, _comparer) - }; - - static class Extrema - { - public static readonly Extrema? , T> First = new FirstExtrema(); - public static readonly Extrema?, T> Last = new LastExtrema(); - - sealed class FirstExtrema : Extrema?, T> - { - public override List? New() => null; - public override void Restart(ref List? store) => store = null; - public override IEnumerable GetEnumerable(List? store) => store ?? Enumerable.Empty(); - - public override void Add(ref List? store, int? limit, T item) - { - if (limit == null || store is null || store.Count < limit) - (store ??= new List()).Add(item); - } - } - - sealed class LastExtrema : Extrema?, T> - { - public override Queue? New() => null; - public override void Restart(ref Queue? store) => store = null; - public override IEnumerable GetEnumerable(Queue? store) => store ?? Enumerable.Empty(); - - public override void Add(ref Queue? store, int? limit, T item) - { - if (limit is {} n && store is {} queue && queue.Count == n) - queue.Dequeue(); - (store ??= new Queue()).Enqueue(item); - } - } - } - - sealed class Extremum : Extrema<(bool, T), T> - { - public static readonly Extrema<(bool, T), T> First = new Extremum(false); - public static readonly Extrema<(bool, T), T> Last = new Extremum(true); - - readonly bool _poppable; - Extremum(bool poppable) => _poppable = poppable; - - public override (bool, T) New() => default; - public override void Restart(ref (bool, T) store) => store = default; - - public override IEnumerable GetEnumerable((bool, T) store) => - store is (true, var item) ? Enumerable.Repeat(item, 1) : Enumerable.Empty(); - - public override void Add(ref (bool, T) store, int? limit, T item) - { - if (!_poppable && store is (true, _)) - return; - store = (true, item); - } - } - } - - // > In mathematical analysis, the maxima and minima (the respective - // > plurals of maximum and minimum) of a function, known collectively - // > as extrema (the plural of extremum), ... - // > - // > - https://en.wikipedia.org/wiki/Maxima_and_minima - - static IEnumerable ExtremaBy( - this IEnumerable source, - Extrema extrema, int? limit, - Func selector, Func comparer) - { - foreach (var item in Extrema()) - yield return item; - - IEnumerable Extrema() - { - using var e = source.GetEnumerator(); - - if (!e.MoveNext()) - return new List(); - - var store = extrema.New(); - extrema.Add(ref store, limit, e.Current); - var extremaKey = selector(e.Current); - - while (e.MoveNext()) - { - var item = e.Current; - var key = selector(item); - var comparison = comparer(key, extremaKey); - if (comparison > 0) - { - extrema.Restart(ref store); - extrema.Add(ref store, limit, item); - extremaKey = key; - } - else if (comparison == 0) - { - extrema.Add(ref store, limit, item); - } - } - - return extrema.GetEnumerable(store); - } - } - - abstract class Extrema - { - public abstract TStore New(); - public abstract void Restart(ref TStore store); - public abstract IEnumerable GetEnumerable(TStore store); - public abstract void Add(ref TStore store, int? limit, T item); - } +#endif + => MaxElementsBy(source, selector, comparer); } } diff --git a/MoreLinq/MaxElementsBy.cs b/MoreLinq/MaxElementsBy.cs new file mode 100644 index 000000000..1b594bdb3 --- /dev/null +++ b/MoreLinq/MaxElementsBy.cs @@ -0,0 +1,368 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + + /// + /// Exposes the enumerator, which supports iteration over a sequence of + /// some extremum property (maximum or minimum) of a specified type. + /// + /// The type of objects to enumerate. + + public interface IExtremaEnumerable : IEnumerable + { + /// + /// Returns a specified number of contiguous elements from the start of + /// the sequence. + /// + /// The number of elements to return. + /// + /// An that contains the specified number + /// of elements from the start of the input sequence. + /// + + IEnumerable Take(int count); + + /// + /// Returns a specified number of contiguous elements at the end of the + /// sequence. + /// + /// The number of elements to return. + /// + /// An that contains the specified number + /// of elements at the end of the input sequence. + /// + + IEnumerable TakeLast(int count); + } + + static partial class MoreEnumerable + { + /// + /// Returns the first element of a sequence. + /// + /// + /// The type of the elements of . + /// The input sequence. + /// + /// The input sequence is empty. + /// + /// The first element of the input sequence. + /// + + public static T First(this IExtremaEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Take(1).AsEnumerable().First(); + } + + /// + /// Returns the first element of a sequence, or a default value if the + /// sequence contains no elements. + /// + /// + /// The type of the elements of . + /// The input sequence. + /// + /// Default value of type if source is empty; + /// otherwise, the first element in source. + /// + + [return: MaybeNull] + public static T FirstOrDefault(this IExtremaEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Take(1).AsEnumerable().FirstOrDefault(); + } + + /// + /// Returns the last element of a sequence. + /// + /// + /// The type of the elements of . + /// The input sequence. + /// + /// The input sequence is empty. + /// + /// The last element of the input sequence. + /// + + public static T Last(this IExtremaEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.TakeLast(1).AsEnumerable().Last(); + } + + /// + /// Returns the last element of a sequence, or a default value if the + /// sequence contains no elements. + /// + /// + /// The type of the elements of . + /// The input sequence. + /// + /// Default value of type if source is empty; + /// otherwise, the last element in source. + /// + + [return: MaybeNull] + public static T LastOrDefault(this IExtremaEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.TakeLast(1).AsEnumerable().LastOrDefault(); + } + + /// + /// Returns the only element of a sequence, and throws an exception if + /// there is not exactly one element in the sequence. + /// + /// + /// The type of the elements of . + /// The input sequence. + /// + /// The input sequence contains more than one element. + /// + /// The single element of the input sequence. + /// + + public static T Single(this IExtremaEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Take(2).AsEnumerable().Single(); + } + + /// + /// Returns the only element of a sequence, or a default value if the + /// sequence is empty; this method throws an exception if there is more + /// than one element in the sequence. + /// + /// + /// The type of the elements of . + /// The input sequence. + /// + /// The single element of the input sequence, or default value of type + /// if the sequence contains no elements. + /// + + [return: MaybeNull] + public static T SingleOrDefault(this IExtremaEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + return source.Take(2).AsEnumerable().SingleOrDefault(); + } + + /// + /// Returns the maximal elements of the given sequence, based on + /// the given projection. + /// + /// + /// This overload uses the default comparer for the projected type. + /// This operator uses deferred execution. The results are evaluated + /// and cached on first use to returned sequence. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// The sequence of maximal elements, according to the projection. + /// or is null + + public static IExtremaEnumerable MaxElementsBy(this IEnumerable source, + Func selector) + { + return MaxElementsBy(source, selector, null); + } + + /// + /// Returns the maximal elements of the given sequence, based on + /// the given projection and the specified comparer for projected values. + /// + /// + /// This operator uses deferred execution. The results are evaluated + /// and cached on first use to returned sequence. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// Comparer to use to compare projected values + /// The sequence of maximal elements, according to the projection. + /// , + /// or is null + + public static IExtremaEnumerable MaxElementsBy(this IEnumerable source, + Func selector, IComparer? comparer) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (selector == null) throw new ArgumentNullException(nameof(selector)); + + comparer ??= Comparer.Default; + return new ExtremaEnumerable(source, selector, (x, y) => comparer.Compare(x, y)); + } + + sealed class ExtremaEnumerable : IExtremaEnumerable + { + readonly IEnumerable _source; + readonly Func _selector; + readonly Func _comparer; + + public ExtremaEnumerable(IEnumerable source, Func selector, Func comparer) + { + _source = source; + _selector = selector; + _comparer = comparer; + } + + public IEnumerator GetEnumerator() => + ExtremaBy(_source, Extrema.First, null, _selector, _comparer).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => + GetEnumerator(); + + public IEnumerable Take(int count) => + count switch + { + 0 => Enumerable.Empty(), + 1 => ExtremaBy(_source, Extremum.First, 1, _selector, _comparer), + _ => ExtremaBy(_source, Extrema.First, count, _selector, _comparer) + }; + + public IEnumerable TakeLast(int count) => + count switch + { + 0 => Enumerable.Empty(), + 1 => ExtremaBy(_source, Extremum.Last, 1, _selector, _comparer), + _ => ExtremaBy(_source, Extrema.Last, count, _selector, _comparer) + }; + + static class Extrema + { + public static readonly Extrema?, T> First = new FirstExtrema(); + public static readonly Extrema?, T> Last = new LastExtrema(); + + sealed class FirstExtrema : Extrema?, T> + { + public override List? New() => null; + public override void Restart(ref List? store) => store = null; + public override IEnumerable GetEnumerable(List? store) => store ?? Enumerable.Empty(); + + public override void Add(ref List? store, int? limit, T item) + { + if (limit == null || store is null || store.Count < limit) + (store ??= new List()).Add(item); + } + } + + sealed class LastExtrema : Extrema?, T> + { + public override Queue? New() => null; + public override void Restart(ref Queue? store) => store = null; + public override IEnumerable GetEnumerable(Queue? store) => store ?? Enumerable.Empty(); + + public override void Add(ref Queue? store, int? limit, T item) + { + if (limit is { } n && store is { } queue && queue.Count == n) + queue.Dequeue(); + (store ??= new Queue()).Enqueue(item); + } + } + } + + sealed class Extremum : Extrema<(bool, T), T> + { + public static readonly Extrema<(bool, T), T> First = new Extremum(false); + public static readonly Extrema<(bool, T), T> Last = new Extremum(true); + + readonly bool _poppable; + Extremum(bool poppable) => _poppable = poppable; + + public override (bool, T) New() => default; + public override void Restart(ref (bool, T) store) => store = default; + + public override IEnumerable GetEnumerable((bool, T) store) => + store is (true, var item) ? Enumerable.Repeat(item, 1) : Enumerable.Empty(); + + public override void Add(ref (bool, T) store, int? limit, T item) + { + if (!_poppable && store is (true, _)) + return; + store = (true, item); + } + } + } + + // > In mathematical analysis, the maxima and minima (the respective + // > plurals of maximum and minimum) of a function, known collectively + // > as extrema (the plural of extremum), ... + // > + // > - https://en.wikipedia.org/wiki/Maxima_and_minima + + static IEnumerable ExtremaBy( + this IEnumerable source, + Extrema extrema, int? limit, + Func selector, Func comparer) + { + foreach (var item in Extrema()) + yield return item; + + IEnumerable Extrema() + { + using var e = source.GetEnumerator(); + + if (!e.MoveNext()) + return new List(); + + var store = extrema.New(); + extrema.Add(ref store, limit, e.Current); + var extremaKey = selector(e.Current); + + while (e.MoveNext()) + { + var item = e.Current; + var key = selector(item); + var comparison = comparer(key, extremaKey); + if (comparison > 0) + { + extrema.Restart(ref store); + extrema.Add(ref store, limit, item); + extremaKey = key; + } + else if (comparison == 0) + { + extrema.Add(ref store, limit, item); + } + } + + return extrema.GetEnumerable(store); + } + } + + abstract class Extrema + { + public abstract TStore New(); + public abstract void Restart(ref TStore store); + public abstract IEnumerable GetEnumerable(TStore store); + public abstract void Add(ref TStore store, int? limit, T item); + } + } +} diff --git a/MoreLinq/MinBy.cs b/MoreLinq/MinBy.cs index 625d698df..67a0634da 100644 --- a/MoreLinq/MinBy.cs +++ b/MoreLinq/MinBy.cs @@ -19,6 +19,7 @@ namespace MoreLinq { using System; using System.Collections.Generic; + using System.Runtime.CompilerServices; static partial class MoreEnumerable { @@ -38,11 +39,17 @@ static partial class MoreEnumerable /// The sequence of minimal elements, according to the projection. /// or is null + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#if NET6_0_OR_GREATER + [Obsolete("MinBy() conflicts with new .NET Core method. Use MinElementsBy() instead.")] + public static IExtremaEnumerable MinBy(IEnumerable source, + Func selector) +#else + [Obsolete("MinBy() conflicts with new .NET Core method. Use MinElementsBy() instead.")] public static IExtremaEnumerable MinBy(this IEnumerable source, Func selector) - { - return source.MinBy(selector, null); - } +#endif + => MinElementsBy(source, selector); /// /// Returns the minimal elements of the given sequence, based on @@ -61,14 +68,16 @@ public static IExtremaEnumerable MinBy(this IEnumerable< /// , /// or is null + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#if NET6_0_OR_GREATER + [Obsolete("MinBy() conflicts with new .NET Core method. Use MinElementsBy() instead.")] + public static IExtremaEnumerable MinBy(IEnumerable source, + Func selector, IComparer? comparer) +#else + [Obsolete("MinBy() conflicts with new .NET Core method. Use MinElementsBy() instead.")] public static IExtremaEnumerable MinBy(this IEnumerable source, Func selector, IComparer? comparer) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (selector == null) throw new ArgumentNullException(nameof(selector)); - - comparer ??= Comparer.Default; - return new ExtremaEnumerable(source, selector, (x, y) => -Math.Sign(comparer.Compare(x, y))); - } +#endif + => MinElementsBy(source, selector, comparer); } } diff --git a/MoreLinq/MinElementsBy.cs b/MoreLinq/MinElementsBy.cs new file mode 100644 index 000000000..80228251e --- /dev/null +++ b/MoreLinq/MinElementsBy.cs @@ -0,0 +1,74 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns the minimal elements of the given sequence, based on + /// the given projection. + /// + /// + /// This overload uses the default comparer for the projected type. + /// This operator uses deferred execution. The results are evaluated + /// and cached on first use to returned sequence. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// The sequence of minimal elements, according to the projection. + /// or is null + + public static IExtremaEnumerable MinElementsBy(this IEnumerable source, + Func selector) + { + return MinElementsBy(source, selector, null); + } + + /// + /// Returns the minimal elements of the given sequence, based on + /// the given projection and the specified comparer for projected values. + /// + /// + /// This operator uses deferred execution. The results are evaluated + /// and cached on first use to returned sequence. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// Comparer to use to compare projected values + /// The sequence of minimal elements, according to the projection. + /// , + /// or is null + + public static IExtremaEnumerable MinElementsBy(this IEnumerable source, + Func selector, IComparer? comparer) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (selector == null) throw new ArgumentNullException(nameof(selector)); + + comparer ??= Comparer.Default; + return new ExtremaEnumerable(source, selector, (x, y) => -Math.Sign(comparer.Compare(x, y))); + } + } +} diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 5a1689b90..24f074d16 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -117,9 +117,9 @@ $([System.Text.RegularExpressions.Regex]::Replace($(Copyright), `\s+`, ` `).Trim()) MoreLINQ en-US - 3.3.2 + 4.0.0 MoreLINQ Developers. - net451;netstandard1.0;netstandard2.0 + net451;net471;net472;netstandard1.0;netstandard1.6;netstandard2.0;netstandard2.1;netcoreapp2.0;net6.0 enable @@ -145,6 +145,7 @@ false false $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + False @@ -180,27 +181,27 @@ - + - + $(DefineConstants);MORELINQ - - $(DefineConstants);MORELINQ;NO_SERIALIZATION_ATTRIBUTES;NO_EXCEPTION_SERIALIZATION;NO_TRACING;NO_COM;NO_ASYNC + + $(DefineConstants);NO_SERIALIZATION_ATTRIBUTES;NO_EXCEPTION_SERIALIZATION;NO_TRACING;NO_COM;NO_ASYNC - + - + diff --git a/MoreLinq/PartialSort.cs b/MoreLinq/PartialSort.cs index 60076133a..6c76aec3b 100644 --- a/MoreLinq/PartialSort.cs +++ b/MoreLinq/PartialSort.cs @@ -25,7 +25,7 @@ static partial class MoreEnumerable { /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// /// Type of elements in the sequence. @@ -44,7 +44,7 @@ public static IEnumerable PartialSort(this IEnumerable source, int coun /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// An additional parameter specifies the direction of the sort /// @@ -66,7 +66,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. An additional parameter specifies how the /// elements compare to each other. /// @@ -89,7 +89,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// where each element is its key, and + /// where each element is its key, and /// in a single operation. /// Additional parameters specify how the elements compare to each other and /// the direction of the sort. @@ -116,7 +116,7 @@ public static IEnumerable PartialSort(this IEnumerable source, /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// /// Type of elements in the sequence. /// Type of keys. @@ -138,7 +138,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// An additional parameter specifies the direction of the sort /// /// Type of elements in the sequence. @@ -162,7 +162,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// An additional parameter specifies how the keys compare to each other. /// /// Type of elements in the sequence. @@ -189,7 +189,7 @@ public static IEnumerable PartialSortBy( /// /// Combines , - /// and in a single operation. + /// and in a single operation. /// Additional parameters specify how the elements compare to each other and /// the direction of the sort. /// diff --git a/MoreLinq/Prepend.cs b/MoreLinq/Prepend.cs index 06f5a0f27..fb82e8538 100644 --- a/MoreLinq/Prepend.cs +++ b/MoreLinq/Prepend.cs @@ -41,7 +41,11 @@ static partial class MoreEnumerable /// The result variable, when iterated over, will yield /// 0, 1, 2 and 3, in turn. +#if NET471_OR_GREATER || NETSTANDARD1_6_OR_GREATER || NETCOREAPP2_0_OR_GREATER + public static IEnumerable Prepend(IEnumerable source, TSource value) +#else public static IEnumerable Prepend(this IEnumerable source, TSource value) +#endif { if (source == null) throw new ArgumentNullException(nameof(source)); return source is PendNode node diff --git a/MoreLinq/Rank.cs b/MoreLinq/Rank.cs index 82b2d6076..fa3b626a8 100644 --- a/MoreLinq/Rank.cs +++ b/MoreLinq/Rank.cs @@ -31,6 +31,7 @@ public static partial class MoreEnumerable /// A sequence of position integers representing the ranks of the corresponding items in the sequence public static IEnumerable Rank(this IEnumerable source) + where TSource : notnull { return source.RankBy(x => x); } @@ -44,6 +45,7 @@ public static IEnumerable Rank(this IEnumerable source) /// A sequence of position integers representing the ranks of the corresponding items in the sequence public static IEnumerable Rank(this IEnumerable source, IComparer comparer) + where TSource : notnull { return source.RankBy(x => x, comparer); } @@ -58,6 +60,7 @@ public static IEnumerable Rank(this IEnumerable source, I /// A sequence of position integers representing the ranks of the corresponding items in the sequence public static IEnumerable RankBy(this IEnumerable source, Func keySelector) + where TSource : notnull { return RankBy(source, keySelector, null); } @@ -73,6 +76,7 @@ public static IEnumerable RankBy(this IEnumerable s /// A sequence of position integers representing the ranks of the corresponding items in the sequence public static IEnumerable RankBy(this IEnumerable source, Func keySelector, IComparer? comparer) + where TSource : notnull { if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); diff --git a/MoreLinq/ReverseComparer.cs b/MoreLinq/ReverseComparer.cs index bb46d3da5..935020d23 100644 --- a/MoreLinq/ReverseComparer.cs +++ b/MoreLinq/ReverseComparer.cs @@ -28,10 +28,13 @@ public ReverseComparer(IComparer? underlying) _underlying = underlying ?? Comparer.Default; } - public int Compare(T x, T y) + // Handle the discrepancy between IComparer.Compare(T x, T y) and IComparer.Compare(T? x, T? y) in diff framework versions +#pragma warning disable 8604 + public int Compare(T? x, T? y) { var result = _underlying.Compare(x, y); return result < 0 ? 1 : result > 0 ? -1 : 0; } +#pragma warning restore 8604 } } diff --git a/MoreLinq/ScanBy.cs b/MoreLinq/ScanBy.cs index b91a02dc4..128b3e5a1 100644 --- a/MoreLinq/ScanBy.cs +++ b/MoreLinq/ScanBy.cs @@ -99,7 +99,6 @@ IEnumerable> _(IEqualityComparer comparer) var state = // key same as the previous? then re-use the state prev is (true, {} pk, {} ps) - && comparer.GetHashCode(pk) == comparer.GetHashCode(key) && comparer.Equals(pk, key) ? ps : // otherwise try & find state of the key stateByKey.TryGetValue(key, out var ns) ? ns diff --git a/MoreLinq/SkipLast.cs b/MoreLinq/SkipLast.cs index cfee629b3..bab239164 100644 --- a/MoreLinq/SkipLast.cs +++ b/MoreLinq/SkipLast.cs @@ -33,7 +33,11 @@ static partial class MoreEnumerable /// An containing the source sequence elements except for the bypassed ones at the end. /// +#if NETSTANDARD2_1 || NETCOREAPP2_0_OR_GREATER + public static IEnumerable SkipLast(IEnumerable source, int count) +#else public static IEnumerable SkipLast(this IEnumerable source, int count) +#endif { if (source == null) throw new ArgumentNullException(nameof(source)); diff --git a/MoreLinq/TakeLast.cs b/MoreLinq/TakeLast.cs index dba1d1ebb..fcf4bf2f8 100644 --- a/MoreLinq/TakeLast.cs +++ b/MoreLinq/TakeLast.cs @@ -46,7 +46,11 @@ static partial class MoreEnumerable /// 56 and 78 in turn. /// +#if NETSTANDARD2_1 || NETCOREAPP2_0_OR_GREATER + public static IEnumerable TakeLast(IEnumerable source, int count) +#else public static IEnumerable TakeLast(this IEnumerable source, int count) +#endif { if (source == null) throw new ArgumentNullException(nameof(source)); diff --git a/MoreLinq/ToDataTable.cs b/MoreLinq/ToDataTable.cs index dfbf0eca2..c00581a02 100644 --- a/MoreLinq/ToDataTable.cs +++ b/MoreLinq/ToDataTable.cs @@ -170,6 +170,7 @@ MemberInfo GetAccessedMember(LambdaExpression lambda) // Check if the member expression is valid and is a "first level" // member access e.g. not a.b.c return body is MemberExpression memberExpression + && memberExpression.Expression != null && memberExpression.Expression.NodeType == ExpressionType.Parameter ? memberExpression.Member : throw new ArgumentException($"Illegal expression: {lambda}", nameof(lambda)); diff --git a/MoreLinq/ToDictionary.cs b/MoreLinq/ToDictionary.cs index 925dbea7f..47db8bf76 100644 --- a/MoreLinq/ToDictionary.cs +++ b/MoreLinq/ToDictionary.cs @@ -35,7 +35,8 @@ static partial class MoreEnumerable /// mapped to their keys. /// - public static Dictionary ToDictionary(this IEnumerable> source) => + public static Dictionary ToDictionary(this IEnumerable> source) + where TKey : notnull => source.ToDictionary(null); /// @@ -54,6 +55,7 @@ public static Dictionary ToDictionary(this IEnumerab public static Dictionary ToDictionary(this IEnumerable> source, IEqualityComparer? comparer) + where TKey : notnull { if (source == null) throw new ArgumentNullException(nameof(source)); return source.ToDictionary(e => e.Key, e => e.Value, comparer); @@ -72,7 +74,8 @@ public static Dictionary ToDictionary(this IEnumerab /// mapped to their keys. /// - public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source) => + public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source) + where TKey : notnull => source.ToDictionary(null); /// @@ -91,6 +94,7 @@ public static Dictionary ToDictionary(this IEnumerab public static Dictionary ToDictionary(this IEnumerable<(TKey Key, TValue Value)> source, IEqualityComparer? comparer) + where TKey : notnull { if (source == null) throw new ArgumentNullException(nameof(source)); return source.ToDictionary(e => e.Key, e => e.Value, comparer); diff --git a/MoreLinq/ToHashSet.cs b/MoreLinq/ToHashSet.cs index 7301ed357..1f6dc8bd1 100644 --- a/MoreLinq/ToHashSet.cs +++ b/MoreLinq/ToHashSet.cs @@ -35,9 +35,13 @@ static partial class MoreEnumerable /// This evaluates the input sequence completely. /// +#if NETSTANDARD2_1 || NET472_OR_GREATER || NETCOREAPP2_0_OR_GREATER + public static HashSet ToHashSet(IEnumerable source) +#else public static HashSet ToHashSet(this IEnumerable source) +#endif { - return source.ToHashSet(null); + return ToHashSet(source, null); } /// @@ -53,7 +57,11 @@ public static HashSet ToHashSet(this IEnumerable sour /// This evaluates the input sequence completely. /// +#if NETSTANDARD2_1 || NET472_OR_GREATER || NETCOREAPP2_0_OR_GREATER + public static HashSet ToHashSet(IEnumerable source, IEqualityComparer? comparer) +#else public static HashSet ToHashSet(this IEnumerable source, IEqualityComparer? comparer) +#endif { if (source == null) throw new ArgumentNullException(nameof(source)); return new HashSet(source, comparer); diff --git a/MoreLinq/Trace.cs b/MoreLinq/Trace.cs index ff9438a3f..9a3c5b06a 100644 --- a/MoreLinq/Trace.cs +++ b/MoreLinq/Trace.cs @@ -64,7 +64,7 @@ public static IEnumerable Trace(this IEnumerable sour return TraceImpl(source, string.IsNullOrEmpty(format) - ? (Func) (x => x == null ? string.Empty : x.ToString()) + ? (Func) (x => x?.ToString() ?? string.Empty) : (x => string.Format(format, x))); } diff --git a/bld/ExtensionsGenerator/Program.cs b/bld/ExtensionsGenerator/Program.cs index 3b8c983bb..247f7591c 100644 --- a/bld/ExtensionsGenerator/Program.cs +++ b/bld/ExtensionsGenerator/Program.cs @@ -14,21 +14,21 @@ // #endregion -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Reflection; + using System.Text.RegularExpressions; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; try -{ + { Run(args); return 0; } @@ -38,251 +38,261 @@ return 0xbad; } -static void Run(IEnumerable args) -{ - var dir = Directory.GetCurrentDirectory(); + static void Run(IEnumerable args) + { + var dir = Directory.GetCurrentDirectory(); - string includePattern = null; - string excludePattern = null; - var debug = false; - var usings = new List(); - var noClassLead = false; + string includePattern = null; + string excludePattern = null; + var debug = false; + var usings = new List(); + var noClassLead = false; - using (var arg = args.GetEnumerator()) - { - while (arg.MoveNext()) - { - switch (arg.Current) + using (var arg = args.GetEnumerator()) { - case "-i": - case "--include": - includePattern = Read(arg, MissingArgValue); - break; - case "-x": - case "--exclude": - excludePattern = Read(arg, MissingArgValue); - break; - case "-u": - case "--using": - usings.Add(Read(arg, MissingArgValue)); - break; - case "--no-class-lead": - noClassLead = true; - break; - case "-d": - case "--debug": - debug = true; - break; - case "": - continue; - default: - dir = arg.Current[0] != '-' - ? arg.Current - : throw new Exception("Invalid argument: " + arg.Current); - break; + while (arg.MoveNext()) + { + switch (arg.Current) + { + case "-i": + case "--include": + includePattern = Read(arg, MissingArgValue); + break; + case "-x": + case "--exclude": + excludePattern = Read(arg, MissingArgValue); + break; + case "-u": + case "--using": + usings.Add(Read(arg, MissingArgValue)); + break; + case "--no-class-lead": + noClassLead = true; + break; + case "-d": + case "--debug": + debug = true; + break; + case "": + continue; + default: + dir = arg.Current[0] != '-' + ? arg.Current + : throw new Exception("Invalid argument: " + arg.Current); + break; + } + } + + static Exception MissingArgValue() => + new InvalidOperationException("Missing argument value."); } - } - - static Exception MissingArgValue() => - new InvalidOperationException("Missing argument value."); - } - static Func - PredicateFromPattern(string pattern, bool @default) => - string.IsNullOrEmpty(pattern) - ? delegate { return @default; } - : new Func(new Regex(pattern).IsMatch); + static Func + PredicateFromPattern(string pattern, bool @default) => + string.IsNullOrEmpty(pattern) + ? delegate { return @default; } + : new Func(new Regex(pattern).IsMatch); - var includePredicate = PredicateFromPattern(includePattern, true); - var excludePredicate = PredicateFromPattern(excludePattern, false); + var includePredicate = PredicateFromPattern(includePattern, true); + var excludePredicate = PredicateFromPattern(excludePattern, false); var thisAssemblyName = typeof(TypeKey).GetTypeInfo().Assembly.GetName(); - // - // Type abbreviations are used to abbreviate all generic type - // parameters into a letter from the alphabet. So for example, a - // method with generic type parameters `TSource` and `TResult` will - // become `a` and `b`. This is used later for sorting and stabilizes - // the sort irrespective of how the type parameters are named or - // renamed in the source. - // + // + // Type abbreviations are used to abbreviate all generic type + // parameters into a letter from the alphabet. So for example, a + // method with generic type parameters `TSource` and `TResult` will + // become `a` and `b`. This is used later for sorting and stabilizes + // the sort irrespective of how the type parameters are named or + // renamed in the source. + // - var abbreviatedTypeNodes = Enumerable - .Range(0, 26) - .Select(a => (char) ('a' + a)) - .Select(ch => new SimpleTypeKey(ch.ToString())) - .ToArray(); + var abbreviatedTypeNodes = Enumerable + .Range(0, 26) + .Select(a => (char) ('a' + a)) + .Select(ch => new SimpleTypeKey(ch.ToString())) + .ToArray(); - var q = + var q = - from ms in new[] - { - from fp in Directory.EnumerateFiles(dir, "*.cs") - where !excludePredicate(fp) && includePredicate(fp) - orderby fp - // - // Find all class declarations where class name is - // `MoreEnumerable`. Note that this is irrespective of - // namespace, which is out of sheer laziness. - // - from cd in - CSharpSyntaxTree - .ParseText(File.ReadAllText(fp), CSharpParseOptions.Default.WithPreprocessorSymbols("MORELINQ")) - .GetRoot() - .SyntaxTree - .GetCompilationUnitRoot() - .DescendantNodes().OfType() - where (string) cd.Identifier.Value == "MoreEnumerable" - // - // Get all method declarations where method: - // - // - has at least one parameter - // - extends a type (first parameter uses the `this` modifier) - // - is public - // - isn't marked as being obsolete - // - from md in cd.DescendantNodes().OfType() - let mn = (string) md.Identifier.Value - where md.ParameterList.Parameters.Count > 0 - && md.ParameterList.Parameters.First().Modifiers.Any(m => (string)m.Value == "this") - && md.Modifiers.Any(m => (string)m.Value == "public") - && md.AttributeLists.SelectMany(al => al.Attributes).All(a => a.Name.ToString() != "Obsolete") - // - // Build a dictionary of type abbreviations (e.g. TSource -> a, - // TResult -> b, etc.) for the method's type parameters. If the - // method is non-generic, then this will be null! - // - let typeParameterAbbreviationByName = - md.TypeParameterList - ?.Parameters - .Select((e, i) => (Original: e.Identifier.ValueText, Alias: abbreviatedTypeNodes[i])) - .ToDictionary(e => e.Original, e => e.Alias) - // - // Put everything together. While we mostly care about the - // method declaration, the rest of the information is captured - // for the purpose of stabilizing the code generation order and - // debugging (--debug). - // - select new + from ms in new[] { - Syntax = md, - Name = md.Identifier.ToString(), - TypeParameterCount = md.TypeParameterList?.Parameters.Count ?? 0, - TypeParameterAbbreviationByName = typeParameterAbbreviationByName, - ParameterCount = md.ParameterList.Parameters.Count, - SortableParameterTypes = - from p in md.ParameterList.Parameters - select CreateTypeKey(p.Type, - n => typeParameterAbbreviationByName != null - && typeParameterAbbreviationByName.TryGetValue(n, out var a) ? a : null), + from fp in Directory.EnumerateFiles(dir, "*.cs") + where !excludePredicate(fp) && includePredicate(fp) + orderby fp + // + // Find all class declarations where class name is + // `MoreEnumerable`. Note that this is irrespective of + // namespace, which is out of sheer laziness. + // + from cd in + CSharpSyntaxTree + .ParseText(File.ReadAllText(fp), CSharpParseOptions.Default.WithPreprocessorSymbols("MORELINQ")) + .GetRoot() + .SyntaxTree + .GetCompilationUnitRoot() + .DescendantNodes().OfType() + where (string) cd.Identifier.Value == "MoreEnumerable" + // + // Get all method declarations where method: + // + // - has at least one parameter + // - extends a type (first parameter uses the `this` modifier) + // - is public + // - isn't marked as being obsolete + // + from md in cd.DescendantNodes().OfType() + let mn = (string) md.Identifier.Value + where md.ParameterList.Parameters.Count > 0 + && md.ParameterList.Parameters.First().Modifiers.Any(m => (string)m.Value == "this") + && md.Modifiers.Any(m => (string)m.Value == "public") + && md.AttributeLists.SelectMany(al => al.Attributes).All(a => a.Name.ToString() != "Obsolete") + // + // Build a dictionary of type abbreviations (e.g. TSource -> a, + // TResult -> b, etc.) for the method's type parameters. If the + // method is non-generic, then this will be null! + // + let typeParameterAbbreviationByName = + md.TypeParameterList + ?.Parameters + .Select((e, i) => (Original: e.Identifier.ValueText, Alias: abbreviatedTypeNodes[i])) + .ToDictionary(e => e.Original, e => e.Alias) + // + // Put everything together. While we mostly care about the + // method declaration, the rest of the information is captured + // for the purpose of stabilizing the code generation order and + // debugging (--debug). + // + select new + { + Syntax = md, + Name = md.Identifier.ToString(), + TypeParameterCount = md.TypeParameterList?.Parameters.Count ?? 0, + TypeParameterAbbreviationByName = typeParameterAbbreviationByName, + ParameterCount = md.ParameterList.Parameters.Count, + SortableParameterTypes = + from p in md.ParameterList.Parameters + select CreateTypeKey(p.Type, + n => typeParameterAbbreviationByName != null + && typeParameterAbbreviationByName.TryGetValue(n, out var a) ? a : null), + } } - } - from e in ms.Select((m, i) => (SourceOrder: i + 1, Method: m)) - orderby - e.Method.Name, - e.Method.TypeParameterCount, - e.Method.ParameterCount, - new TupleTypeKey(ImmutableList.CreateRange(e.Method.SortableParameterTypes)) - select new - { - e.Method, - e.SourceOrder, - }; + from e in ms.Select((m, i) => (SourceOrder: i + 1, Method: m)) + orderby + e.Method.Name, + e.Method.TypeParameterCount, + e.Method.ParameterCount, + new TupleTypeKey(ImmutableList.CreateRange(e.Method.SortableParameterTypes)) + select new + { + e.Method, + e.SourceOrder, + }; - q = q.ToArray(); + q = q.ToArray(); - if (debug) - { - var ms = - // - // Example of what this is designed to produce: - // - // 083: Lag(IEnumerable, int, Func) where a = TSource, b = TResult - // 084: Lag(IEnumerable, int, a, Func) where a = TSource, b = TResult - // 085: Lead(IEnumerable, int, Func) where a = TSource, b = TResult - // 086: Lead(IEnumerable, int, a, Func) where a = TSource, b = TResult - // - from e in q - let m = e.Method - select new + if (debug) { - m.Name, - - SourceOrder = e.SourceOrder.ToString("000", CultureInfo.InvariantCulture), - - TypeParameters = - m.TypeParameterCount == 0 - ? string.Empty - : "<" + string.Join(", ", from a in m.TypeParameterAbbreviationByName - select a.Value) + ">", - Parameters = - "(" + string.Join(", ", m.SortableParameterTypes) + ")", - - Abbreviations = - m.TypeParameterCount == 0 - ? string.Empty - : " where " + string.Join(", ", from a in m.TypeParameterAbbreviationByName - select a.Value + " = " + a.Key), + var ms = + // + // Example of what this is designed to produce: + // + // 083: Lag(IEnumerable, int, Func) where a = TSource, b = TResult + // 084: Lag(IEnumerable, int, a, Func) where a = TSource, b = TResult + // 085: Lead(IEnumerable, int, Func) where a = TSource, b = TResult + // 086: Lead(IEnumerable, int, a, Func) where a = TSource, b = TResult + // + from e in q + let m = e.Method + select new + { + m.Name, + + SourceOrder = e.SourceOrder.ToString("000", CultureInfo.InvariantCulture), + + TypeParameters = + m.TypeParameterCount == 0 + ? string.Empty + : "<" + string.Join(", ", from a in m.TypeParameterAbbreviationByName + select a.Value) + ">", + Parameters = + "(" + string.Join(", ", m.SortableParameterTypes) + ")", + + Abbreviations = + m.TypeParameterCount == 0 + ? string.Empty + : " where " + string.Join(", ", from a in m.TypeParameterAbbreviationByName + select a.Value + " = " + a.Key), + } + into e + select e.SourceOrder + ": " + + e.Name + e.TypeParameters + e.Parameters + e.Abbreviations; + + foreach (var m in ms) + Console.Error.WriteLine(m); } - into e - select e.SourceOrder + ": " - + e.Name + e.TypeParameters + e.Parameters + e.Abbreviations; - - foreach (var m in ms) - Console.Error.WriteLine(m); - } - var indent = new string(' ', 4); - var indent2 = indent + indent; - var indent3 = indent2 + indent; + var indent = new string(' ', 4); + var indent2 = indent + indent; + var indent3 = indent2 + indent; - var baseImports = new [] - { - "System", - "System.CodeDom.Compiler", - "System.Collections.Generic", - "System.Diagnostics.CodeAnalysis", - }; - - var imports = - from ns in baseImports.Concat(usings) - select indent + $"using {ns};"; - - var classes = - from md in q - select md.Method.Syntax into md - group md by (string) md.Identifier.Value into g - select new - { - Name = g.Key, - Overloads = - from md in g - select - MethodDeclaration(md.ReturnType, md.Identifier) - .WithAttributeLists(md.AttributeLists) - .WithModifiers(md.Modifiers) - .WithTypeParameterList(md.TypeParameterList) - .WithConstraintClauses(md.ConstraintClauses) - .WithParameterList(md.ParameterList) - .WithExpressionBody( - ArrowExpressionClause( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("MoreEnumerable"), - IdentifierName(md.Identifier)), - ArgumentList( - SeparatedList( - from p in md.ParameterList.Parameters - select Argument(IdentifierName(p.Identifier)), - Enumerable.Repeat(ParseToken(",").WithTrailingTrivia(Space), - md.ParameterList.Parameters.Count - 1)))) - .WithLeadingTrivia(Space)) - .WithLeadingTrivia(Whitespace(indent3))) - .WithSemicolonToken(ParseToken(";").WithTrailingTrivia(LineFeed)) - } - into m - select (!noClassLead ? $@" + var baseImports = new [] + { + "System", + "System.CodeDom.Compiler", + "System.Collections.Generic", + "System.Diagnostics.CodeAnalysis", + }; + + var imports = + from ns in baseImports.Concat(usings) + select indent + $"using {ns};"; + + var classes = + from md in q + select md.Method.Syntax into md + group md by (string) md.Identifier.Value into g + select new + { + Name = g.Key, + Overloads = + from md in g + select + MethodDeclaration(md.ReturnType, md.Identifier) + .WithAttributeLists(md.AttributeLists) + .WithModifiers( + new SyntaxTokenList( + md.Modifiers[0] + .WithLeadingTrivia( + from lt in md.Modifiers[0].LeadingTrivia + where lt.Kind() != SyntaxKind.DisabledTextTrivia + where lt.Kind() != SyntaxKind.IfDirectiveTrivia + where lt.Kind() != SyntaxKind.ElseDirectiveTrivia + where lt.Kind() != SyntaxKind.EndIfDirectiveTrivia + select lt), + md.Modifiers[1])) + .WithTypeParameterList(md.TypeParameterList) + .WithConstraintClauses(md.ConstraintClauses) + .WithParameterList(md.ParameterList) + .WithExpressionBody( + ArrowExpressionClause( + InvocationExpression( + MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + IdentifierName("MoreEnumerable"), + IdentifierName(md.Identifier)), + ArgumentList( + SeparatedList( + from p in md.ParameterList.Parameters + select Argument(IdentifierName(p.Identifier)), + Enumerable.Repeat(ParseToken(",").WithTrailingTrivia(Space), + md.ParameterList.Parameters.Count - 1)))) + .WithLeadingTrivia(Space)) + .WithLeadingTrivia(Whitespace(indent3))) + .WithSemicolonToken(ParseToken(";").WithTrailingTrivia(LineFeed)) + } + into m + select (!noClassLead ? $@" /// {m.Name} extension. [GeneratedCode(""{thisAssemblyName.Name}"", ""{thisAssemblyName.Version}"")]" : null) + $@" @@ -291,7 +301,7 @@ public static partial class {m.Name}Extension {string.Join(null, from mo in m.Overloads select mo.ToFullString())} }}"; - var template = $@" + var template = $@" #region License and Terms // MoreLINQ - Extensions to LINQ to Objects // @@ -326,142 +336,142 @@ namespace MoreLinq.Extensions {string.Join("\n", imports)} {string.Join("\n", classes)} }} - "; + "; - Console.WriteLine(template.Trim() - // normalize line endings - .Replace("\r", string.Empty) - .Replace("\n", Environment.NewLine)); -} + Console.WriteLine(template.Trim() + // normalize line endings + .Replace("\r", string.Empty) + .Replace("\n", Environment.NewLine)); + } static TypeKey CreateTypeKey(TypeSyntax root, - Func abbreviator = null) -{ - return Walk(root ?? throw new ArgumentNullException(nameof(root))); + Func abbreviator = null) + { + return Walk(root ?? throw new ArgumentNullException(nameof(root))); - TypeKey Walk(TypeSyntax ts) => ts switch - { - PredefinedTypeSyntax pts => new SimpleTypeKey(pts.ToString()), - NullableTypeSyntax nts => new NullableTypeKey(Walk(nts.ElementType)), - IdentifierNameSyntax ins => abbreviator?.Invoke(ins.Identifier.ValueText) - ?? new SimpleTypeKey(ins.ToString()), - GenericNameSyntax gns => - new GenericTypeKey(gns.Identifier.ToString(), - ImmutableList.CreateRange(gns.TypeArgumentList.Arguments.Select(Walk))), - ArrayTypeSyntax ats => - new ArrayTypeKey(Walk(ats.ElementType), - ImmutableList.CreateRange(from rs in ats.RankSpecifiers - select rs.Rank)), - TupleTypeSyntax tts => - new TupleTypeKey(ImmutableList.CreateRange(from te in tts.Elements - select Walk(te.Type))), - _ => throw new NotSupportedException("Unhandled type: " + ts) - }; -} + TypeKey Walk(TypeSyntax ts) => ts switch + { + PredefinedTypeSyntax pts => new SimpleTypeKey(pts.ToString()), + NullableTypeSyntax nts => new NullableTypeKey(Walk(nts.ElementType)), + IdentifierNameSyntax ins => abbreviator?.Invoke(ins.Identifier.ValueText) + ?? new SimpleTypeKey(ins.ToString()), + GenericNameSyntax gns => + new GenericTypeKey(gns.Identifier.ToString(), + ImmutableList.CreateRange(gns.TypeArgumentList.Arguments.Select(Walk))), + ArrayTypeSyntax ats => + new ArrayTypeKey(Walk(ats.ElementType), + ImmutableList.CreateRange(from rs in ats.RankSpecifiers + select rs.Rank)), + TupleTypeSyntax tts => + new TupleTypeKey(ImmutableList.CreateRange(from te in tts.Elements + select Walk(te.Type))), + _ => throw new NotSupportedException("Unhandled type: " + ts) + }; + } -static T Read(IEnumerator e, Func errorFactory = null) -{ - if (!e.MoveNext()) - throw errorFactory?.Invoke() ?? new InvalidOperationException(); - return e.Current; -} + static T Read(IEnumerator e, Func errorFactory = null) + { + if (!e.MoveNext()) + throw errorFactory?.Invoke() ?? new InvalidOperationException(); + return e.Current; + } -// -// Logical type nodes designed to be structurally sortable based on: -// -// - Type parameter count -// - Name -// - Array rank, if an array -// - Each type parameter (recursively) -// + // + // Logical type nodes designed to be structurally sortable based on: + // + // - Type parameter count + // - Name + // - Array rank, if an array + // - Each type parameter (recursively) + // -abstract class TypeKey : IComparable -{ - protected TypeKey(string name) => Name = name; + abstract class TypeKey : IComparable + { + protected TypeKey(string name) => Name = name; - public string Name { get; } - public abstract ImmutableList Parameters { get; } + public string Name { get; } + public abstract ImmutableList Parameters { get; } - public virtual int CompareTo(TypeKey other) - => ReferenceEquals(this, other) ? 0 - : other == null ? 1 + public virtual int CompareTo(TypeKey other) + => ReferenceEquals(this, other) ? 0 + : other == null ? 1 : Parameters.Count.CompareTo(other.Parameters.Count) is {} lc and not 0 ? lc : string.Compare(Name, other.Name, StringComparison.Ordinal) is {} nc and not 0 ? nc - : CompareParameters(other); + : CompareParameters(other); - protected virtual int CompareParameters(TypeKey other) => - Compare(Parameters, other.Parameters); + protected virtual int CompareParameters(TypeKey other) => + Compare(Parameters, other.Parameters); - protected static int Compare(IEnumerable a, IEnumerable b) => - a.Zip(b, (us, them) => (Us: us, Them: them)) - .Select(e => e.Us.CompareTo(e.Them)) - .FirstOrDefault(e => e != 0); -} + protected static int Compare(IEnumerable a, IEnumerable b) => + a.Zip(b, (us, them) => (Us: us, Them: them)) + .Select(e => e.Us.CompareTo(e.Them)) + .FirstOrDefault(e => e != 0); + } -sealed class SimpleTypeKey : TypeKey -{ - public SimpleTypeKey(string name) : base(name) {} - public override string ToString() => Name; - public override ImmutableList Parameters => ImmutableList.Empty; -} + sealed class SimpleTypeKey : TypeKey + { + public SimpleTypeKey(string name) : base(name) {} + public override string ToString() => Name; + public override ImmutableList Parameters => ImmutableList.Empty; + } -abstract class ParameterizedTypeKey : TypeKey -{ - protected ParameterizedTypeKey(string name, TypeKey parameter) : - this(name, ImmutableList.Create(parameter)) {} + abstract class ParameterizedTypeKey : TypeKey + { + protected ParameterizedTypeKey(string name, TypeKey parameter) : + this(name, ImmutableList.Create(parameter)) {} - protected ParameterizedTypeKey(string name, ImmutableList parameters) : - base(name) => Parameters = parameters; + protected ParameterizedTypeKey(string name, ImmutableList parameters) : + base(name) => Parameters = parameters; - public override ImmutableList Parameters { get; } -} + public override ImmutableList Parameters { get; } + } -sealed class GenericTypeKey : ParameterizedTypeKey -{ - public GenericTypeKey(string name, ImmutableList parameters) : - base(name, parameters) {} + sealed class GenericTypeKey : ParameterizedTypeKey + { + public GenericTypeKey(string name, ImmutableList parameters) : + base(name, parameters) {} - public override string ToString() => - Name + "<" + string.Join(", ", Parameters) + ">"; -} + public override string ToString() => + Name + "<" + string.Join(", ", Parameters) + ">"; + } -sealed class NullableTypeKey : ParameterizedTypeKey -{ - public NullableTypeKey(TypeKey underlying) : base("?", underlying) {} - public override string ToString() => Parameters.Single() + "?"; -} + sealed class NullableTypeKey : ParameterizedTypeKey + { + public NullableTypeKey(TypeKey underlying) : base("?", underlying) {} + public override string ToString() => Parameters.Single() + "?"; + } -sealed class TupleTypeKey : ParameterizedTypeKey -{ - public TupleTypeKey(ImmutableList parameters) : - base("()", parameters) {} + sealed class TupleTypeKey : ParameterizedTypeKey + { + public TupleTypeKey(ImmutableList parameters) : + base("()", parameters) {} - public override string ToString() => - "(" + string.Join(", ", Parameters) + ")"; -} + public override string ToString() => + "(" + string.Join(", ", Parameters) + ")"; + } -sealed class ArrayTypeKey : ParameterizedTypeKey -{ - public ArrayTypeKey(TypeKey element, IEnumerable ranks) : - base("[]", element) => Ranks = ImmutableList.CreateRange(ranks); + sealed class ArrayTypeKey : ParameterizedTypeKey + { + public ArrayTypeKey(TypeKey element, IEnumerable ranks) : + base("[]", element) => Ranks = ImmutableList.CreateRange(ranks); - public ImmutableList Ranks { get; } + public ImmutableList Ranks { get; } - public override string ToString() => - Parameters.Single() + string.Concat(from r in Ranks - select "[" + string.Concat(Enumerable.Repeat(",", r - 1)) + "]"); + public override string ToString() => + Parameters.Single() + string.Concat(from r in Ranks + select "[" + string.Concat(Enumerable.Repeat(",", r - 1)) + "]"); - protected override int CompareParameters(TypeKey other) - { - if (other is ArrayTypeKey a) + protected override int CompareParameters(TypeKey other) { + if (other is ArrayTypeKey a) + { if (Ranks.Count.CompareTo(a.Ranks.Count) is {} rlc and not 0) - return rlc; - if (Ranks.Zip(a.Ranks, (us, them) => (Us: us, Them: them)) + return rlc; + if (Ranks.Zip(a.Ranks, (us, them) => (Us: us, Them: them)) .Aggregate(0, (c, r) => c == 0 ? r.Us.CompareTo(r.Them) : c) is {} rc and not 0) - return rc; - } + return rc; + } - return base.CompareParameters(other); + return base.CompareParameters(other); + } } -} diff --git a/global.json b/global.json index a46dc9b06..fa44969c4 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,7 @@ { "sdk": { "version": "5.0.100", - "rollForward": "latestFeature" + "allowPrerelease": true, + "rollForward": "latestMajor" } }