Revert "Revert "Add another Zip IEnumerable<T> extension method (dotnet/corefx#26582...
authorStephen Toub <stoub@microsoft.com>
Wed, 27 Feb 2019 15:20:41 +0000 (10:20 -0500)
committerGitHub <noreply@github.com>
Wed, 27 Feb 2019 15:20:41 +0000 (10:20 -0500)
* Revert "Revert "Add another Zip IEnumerable<T> extension method (dotnet/corefx#26582)" (dotnet/corefx#33709)"

This reverts commit dotnet/corefx@53ae9af48c5c133f4413e76c1469dfe6ccecd926.

* Adapt to ThrowHelper changes

Commit migrated from https://github.com/dotnet/corefx/commit/720591ab8c213c8f3563db1f06114ec7c8d012bb

src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs
src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs
src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
src/libraries/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj
src/libraries/System.Linq.Queryable/tests/ZipTests.cs
src/libraries/System.Linq.Queryable/tests/ZipTests.netcoreapp.cs [new file with mode: 0644]
src/libraries/System.Linq/ref/System.Linq.cs
src/libraries/System.Linq/src/System/Linq/Zip.cs
src/libraries/System.Linq/tests/System.Linq.Tests.csproj
src/libraries/System.Linq/tests/ZipTests.cs
src/libraries/System.Linq/tests/ZipTests.netcoreapp.cs [new file with mode: 0644]

index cb1bcde..6a435a2 100644 (file)
@@ -163,6 +163,7 @@ namespace System.Linq
         public static System.Linq.IQueryable<TSource> Union<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2, System.Collections.Generic.IEqualityComparer<TSource> comparer) { throw null; }
         public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
         public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
+        public static System.Linq.IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2) { throw null; }
         public static System.Linq.IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Linq.Expressions.Expression<System.Func<TFirst, TSecond, TResult>> resultSelector) { throw null; }
     }
 }
index 00dd5bc..7d120a6 100644 (file)
@@ -830,6 +830,13 @@ namespace System.Linq
              (s_Where_Index_TSource_2 = new Func<IQueryable<object>, Expression<Func<object, int, bool>>, IQueryable<object>>(Queryable.Where).GetMethodInfo().GetGenericMethodDefinition()))
               .MakeGenericMethod(TSource);
 
+        private static MethodInfo s_Zip_TFirst_TSecond_2;
+
+        public static MethodInfo Zip_TFirst_TSecond_2(Type TFirst, Type TSecond) =>
+            (s_Zip_TFirst_TSecond_2 ??
+            (s_Zip_TFirst_TSecond_2 = new Func<IQueryable<object>, IEnumerable<object>, IQueryable<ValueTuple<object, object>>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
+            .MakeGenericMethod(TFirst, TSecond);
+
         private static MethodInfo s_Zip_TFirst_TSecond_TResult_3;
 
         public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond, Type TResult) =>
@@ -866,4 +873,4 @@ namespace System.Linq
              (s_Prepend_TSource_2 = new Func<IQueryable<object>, object, IQueryable<object>>(Queryable.Prepend).GetMethodInfo().GetGenericMethodDefinition()))
               .MakeGenericMethod(TSource);
     }
-}
\ No newline at end of file
+}
index 47a0b6c..54c7ac4 100644 (file)
@@ -592,6 +592,25 @@ namespace System.Linq
                     ));
         }
 
