From 964281dfc6e1828f078890099e0042f2e5ed85bf Mon Sep 17 00:00:00 2001 From: vsadov Date: Sat, 6 Feb 2016 14:17:06 -0800 Subject: [PATCH 1/2] Added Enumerable.Append and Enumerable.Prepend New public API in System.Linq Closes #2075 --- src/System.Linq/ref/System.Linq.cs | 2 + src/System.Linq/src/System/Linq/Enumerable.cs | 24 ++++++ src/System.Linq/tests/AppendPrependTests.cs | 74 +++++++++++++++++++ .../tests/System.Linq.Tests.csproj | 5 +- 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/System.Linq/tests/AppendPrependTests.cs diff --git a/src/System.Linq/ref/System.Linq.cs b/src/System.Linq/ref/System.Linq.cs index 68799f5d74ec..932e48ce8d3f 100644 --- a/src/System.Linq/ref/System.Linq.cs +++ b/src/System.Linq/ref/System.Linq.cs @@ -36,6 +36,8 @@ public static partial class Enumerable public static float Average(this System.Collections.Generic.IEnumerable source, System.Func selector) { return default(float); } public static System.Collections.Generic.IEnumerable Cast(this System.Collections.IEnumerable source) { return default(System.Collections.Generic.IEnumerable); } public static System.Collections.Generic.IEnumerable Concat(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second) { return default(System.Collections.Generic.IEnumerable); } + public static System.Collections.Generic.IEnumerable Append(this System.Collections.Generic.IEnumerable source, TSource element) { return default(System.Collections.Generic.IEnumerable); } + public static System.Collections.Generic.IEnumerable Prepend(this System.Collections.Generic.IEnumerable source, TSource element) { return default(System.Collections.Generic.IEnumerable); } public static bool Contains(this System.Collections.Generic.IEnumerable source, TSource value) { return default(bool); } public static bool Contains(this System.Collections.Generic.IEnumerable source, TSource value, System.Collections.Generic.IEqualityComparer comparer) { return default(bool); } public static int Count(this System.Collections.Generic.IEnumerable source) { return default(int); } diff --git a/src/System.Linq/src/System/Linq/Enumerable.cs b/src/System.Linq/src/System/Linq/Enumerable.cs index 5dd7bbeceed2..9e7cd3ed7005 100644 --- a/src/System.Linq/src/System/Linq/Enumerable.cs +++ b/src/System.Linq/src/System/Linq/Enumerable.cs @@ -1152,6 +1152,30 @@ private static IEnumerable ConcatIterator(IEnumerable foreach (TSource element in second) yield return element; } + public static IEnumerable Append(this IEnumerable source, TSource element) + { + if (source == null) throw Error.ArgumentNull("source"); + return AppendIterator(source, element); + } + + private static IEnumerable AppendIterator(IEnumerable source, TSource element) + { + foreach (TSource e1 in source) yield return e1; + yield return element; + } + + public static IEnumerable Prepend(this IEnumerable source, TSource element) + { + if (source == null) throw Error.ArgumentNull("source"); + return PrependIterator(source, element); + } + + private static IEnumerable PrependIterator(IEnumerable source, TSource element) + { + yield return element; + foreach (TSource e1 in source) yield return e1; + } + public static IEnumerable Zip(this IEnumerable first, IEnumerable second, Func resultSelector) { if (first == null) throw Error.ArgumentNull("first"); diff --git a/src/System.Linq/tests/AppendPrependTests.cs b/src/System.Linq/tests/AppendPrependTests.cs new file mode 100644 index 000000000000..0bc863eeff41 --- /dev/null +++ b/src/System.Linq/tests/AppendPrependTests.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Xunit; + +namespace System.Linq.Tests +{ + public class AppendPrependTests : EnumerableTests + { + [Fact] + public void SameResultsRepeatCallsIntQueryAppend() + { + var q1 = from x1 in new int?[] { 2, 3, null, 2, null, 4, 5 } + select x1; + + Assert.Equal(q1.Append(42), q1.Append(42)); + Assert.Equal(q1.Append(42), q1.Concat(new int?[] { 42 })); + } + + [Fact] + public void SameResultsRepeatCallsIntQueryPrePend() + { + var q1 = from x1 in new int?[] { 2, 3, null, 2, null, 4, 5 } + select x1; + + Assert.Equal(q1.Prepend(42), q1.Prepend(42)); + Assert.Equal(q1.Prepend(42), (new int?[] { 42 }).Concat(q1)); + } + + [Fact] + public void SameResultsRepeatCallsStringQueryAppend() + { + var q1 = from x1 in new[] { "AAA", String.Empty, "q", "C", "#", "!@#$%^", "0987654321", "Calling Twice" } + select x1; + + Assert.Equal(q1.Append("hi"), q1.Append("hi")); + Assert.Equal(q1.Append("hi"), q1.Concat(new string[] { "hi" })); + } + + [Fact] + public void SameResultsRepeatCallsStringQueryPrepend() + { + var q1 = from x1 in new[] { "AAA", String.Empty, "q", "C", "#", "!@#$%^", "0987654321", "Calling Twice" } + select x1; + + Assert.Equal(q1.Prepend("hi"), q1.Prepend("hi")); + Assert.Equal(q1.Prepend("hi"), (new string[] { "hi" }).Concat(q1)); + } + + [Fact] + public void EmptyAppend() + { + int[] first = { }; + Assert.Single(first.Append(42), 42); + } + + [Fact] + public void EmptyPrepend() + { + string[] first = { }; + Assert.Single(first.Prepend("aa"), "aa"); + } + + [Fact] + public void SourceNull() + { + Assert.Throws("source", () => ((IEnumerable)null).Append(1)); + Assert.Throws("source", () => ((IEnumerable)null).Prepend(1)); + } + } +} diff --git a/src/System.Linq/tests/System.Linq.Tests.csproj b/src/System.Linq/tests/System.Linq.Tests.csproj index be7cf20e00bb..772d7fa1d747 100644 --- a/src/System.Linq/tests/System.Linq.Tests.csproj +++ b/src/System.Linq/tests/System.Linq.Tests.csproj @@ -1,4 +1,4 @@ - + @@ -20,6 +20,7 @@ + @@ -89,4 +90,4 @@ - + \ No newline at end of file From a00aaf16f282891482278376ae6e25cdcc968c15 Mon Sep 17 00:00:00 2001 From: vsadov Date: Sat, 6 Feb 2016 22:01:22 -0800 Subject: [PATCH 2/2] CR feedback * Bumped up version of System.Linq * Added Append/Prepend to the MatchSequencePattern test in System.Linq.Expressions since the new operators are not avaialble on IQueryable. * added a test for combinations of Append, Prepend, Concat --- .../tests/SequenceTests/SequenceTests.cs | 4 +- src/System.Linq/ref/System.Linq.csproj | 2 +- src/System.Linq/src/System.Linq.csproj | 2 +- src/System.Linq/tests/AppendPrependTests.cs | 47 ++++++++++++++++++- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs b/src/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs index 08575e37718b..0f74ddea1931 100644 --- a/src/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs +++ b/src/System.Linq.Expressions/tests/SequenceTests/SequenceTests.cs @@ -1669,7 +1669,9 @@ public static void MatchSequencePattern() "AsEnumerable", "ToList", "Fold", - "LeftJoin" + "LeftJoin", + "Append", + "Prepend", } ).ToList(); diff --git a/src/System.Linq/ref/System.Linq.csproj b/src/System.Linq/ref/System.Linq.csproj index 7685754a8ba0..cadafe52c89a 100644 --- a/src/System.Linq/ref/System.Linq.csproj +++ b/src/System.Linq/ref/System.Linq.csproj @@ -2,7 +2,7 @@ - 4.0.2.0 + 4.1.0.0 Library dotnet5.1 .NETPlatform,Version=v5.1 diff --git a/src/System.Linq/src/System.Linq.csproj b/src/System.Linq/src/System.Linq.csproj index c6b3d83d5e0f..d75aec9cddb3 100644 --- a/src/System.Linq/src/System.Linq.csproj +++ b/src/System.Linq/src/System.Linq.csproj @@ -4,7 +4,7 @@ {CA488507-3B6E-4494-B7BE-7B4EEEB2C4D1} System.Linq - 4.0.2.0 + 4.1.0.0 System.Linq dotnet5.4 diff --git a/src/System.Linq/tests/AppendPrependTests.cs b/src/System.Linq/tests/AppendPrependTests.cs index 0bc863eeff41..eaa2663e8e32 100644 --- a/src/System.Linq/tests/AppendPrependTests.cs +++ b/src/System.Linq/tests/AppendPrependTests.cs @@ -21,7 +21,7 @@ public void SameResultsRepeatCallsIntQueryAppend() } [Fact] - public void SameResultsRepeatCallsIntQueryPrePend() + public void SameResultsRepeatCallsIntQueryPrepend() { var q1 = from x1 in new int?[] { 2, 3, null, 2, null, 4, 5 } select x1; @@ -64,11 +64,56 @@ public void EmptyPrepend() Assert.Single(first.Prepend("aa"), "aa"); } + [Fact] + public void PrependNoIteratingSourceBeforeFirstItem() + { + var ie = new List(); + var prepended = (from i in ie select i).Prepend(4); + + ie.Add(42); + + Assert.Equal(prepended, ie.Prepend(4)); + } + + [Fact] + public void ForcedToEnumeratorDoesntEnumeratePrepend() + { + var iterator = NumberRangeGuaranteedNotCollectionType(0, 3).Prepend(4); + // Don't insist on this behaviour, but check it's correct if it happens + var en = iterator as IEnumerator; + Assert.False(en != null && en.MoveNext()); + } + + [Fact] + public void ForcedToEnumeratorDoesntEnumerateAppend() + { + var iterator = NumberRangeGuaranteedNotCollectionType(0, 3).Append(4); + // Don't insist on this behaviour, but check it's correct if it happens + var en = iterator as IEnumerator; + Assert.False(en != null && en.MoveNext()); + } + [Fact] public void SourceNull() { Assert.Throws("source", () => ((IEnumerable)null).Append(1)); Assert.Throws("source", () => ((IEnumerable)null).Prepend(1)); } + + [Fact] + public void Combined() + { + var v = "foo".Append('1').Append('2').Prepend('3').Concat("qq".Append('Q').Prepend('W')); + + Assert.Equal(v.ToArray(), "3foo12WqqQ".ToArray()); + + var v1 = "a".Append('b').Append('c').Append('d'); + + Assert.Equal(v1.ToArray(), "abcd".ToArray()); + + var v2 = "a".Prepend('b').Prepend('c').Prepend('d'); + + Assert.Equal(v2.ToArray(), "dcba".ToArray()); + } } }