From ee938c06c25185c9dda547fbfc63210d2a633831 Mon Sep 17 00:00:00 2001 From: Felipe Sateler Date: Fri, 12 May 2017 19:11:31 -0300 Subject: [PATCH 01/10] FallbackIfEmpty: preserve source and fallback collections if possible --- MoreLinq.Test/FallbackIfEmptyTest.cs | 22 +++++++++ MoreLinq/FallbackIfEmpty.cs | 69 ++++++++++++++-------------- 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/MoreLinq.Test/FallbackIfEmptyTest.cs b/MoreLinq.Test/FallbackIfEmptyTest.cs index 8a9b28d55..f79141b0d 100644 --- a/MoreLinq.Test/FallbackIfEmptyTest.cs +++ b/MoreLinq.Test/FallbackIfEmptyTest.cs @@ -39,6 +39,28 @@ public void FallbackIfEmptyWithEmptySequence() } [Test] + public void FallbackIfEmptyPreservesSourceCollectionIfPossible() + { + var source = new int[] { 1 }; + // ReSharper disable PossibleMultipleEnumeration + Assert.AreSame(source.FallbackIfEmpty(12), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23, 34), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56, 67), source); + // ReSharper restore PossibleMultipleEnumeration + } + + [Test] + public void FallbackIfEmptyPreservesFallbackCollectionIfPossible() + { + var source = new int[0]; + var fallback = new int[] { 1 }; + Assert.AreSame(source.FallbackIfEmpty(fallback), fallback); + Assert.AreSame(source.FallbackIfEmpty(fallback.AsEnumerable()), fallback); + } + public void FallbackIfEmptyWithEmptySequenceCollectionOptimized() { var source = LinqEnumerable.Empty(); diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index 40f1fe57a..e9a217643 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Linq; static partial class MoreEnumerable { @@ -162,45 +161,45 @@ static IEnumerable FallbackIfEmptyImpl(IEnumerable source, int? count, T fallback1, T fallback2, T fallback3, T fallback4, IEnumerable fallback) { - var collection = source as ICollection; - if (collection != null && collection.Count == 0) - { - // - // Replace the empty collection with an empty sequence and - // carry on. LINQ's Enumerable.Empty is implemented - // intelligently to return the same enumerator instance and so - // does not incur an allocation. However, the same cannot be - // said for a collection like an empty array or list. This - // permits the rest of the logic while keeping the call to - // source.GetEnumerator() cheap. - // - - source = Enumerable.Empty(); + if (source is ICollection collection) { + if (collection.Count == 0) { + return fallbackChooser(); + } + else { + return collection; + } } - using (var e = source.GetEnumerator()) + return nonCollectionIterator(); + + IEnumerable nonCollectionIterator() { - if (e.MoveNext()) - { - do { yield return e.Current; } - while (e.MoveNext()); - } - else - { - e.Dispose(); // eager disposal - if (count > 0 && count <= 4) - { - yield return fallback1; - if (count > 1) yield return fallback2; - if (count > 2) yield return fallback3; - if (count > 3) yield return fallback4; - } - else - { - foreach (var item in fallback) - yield return item; + using (var e = source.GetEnumerator()) { + if (e.MoveNext()) { + do { yield return e.Current; } + while (e.MoveNext()); + yield break; } } + + foreach (var item in fallbackChooser()) + yield return item; + } + IEnumerable fallbackChooser() + { + if (count > 0 && count <= 4) { + return instancesFallback(); + } + else { + return fallback; + } + } + IEnumerable instancesFallback() + { + yield return fallback1; + if (count > 1) yield return fallback2; + if (count > 2) yield return fallback3; + if (count > 3) yield return fallback4; } } } From a521afa1b55b09ba387ecabb62d3e1e413794284 Mon Sep 17 00:00:00 2001 From: Felipe Sateler Date: Tue, 8 Aug 2017 12:56:43 -0400 Subject: [PATCH 02/10] Brace style --- MoreLinq/FallbackIfEmpty.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index e9a217643..de8cf9bdb 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -161,11 +161,14 @@ static IEnumerable FallbackIfEmptyImpl(IEnumerable source, int? count, T fallback1, T fallback2, T fallback3, T fallback4, IEnumerable fallback) { - if (source is ICollection collection) { - if (collection.Count == 0) { + if (source is ICollection collection) + { + if (collection.Count == 0) + { return fallbackChooser(); } - else { + else + { return collection; } } @@ -174,8 +177,10 @@ static IEnumerable FallbackIfEmptyImpl(IEnumerable source, IEnumerable nonCollectionIterator() { - using (var e = source.GetEnumerator()) { - if (e.MoveNext()) { + using (var e = source.GetEnumerator()) + { + if (e.MoveNext()) + { do { yield return e.Current; } while (e.MoveNext()); yield break; @@ -187,10 +192,12 @@ IEnumerable nonCollectionIterator() } IEnumerable fallbackChooser() { - if (count > 0 && count <= 4) { + if (count > 0 && count <= 4) + { return instancesFallback(); } - else { + else + { return fallback; } } From f5cb2b508811c8a9cd290eb7c3d845fb55d7abf4 Mon Sep 17 00:00:00 2001 From: Felipe Sateler Date: Tue, 8 Aug 2017 13:09:26 -0400 Subject: [PATCH 03/10] Better names and localizacion of inner methods --- MoreLinq/FallbackIfEmpty.cs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index de8cf9bdb..de650103f 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -165,7 +165,7 @@ static IEnumerable FallbackIfEmptyImpl(IEnumerable source, { if (collection.Count == 0) { - return fallbackChooser(); + return FallbackChooser(); } else { @@ -173,9 +173,9 @@ static IEnumerable FallbackIfEmptyImpl(IEnumerable source, } } - return nonCollectionIterator(); + return _(); - IEnumerable nonCollectionIterator() + IEnumerable _() { using (var e = source.GetEnumerator()) { @@ -187,27 +187,22 @@ IEnumerable nonCollectionIterator() } } - foreach (var item in fallbackChooser()) + foreach (var item in FallbackChooser()) yield return item; } - IEnumerable fallbackChooser() + + IEnumerable FallbackChooser() { - if (count > 0 && count <= 4) - { - return instancesFallback(); - } - else + return (count <= 0 || count > 4) ? fallback : InstancesFallback(); + + IEnumerable InstancesFallback() { - return fallback; + yield return fallback1; + if (count > 1) yield return fallback2; + if (count > 2) yield return fallback3; + if (count > 3) yield return fallback4; } } - IEnumerable instancesFallback() - { - yield return fallback1; - if (count > 1) yield return fallback2; - if (count > 2) yield return fallback3; - if (count > 3) yield return fallback4; - } } } } From ccca164c20d889716ad54e22b75275e6beb24c4b Mon Sep 17 00:00:00 2001 From: Felipe Sateler Date: Tue, 8 Aug 2017 13:16:31 -0400 Subject: [PATCH 04/10] Turn FallbackIfEmptyPreservesSourceCollectionIfPossible into test cases --- MoreLinq.Test/FallbackIfEmptyTest.cs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/MoreLinq.Test/FallbackIfEmptyTest.cs b/MoreLinq.Test/FallbackIfEmptyTest.cs index f79141b0d..bd2068e6d 100644 --- a/MoreLinq.Test/FallbackIfEmptyTest.cs +++ b/MoreLinq.Test/FallbackIfEmptyTest.cs @@ -17,6 +17,7 @@ namespace MoreLinq.Test { + using System; using System.Linq; using NUnit.Framework; using LinqEnumerable = System.Linq.Enumerable; @@ -38,17 +39,26 @@ public void FallbackIfEmptyWithEmptySequence() // ReSharper restore PossibleMultipleEnumeration } - [Test] - public void FallbackIfEmptyPreservesSourceCollectionIfPossible() + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + [TestCase(4)] + [TestCase(5)] + [TestCase(6)] + public void FallbackIfEmptyPreservesSourceCollectionIfPossible(int length) { var source = new int[] { 1 }; // ReSharper disable PossibleMultipleEnumeration - Assert.AreSame(source.FallbackIfEmpty(12), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23, 34), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56), source); - Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56, 67), source); + switch (length) { + case 1: Assert.AreSame(source.FallbackIfEmpty(12), source); break; + case 2: Assert.AreSame(source.FallbackIfEmpty(12, 23), source); break; + case 3: Assert.AreSame(source.FallbackIfEmpty(12, 23, 34), source); break; + case 4: Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45), source); break; + case 5: Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56), source); break; + case 6: Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56, 67), source); break; + default: + throw new ArgumentOutOfRangeException(nameof(length)); + } // ReSharper restore PossibleMultipleEnumeration } From 1d9b052d88299a8785106aa75d192e33c280e236 Mon Sep 17 00:00:00 2001 From: Felipe Sateler Date: Thu, 10 Aug 2017 10:52:57 -0400 Subject: [PATCH 05/10] Use null check on count for choosing fallback --- MoreLinq/FallbackIfEmpty.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index de650103f..81e9ac2eb 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -154,7 +154,7 @@ public static IEnumerable FallbackIfEmpty(this IEnumerable source, IEnu { if (source == null) throw new ArgumentNullException(nameof(source)); if (fallback == null) throw new ArgumentNullException(nameof(fallback)); - return FallbackIfEmptyImpl(source, 0, default(T), default(T), default(T), default(T), fallback); + return FallbackIfEmptyImpl(source, null, default(T), default(T), default(T), default(T), fallback); } static IEnumerable FallbackIfEmptyImpl(IEnumerable source, @@ -193,7 +193,12 @@ IEnumerable _() IEnumerable FallbackChooser() { - return (count <= 0 || count > 4) ? fallback : InstancesFallback(); + switch (count) { + case null: return fallback; + case int n when (n >= 1 && n <= 4): return InstancesFallback(); + default: + throw new ArgumentOutOfRangeException(nameof(count), $"value {count} is outside the range"); + } IEnumerable InstancesFallback() { From bef6cfe4f8e5b55149038253d2ef43f3e81b1afd Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 10 Aug 2017 18:58:48 +0100 Subject: [PATCH 06/10] Code formatting --- MoreLinq/FallbackIfEmpty.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index 81e9ac2eb..7cd48b4fd 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -193,11 +193,11 @@ IEnumerable _() IEnumerable FallbackChooser() { - switch (count) { - case null: return fallback; - case int n when (n >= 1 && n <= 4): return InstancesFallback(); - default: - throw new ArgumentOutOfRangeException(nameof(count), $"value {count} is outside the range"); + switch (count) + { + case null: return fallback; + case int n when (n >= 1 && n <= 4): return InstancesFallback(); + default: throw new ArgumentOutOfRangeException(nameof(count), $"value {count} is outside the range"); } IEnumerable InstancesFallback() From 0952f8dbae3b3b6ddaa21f2f8af9d813cf1baa1b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 10 Aug 2017 18:59:15 +0100 Subject: [PATCH 07/10] Redundant parens --- MoreLinq/FallbackIfEmpty.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index 7cd48b4fd..45a00d582 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -196,7 +196,7 @@ IEnumerable FallbackChooser() switch (count) { case null: return fallback; - case int n when (n >= 1 && n <= 4): return InstancesFallback(); + case int n when n >= 1 && n <= 4: return InstancesFallback(); default: throw new ArgumentOutOfRangeException(nameof(count), $"value {count} is outside the range"); } From e2477749579fc7a8a60f7d7ff97ae375e536ea2a Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 10 Aug 2017 19:00:28 +0100 Subject: [PATCH 08/10] Put offending value into ArgumentOutOfRangeException --- MoreLinq/FallbackIfEmpty.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index 45a00d582..e95af6a89 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -197,7 +197,7 @@ IEnumerable FallbackChooser() { case null: return fallback; case int n when n >= 1 && n <= 4: return InstancesFallback(); - default: throw new ArgumentOutOfRangeException(nameof(count), $"value {count} is outside the range"); + default: throw new ArgumentOutOfRangeException(nameof(count), count, null); } IEnumerable InstancesFallback() From 52966cc41ae90c436626fb34280fc270be27c010 Mon Sep 17 00:00:00 2001 From: Felipe Sateler Date: Thu, 10 Aug 2017 15:15:59 -0400 Subject: [PATCH 09/10] Revert "Turn FallbackIfEmptyPreservesSourceCollectionIfPossible into test cases" This reverts commit ccca164c20d889716ad54e22b75275e6beb24c4b. --- MoreLinq.Test/FallbackIfEmptyTest.cs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/MoreLinq.Test/FallbackIfEmptyTest.cs b/MoreLinq.Test/FallbackIfEmptyTest.cs index bd2068e6d..f79141b0d 100644 --- a/MoreLinq.Test/FallbackIfEmptyTest.cs +++ b/MoreLinq.Test/FallbackIfEmptyTest.cs @@ -17,7 +17,6 @@ namespace MoreLinq.Test { - using System; using System.Linq; using NUnit.Framework; using LinqEnumerable = System.Linq.Enumerable; @@ -39,26 +38,17 @@ public void FallbackIfEmptyWithEmptySequence() // ReSharper restore PossibleMultipleEnumeration } - [TestCase(1)] - [TestCase(2)] - [TestCase(3)] - [TestCase(4)] - [TestCase(5)] - [TestCase(6)] - public void FallbackIfEmptyPreservesSourceCollectionIfPossible(int length) + [Test] + public void FallbackIfEmptyPreservesSourceCollectionIfPossible() { var source = new int[] { 1 }; // ReSharper disable PossibleMultipleEnumeration - switch (length) { - case 1: Assert.AreSame(source.FallbackIfEmpty(12), source); break; - case 2: Assert.AreSame(source.FallbackIfEmpty(12, 23), source); break; - case 3: Assert.AreSame(source.FallbackIfEmpty(12, 23, 34), source); break; - case 4: Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45), source); break; - case 5: Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56), source); break; - case 6: Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56, 67), source); break; - default: - throw new ArgumentOutOfRangeException(nameof(length)); - } + Assert.AreSame(source.FallbackIfEmpty(12), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23, 34), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56), source); + Assert.AreSame(source.FallbackIfEmpty(12, 23, 34, 45, 56, 67), source); // ReSharper restore PossibleMultipleEnumeration } From 16a78a93d6c23c28b74892c244cacc0562d41d46 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 10 Aug 2017 22:52:21 +0100 Subject: [PATCH 10/10] Simplification of names --- MoreLinq/FallbackIfEmpty.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MoreLinq/FallbackIfEmpty.cs b/MoreLinq/FallbackIfEmpty.cs index e95af6a89..db08417b4 100644 --- a/MoreLinq/FallbackIfEmpty.cs +++ b/MoreLinq/FallbackIfEmpty.cs @@ -165,7 +165,7 @@ static IEnumerable FallbackIfEmptyImpl(IEnumerable source, { if (collection.Count == 0) { - return FallbackChooser(); + return Fallback(); } else { @@ -187,20 +187,20 @@ IEnumerable _() } } - foreach (var item in FallbackChooser()) + foreach (var item in Fallback()) yield return item; } - IEnumerable FallbackChooser() + IEnumerable Fallback() { switch (count) { case null: return fallback; - case int n when n >= 1 && n <= 4: return InstancesFallback(); + case int n when n >= 1 && n <= 4: return FallbackOnArgs(); default: throw new ArgumentOutOfRangeException(nameof(count), count, null); } - IEnumerable InstancesFallback() + IEnumerable FallbackOnArgs() { yield return fallback1; if (count > 1) yield return fallback2;