+        public static IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2)
+        {
+            if (source1 == null)
+            {
+                throw Error.ArgumentNull(nameof(source1));
+            }
+
+            if (source2 == null)
+            {
+                throw Error.ArgumentNull(nameof(source2));
+            }
+
+            return source1.Provider.CreateQuery<(TFirst, TSecond)>(
+                Expression.Call(
+                    null,
+                    CachedReflectionInfo.Zip_TFirst_TSecond_2(typeof(TFirst), typeof(TSecond)),
+                    source1.Expression, GetSourceExpression(source2)));
+        }
+
         public static IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, Expression<Func<TFirst, TSecond, TResult>> resultSelector)
         {
             if (source1 == null)
index b4b08de..9044074 100644 (file)
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <ProjectGuid>{7B88D79B-B799-4116-A7D0-AED572540CD4}</ProjectGuid>
     <Configurations>netcoreapp-Debug;netcoreapp-Release;netstandard-Debug;netstandard-Release</Configurations>
@@ -7,6 +7,7 @@
     <Compile Include="AppendPrependTests.cs" />
     <Compile Include="TakeLastTests.cs" />
     <Compile Include="SkipLastTests.cs" />
+    <Compile Include="ZipTests.netcoreapp.cs" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="AggregateTests.cs" />
index 924bc50..6ba48a7 100644 (file)
@@ -7,7 +7,7 @@ using Xunit;
 
 namespace System.Linq.Tests
 {
-    public class ZipTests : EnumerableBasedTests
+    public partial class ZipTests : EnumerableBasedTests
     {
         [Fact]
         public void CorrectResults()
diff --git a/src/libraries/System.Linq.Queryable/tests/ZipTests.netcoreapp.cs b/src/libraries/System.Linq.Queryable/tests/ZipTests.netcoreapp.cs
new file mode 100644 (file)
index 0000000..97c5366
--- /dev/null
@@ -0,0 +1,53 @@
+// 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 Xunit;
+
+namespace System.Linq.Tests
+{
+    public partial class ZipTests
+    {
+        [Fact]
+        public void Zip2_CorrectResults()
+        {
+            int[] first = new int[] { 1, 2, 3 };
+            int[] second = new int[] { 2, 5, 9 };
+            var expected = new (int, int)[] { (1, 2), (2, 5), (3, 9) };
+            Assert.Equal(expected, first.AsQueryable().Zip(second.AsQueryable()));
+        }
+        
+        [Fact]
+        public void Zip2_FirstIsNull()
+        {
+            IQueryable<int> first = null;
+            int[] second = new int[] { 2, 5, 9 };
+            AssertExtensions.Throws<ArgumentNullException>("source1", () => first.Zip(second.AsQueryable()));
+        }
+
+        [Fact]
+        public void Zip2_SecondIsNull()
+        {
+            int[] first = new int[] { 1, 2, 3 };
+            IQueryable<int> second = null;
+            AssertExtensions.Throws<ArgumentNullException>("source2", () => first.AsQueryable().Zip(second));
+        }
+
+        [Fact]
+        public void Zip2()
+        {
+            int count = (new int[] { 0, 1, 2 }).AsQueryable().Zip((new int[] { 10, 11, 12 }).AsQueryable()).Count();
+            Assert.Equal(3, count);
+        }
+
+        [Fact]
+        public void TupleNames()
+        {
+            int[] first = new int[] { 1 };
+            int[] second = new int[] { 2 };
+            var tuple = first.AsQueryable().Zip(second.AsQueryable()).First();
+            Assert.Equal(tuple.Item1, tuple.First);
+            Assert.Equal(tuple.Item2, tuple.Second);
+        }
+    }
+}
index eca16c9..6f6f8d5 100644 (file)
@@ -190,6 +190,7 @@ namespace System.Linq
         public static System.Collections.Generic.IEnumerable<TSource> Union<TSource>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second, System.Collections.Generic.IEqualityComparer<TSource> comparer) { throw null; }
         public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
         public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
+        public static System.Collections.Generic.IEnumerable<(TFirst First,TSecond Second)> Zip<TFirst, TSecond>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second) { throw null; }
         public static System.Collections.Generic.IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Func<TFirst, TSecond, TResult> resultSelector) { throw null; }
     }
     public partial interface IGrouping<out TKey, out TElement> : System.Collections.Generic.IEnumerable<TElement>, System.Collections.IEnumerable
index 19c4dd4..b87bea1 100644 (file)
@@ -28,6 +28,33 @@ namespace System.Linq
             return ZipIterator(first, second, resultSelector);
         }
 
+        public static IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second)
+        {
+            if (first is null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
+            }
+
+            if (second is null)
+            {
+                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
+            }
+
+            return ZipIterator(first, second);
+        }
+
+        private static IEnumerable<(TFirst, TSecond)> ZipIterator<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
+        {
+            using (IEnumerator<TFirst> e1 = first.GetEnumerator())
+            using (IEnumerator<TSecond> e2 = second.GetEnumerator())
+            {
+                while (e1.MoveNext() && e2.MoveNext())
+                {
+                    yield return (e1.Current, e2.Current);
+                }
+            }
+        }
+
         private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
         {
             using (IEnumerator<TFirst> e1 = first.GetEnumerator())
index a2584e5..cc88ba9 100644 (file)
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <ProjectGuid>{7C70BB15-870B-4946-8098-625DACD645A6}</ProjectGuid>
     <Configurations>netcoreapp-Debug;netcoreapp-Release;netstandard-Debug;netstandard-Release;uapaot-Debug;uapaot-Release</Configurations>
@@ -10,6 +10,7 @@
     <Compile Include="OrderByTests.cs" />
     <Compile Include="ToHashSetTests.cs" />
     <Compile Include="SelectManyTests.netcoreapp.cs" />
+    <Compile Include="ZipTests.netcoreapp.cs" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="AggregateTests.cs" />
index 1d94dc1..b3b18ed 100644 (file)
@@ -2,14 +2,12 @@
 // 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;
 using System.Collections.Generic;
 using Xunit;
 
 namespace System.Linq.Tests
 {
-    public class ZipTests : EnumerableTests
+    public partial class ZipTests : EnumerableTests
     {
         [Fact]
         public void ImplicitTypeParameters()
diff --git a/src/libraries/System.Linq/tests/ZipTests.netcoreapp.cs b/src/libraries/System.Linq/tests/ZipTests.netcoreapp.cs
new file mode 100644 (file)
index 0000000..05811fa
--- /dev/null
@@ -0,0 +1,226 @@
+// 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.Collections.Generic;
+using Xunit;
+
+namespace System.Linq.Tests
+{
+    public partial class ZipTests
+    {
+        [Fact]
+        public void Zip2_ImplicitTypeParameters()
+        {
+            IEnumerable<int> first = new int[] { 1, 2, 3 };
+            IEnumerable<int> second = new int[] { 2, 5, 9 };
+            IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+        
+        [Fact]
+        public void Zip2_ExplicitTypeParameters()
+        {
+            IEnumerable<int> first = new int[] { 1, 2, 3 };
+            IEnumerable<int> second = new int[] { 2, 5, 9 };
+            IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
+
+            Assert.Equal(expected, first.Zip<int, int>(second));
+        }
+
+        [Fact]
+        public void Zip2_FirstIsNull()
+        {
+            IEnumerable<int> first = null;
+            IEnumerable<int> second = new int[] { 2, 5, 9 };
+
+            AssertExtensions.Throws<ArgumentNullException>("first", () => first.Zip<int, int>(second));
+        }
+
+        [Fact]
+        public void Zip2_SecondIsNull()
+        {
+            IEnumerable<int> first = new int[] { 1, 2, 3 };
+            IEnumerable<int> second = null;
+
+            AssertExtensions.Throws<ArgumentNullException>("second", () => first.Zip<int, int>(second));
+        }
+
+        [Fact]
+        public void Zip2_ExceptionThrownFromFirstsEnumerator()
+        {
+            ThrowsOnMatchEnumerable<int> first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
+            IEnumerable<int> second = new int[] { 2, 4, 6 };
+            IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };
+            
+            Assert.Equal(expected, first.Zip(second));
+
+            first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 2, 3 }, 2);
+
+            IEnumerable<(int, int)> zip = first.Zip(second);
+            
+            Assert.Throws<Exception>(() => zip.ToList());
+        }
+
+        [Fact]
+        public void Zip2_ExceptionThrownFromSecondsEnumerator()
+        {
+            ThrowsOnMatchEnumerable<int> second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
+            IEnumerable<int> first = new int[] { 2, 4, 6 };
+            IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };
+
+            Assert.Equal(expected, first.Zip(second));
+
+            second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 2, 3 }, 2);
+
+            IEnumerable<(int, int)> zip = first.Zip(second);
+
+            Assert.Throws<Exception>(() => zip.ToList());
+        }
+
+        [Fact]
+        public void Zip2_FirstAndSecondEmpty()
+        {
+            IEnumerable<int> first = new int[] { };
+            IEnumerable<int> second = new int[] { };
+            IEnumerable<(int, int)> expected = new (int, int)[] { };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_FirstEmptySecondSingle()
+        {
+            IEnumerable<int> first = new int[] { };
+            IEnumerable<int> second = new int[] { 2 };
+            IEnumerable<(int, int)> expected = new (int, int)[] { };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_FirstEmptySecondMany()
+        {
+            IEnumerable<int> first = new int[] { };
+            IEnumerable<int> second = new int[] { 2, 4, 8 };
+            IEnumerable<(int, int)> expected = new (int, int)[] { };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_SecondEmptyFirstSingle()
+        {
+            IEnumerable<int> first = new int[] { 1 };
+            IEnumerable<int> second = new int[] { };
+            IEnumerable<(int, int)> expected = new (int, int)[] { };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_SecondEmptyFirstMany()
+        {
+            IEnumerable<int> first = new int[] { 1, 2, 3 };
+            IEnumerable<int> second = new int[] { };
+            IEnumerable<(int, int)> expected = new (int, int)[] { };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_FirstAndSecondSingle()
+        {
+            IEnumerable<int> first = new int[] { 1 };
+            IEnumerable<int> second = new int[] { 2 };
+            IEnumerable<(int, int)> expected = new (int, int)[] { (1, 2) };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_FirstAndSecondEqualSize()
+        {
+            IEnumerable<int> first = new int[] { 1, 2, 3 };
+            IEnumerable<int> second = new int[] { 2, 3, 4 };
+            IEnumerable<(int, int)> expected = new (int, int)[] { (1, 2), (2, 3), (3, 4) };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_SecondOneMoreThanFirst()
+        {
+            IEnumerable<int> first = new int[] { 1, 2 };
+            IEnumerable<int> second = new int[] { 2, 4, 8 };
+            IEnumerable<(int, int)> expected = new (int, int)[] { (1, 2), (2, 4) };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+
+        [Fact]
+        public void Zip2_SecondManyMoreThanFirst()
+        {
+            IEnumerable<int> first = new int[] { 1, 2 };
+            IEnumerable<int> second = new int[] { 2, 4, 8, 16 };
+            IEnumerable<(int, int)> expected = new (int, int)[] { (1, 2), (2, 4) };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_FirstOneMoreThanSecond()
+        {
+            IEnumerable<int> first = new int[] { 1, 2, 3 };
+            IEnumerable<int> second = new int[] { 2, 4 };
+            IEnumerable<(int, int)> expected = new (int, int)[] { (1, 2), (2, 4) };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_FirstManyMoreThanSecond()
+        {
+            IEnumerable<int> first = new int[] { 1, 2, 3, 4 };
+            IEnumerable<int> second = new int[] { 2, 4 };
+            IEnumerable<(int, int)> expected = new (int, int)[] { (1, 2), (2, 4) };
+
+            Assert.Equal(expected, first.Zip(second));
+        }
+
+        [Fact]
+        public void Zip2_RunOnce()
+        {
+            IEnumerable<int?> first = new[] { 1, (int?)null, 3 };
+            IEnumerable<int> second = new[] { 2, 4, 6, 8 };
+            IEnumerable<(int?, int)> expected = new (int?, int)[] { (1, 2), (null, 4), (3, 6) };
+
+            Assert.Equal(expected, first.RunOnce().Zip(second.RunOnce()));
+        }
+
+        [Fact]
+        public void Zip2_NestedTuple()
+        {
+            IEnumerable<int> first = new[] { 1, 3, 5 };
+            IEnumerable<int> second = new[] { 2, 4, 6 };
+            IEnumerable<(int, int)> third = new[] { (1, 2), (3, 4), (5, 6) };
+
+            Assert.Equal(third, first.Zip(second));
+
+            IEnumerable<string> fourth = new[] { "one", "two", "three" };
+
+            IEnumerable<((int, int), string)> final = new[] { ((1, 2), "one"), ((3, 4), "two"), ((5, 6), "three") };
+            Assert.Equal(final, third.Zip(fourth));
+        }
+
+        [Fact]
+        public void Zip2_TupleNames()
+        {
+            var t = new[] { 1, 2, 3 }.Zip(new[] { 2, 4, 6 }).First();
+            Assert.Equal(t.Item1, t.First);
+            Assert.Equal(t.Item2, t.Second);
+        }
+    }
+}