// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Xunit; namespace Tests { public class GroupBy : AsyncEnumerableTests { [Fact] public void GroupBy_Null() { Assert.Throws(() => AsyncEnumerable.GroupBy(default, x => x)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, default(Func))); Assert.Throws(() => AsyncEnumerable.GroupBy(default, x => x, EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, default(Func), EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(default, x => x, x => x)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, default, x => x)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, x => x, default(Func))); Assert.Throws(() => AsyncEnumerable.GroupBy(default, x => x, x => x, EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, default, x => x, EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, x => x, default(Func), EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(default, x => x, (x, ys) => x)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, default, (x, ys) => x)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, x => x, default(Func, int>))); Assert.Throws(() => AsyncEnumerable.GroupBy(default, x => x, (x, ys) => x, EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, default, (x, ys) => x, EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, x => x, default(Func, int>), EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(default, x => x, x => x, (x, ys) => x)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, default, x => x, (x, ys) => x)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, x => x, default, (x, ys) => x)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, x => x, x => x, default)); Assert.Throws(() => AsyncEnumerable.GroupBy(default, x => x, x => x, (x, ys) => x, EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, default, x => x, (x, ys) => x, EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, x => x, default, (x, ys) => x, EqualityComparer.Default)); Assert.Throws(() => AsyncEnumerable.GroupBy(Return42, x => x, x => x, default, EqualityComparer.Default)); } [Fact] public async Task GroupBy1() { var xs = new[] { new { Name = "Bart", Age = 27 }, new { Name = "John", Age = 62 }, new { Name = "Eric", Age = 27 }, new { Name = "Lisa", Age = 14 }, new { Name = "Brad", Age = 27 }, new { Name = "Lisa", Age = 23 }, new { Name = "Eric", Age = 42 }, }; var ys = xs.ToAsyncEnumerable(); var res = ys.GroupBy(x => x.Age / 10); var e = res.GetAsyncEnumerator(); Assert.True(await e.MoveNextAsync()); Assert.Equal(2, e.Current.Key); var g1 = e.Current.GetAsyncEnumerator(); await HasNextAsync(g1, xs[0]); await HasNextAsync(g1, xs[2]); await HasNextAsync(g1, xs[4]); await HasNextAsync(g1, xs[5]); await NoNextAsync(g1); Assert.True(await e.MoveNextAsync()); Assert.Equal(6, e.Current.Key); var g2 = e.Current.GetAsyncEnumerator(); await HasNextAsync(g2, xs[1]); await NoNextAsync(g2); Assert.True(await e.MoveNextAsync()); Assert.Equal(1, e.Current.Key); var g3 = e.Current.GetAsyncEnumerator(); await HasNextAsync(g3, xs[3]); await NoNextAsync(g3); Assert.True(await e.MoveNextAsync()); Assert.Equal(4, e.Current.Key); var g4 = e.Current.GetAsyncEnumerator(); await HasNextAsync(g4, xs[6]); await NoNextAsync(g4); await NoNextAsync(e); } [Fact] public async Task GroupBy2() { var xs = new[] { new { Name = "Bart", Age = 27 }, new { Name = "John", Age = 62 }, new { Name = "Eric", Age = 27 }, new { Name = "Lisa", Age = 14 }, new { Name = "Brad", Age = 27 }, new { Name = "Lisa", Age = 23 }, new { Name = "Eric", Age = 42 }, }; var ys = xs.ToAsyncEnumerable(); var res = ys.GroupBy(x => x.Age / 10); var e = res.GetAsyncEnumerator(); Assert.True(await e.MoveNextAsync()); var g1 = e.Current; Assert.Equal(2, g1.Key); Assert.True(await e.MoveNextAsync()); var g2 = e.Current; Assert.Equal(6, g2.Key); Assert.True(await e.MoveNextAsync()); var g3 = e.Current; Assert.Equal(1, g3.Key); Assert.True(await e.MoveNextAsync()); var g4 = e.Current; Assert.Equal(4, g4.Key); await NoNextAsync(e); var g1e = g1.GetAsyncEnumerator(); await HasNextAsync(g1e, xs[0]); await HasNextAsync(g1e, xs[2]); await HasNextAsync(g1e, xs[4]); await HasNextAsync(g1e, xs[5]); await NoNextAsync(g1e); var g2e = g2.GetAsyncEnumerator(); await HasNextAsync(g2e, xs[1]); await NoNextAsync(g2e); var g3e = g3.GetAsyncEnumerator(); await HasNextAsync(g3e, xs[3]); await NoNextAsync(g3e); var g4e = g4.GetAsyncEnumerator(); await HasNextAsync(g4e, xs[6]); await NoNextAsync(g4e); } [Fact] public async Task GroupBy3() { var xs = AsyncEnumerable.Empty(); var ys = xs.GroupBy(x => x); var e = ys.GetAsyncEnumerator(); await NoNextAsync(e); } [Fact] public async Task GroupBy4Async() { var ex = new Exception("Bang!"); var xs = Throw(ex); var ys = xs.GroupBy(x => x); var e = ys.GetAsyncEnumerator(); await AssertThrowsAsync(e.MoveNextAsync(), ex); } [Fact] public async Task GroupBy5Async() { var ex = new Exception("Bang!"); var xs = GetXs(ex).ToAsyncEnumerable(); var ys = xs.GroupBy(x => x); var e = ys.GetAsyncEnumerator(); await AssertThrowsAsync(e.MoveNextAsync(), ex); } [Fact] public async Task GroupBy6Async() { var ex = new Exception("Bang!"); var xs = GetXs(ex).ToAsyncEnumerable(); var ys = xs.GroupBy(x => x); var e = ys.GetAsyncEnumerator(); await AssertThrowsAsync(e.MoveNextAsync(), ex); } private static IEnumerable GetXs(Exception ex) { yield return 42; yield return 43; throw ex; } [Fact] public async Task GroupBy7Async() { var ex = new Exception("Bang!"); var xs = Return42; var ys = xs.GroupBy(new Func(x => { throw ex; })); var e = ys.GetAsyncEnumerator(); await AssertThrowsAsync(e.MoveNextAsync(), ex); } [Fact] public async Task GroupBy8Async() { var ex = new Exception("Bang!"); var xs = new[] { 1, 2, 3 }.ToAsyncEnumerable(); var ys = xs.GroupBy(x => { if (x == 3) throw ex; return x; }); var e = ys.GetAsyncEnumerator(); await AssertThrowsAsync(e.MoveNextAsync(), ex); } [Fact] public async Task GroupBy9() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x)); var e = ys.GetAsyncEnumerator(); Assert.True(await e.MoveNextAsync()); var g1 = e.Current; Assert.Equal(0, g1.Key); var g1e = g1.GetAsyncEnumerator(); await HasNextAsync(g1e, 'a'); await HasNextAsync(g1e, 'd'); await HasNextAsync(g1e, 'g'); await HasNextAsync(g1e, 'j'); await NoNextAsync(g1e); Assert.True(await e.MoveNextAsync()); var g2 = e.Current; Assert.Equal(1, g2.Key); var g2e = g2.GetAsyncEnumerator(); await HasNextAsync(g2e, 'b'); await HasNextAsync(g2e, 'e'); await HasNextAsync(g2e, 'h'); await NoNextAsync(g2e); Assert.True(await e.MoveNextAsync()); var g3 = e.Current; Assert.Equal(2, g3.Key); var g3e = g3.GetAsyncEnumerator(); await HasNextAsync(g3e, 'c'); await HasNextAsync(g3e, 'f'); await HasNextAsync(g3e, 'i'); await NoNextAsync(g3e); await NoNextAsync(e); } [Fact] public async Task GroupBy10() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x), (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result); var e = ys.GetAsyncEnumerator(); await HasNextAsync(e, "0 - adgj"); await HasNextAsync(e, "1 - beh"); await HasNextAsync(e, "2 - cfi"); await NoNextAsync(e); } [Fact] public async Task GroupBy11() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result); var e = ys.GetAsyncEnumerator(); await HasNextAsync(e, "0 - 0369"); await HasNextAsync(e, "1 - 147"); await HasNextAsync(e, "2 - 258"); await NoNextAsync(e); } [Fact] public async Task GroupBy12() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, new EqMod(3)); var e = ys.GetAsyncEnumerator(); Assert.True(await e.MoveNextAsync()); var g1 = e.Current; Assert.Equal(0, g1.Key); var g1e = g1.GetAsyncEnumerator(); await HasNextAsync(g1e, 0); await HasNextAsync(g1e, 3); await HasNextAsync(g1e, 6); await HasNextAsync(g1e, 9); await NoNextAsync(g1e); Assert.True(await e.MoveNextAsync()); var g2 = e.Current; Assert.Equal(1, g2.Key); var g2e = g2.GetAsyncEnumerator(); await HasNextAsync(g2e, 1); await HasNextAsync(g2e, 4); await HasNextAsync(g2e, 7); await NoNextAsync(g2e); Assert.True(await e.MoveNextAsync()); var g3 = e.Current; Assert.Equal(2, g3.Key); var g3e = g3.GetAsyncEnumerator(); await HasNextAsync(g3e, 2); await HasNextAsync(g3e, 5); await HasNextAsync(g3e, 8); await NoNextAsync(g3e); await NoNextAsync(e); } [Fact] public async Task GroupBy13() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, x => (char)('a' + x), new EqMod(3)); var e = ys.GetAsyncEnumerator(); Assert.True(await e.MoveNextAsync()); var g1 = e.Current; Assert.Equal(0, g1.Key); var g1e = g1.GetAsyncEnumerator(); await HasNextAsync(g1e, 'a'); await HasNextAsync(g1e, 'd'); await HasNextAsync(g1e, 'g'); await HasNextAsync(g1e, 'j'); await NoNextAsync(g1e); Assert.True(await e.MoveNextAsync()); var g2 = e.Current; Assert.Equal(1, g2.Key); var g2e = g2.GetAsyncEnumerator(); await HasNextAsync(g2e, 'b'); await HasNextAsync(g2e, 'e'); await HasNextAsync(g2e, 'h'); await NoNextAsync(g2e); Assert.True(await e.MoveNextAsync()); var g3 = e.Current; Assert.Equal(2, g3.Key); var g3e = g3.GetAsyncEnumerator(); await HasNextAsync(g3e, 'c'); await HasNextAsync(g3e, 'f'); await HasNextAsync(g3e, 'i'); await NoNextAsync(g3e); await NoNextAsync(e); } [Fact] public async Task GroupBy14() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, x => (char)('a' + x), (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result, new EqMod(3)); var e = ys.GetAsyncEnumerator(); await HasNextAsync(e, "0 - adgj"); await HasNextAsync(e, "1 - beh"); await HasNextAsync(e, "2 - cfi"); await NoNextAsync(e); } [Fact] public async Task GroupBy15() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result, new EqMod(3)); var e = ys.GetAsyncEnumerator(); await HasNextAsync(e, "0 - 0369"); await HasNextAsync(e, "1 - 147"); await HasNextAsync(e, "2 - 258"); await NoNextAsync(e); } [Fact] public async Task GroupBy16() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, x => (char)('a' + x), new EqMod(3)); var e = ys.GetAsyncEnumerator(); Assert.True(await e.MoveNextAsync()); var g1 = e.Current; Assert.Equal(0, g1.Key); var g1e = g1.GetAsyncEnumerator(); await HasNextAsync(g1e, 'a'); await HasNextAsync(g1e, 'd'); await HasNextAsync(g1e, 'g'); await HasNextAsync(g1e, 'j'); await NoNextAsync(g1e); await g1e.DisposeAsync(); Assert.True(await e.MoveNextAsync()); var g2 = e.Current; Assert.Equal(1, g2.Key); var g2e = g2.GetAsyncEnumerator(); await HasNextAsync(g2e, 'b'); await HasNextAsync(g2e, 'e'); await HasNextAsync(g2e, 'h'); await NoNextAsync(g2e); await g2e.DisposeAsync(); Assert.True(await e.MoveNextAsync()); var g3 = e.Current; Assert.Equal(2, g3.Key); var g3e = g3.GetAsyncEnumerator(); await HasNextAsync(g3e, 'c'); await HasNextAsync(g3e, 'f'); await HasNextAsync(g3e, 'i'); await NoNextAsync(g3e); await g3e.DisposeAsync(); await NoNextAsync(e); await e.DisposeAsync(); } [Fact] public async Task GroupBy17() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, x => (char)('a' + x), new EqMod(3)); var e = ys.GetAsyncEnumerator(); await e.DisposeAsync(); Assert.False(await e.MoveNextAsync()); } [Fact] public async Task GroupBy18() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, x => (char)('a' + x), new EqMod(3)); var e = ys.GetAsyncEnumerator(); Assert.True(await e.MoveNextAsync()); var g1 = e.Current; Assert.Equal(0, g1.Key); var g1e = g1.GetAsyncEnumerator(); await HasNextAsync(g1e, 'a'); await e.DisposeAsync(); await HasNextAsync(g1e, 'd'); await HasNextAsync(g1e, 'g'); await HasNextAsync(g1e, 'j'); await NoNextAsync(g1e); await g1e.DisposeAsync(); Assert.False(await e.MoveNextAsync()); } [Fact] public async Task GroupBy19() { // We're using Kvp here because the types will eval as equal for this test var xs = new[] { new Kvp("Bart", 27), new Kvp("John", 62), new Kvp("Eric", 27), new Kvp("Lisa", 14), new Kvp("Brad", 27), new Kvp("Lisa", 23), new Kvp("Eric", 42) }; var ys = xs.ToAsyncEnumerable(); var res = ys.GroupBy(x => x.Item / 10); await SequenceIdentity(res); } [Fact] public async Task GroupBy20() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x), (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result); var arr = new[] { "0 - adgj", "1 - beh", "2 - cfi" }; Assert.Equal(arr, await ys.ToArray()); } [Fact] public async Task GroupBy21() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x), (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result); var arr = new List { "0 - adgj", "1 - beh", "2 - cfi" }; Assert.Equal(arr, await ys.ToList()); } [Fact] public async Task GroupBy22() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x), (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result); Assert.Equal(3, await ys.Count()); } [Fact] public async Task GroupBy23() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x), (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result); await SequenceIdentity(ys); } [Fact] public async Task GroupBy24() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x)); var g1a = new[] { 'a', 'd', 'g', 'j' }; var g2a = new[] { 'b', 'e', 'h' }; var g3a = new[] { 'c', 'f', 'i' }; var gar = await ys.ToArray(); Assert.Equal(3, gar.Length); var gg1 = gar[0]; var gg1a = await gg1.ToArray(); Assert.Equal(g1a, gg1a); var gg2 = gar[1]; var gg2a = await gg2.ToArray(); Assert.Equal(g2a, gg2a); var gg3 = gar[2]; var gg3a = await gg3.ToArray(); Assert.Equal(g3a, gg3a); } [Fact] public async Task GroupBy25() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x)); var g1a = new List { 'a', 'd', 'g', 'j' }; var g2a = new List { 'b', 'e', 'h' }; var g3a = new List { 'c', 'f', 'i' }; var gar = await ys.ToList(); Assert.Equal(3, gar.Count); var gg1 = gar[0]; var gg1a = await gg1.ToList(); Assert.Equal(g1a, gg1a); var gg2 = gar[1]; var gg2a = await gg2.ToList(); Assert.Equal(g2a, gg2a); var gg3 = gar[2]; var gg3a = await gg3.ToList(); Assert.Equal(g3a, gg3a); } [Fact] public async Task GroupBy26() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x)); var gar = await ys.ToList(); Assert.Equal(3, gar.Count); var gg1 = gar[0]; var gg1a = await gg1.Count(); Assert.Equal(4, gg1a); var gg2 = gar[1]; var gg2a = await gg2.Count(); Assert.Equal(3, gg2a); var gg3 = gar[2]; var gg3a = await gg3.Count(); Assert.Equal(3, gg3a); } [Fact] public async Task GroupBy27() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x)); var gar = await ys.Count(); Assert.Equal(3, gar); } [Fact] public async Task GroupBy28() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x % 3, x => (char)('a' + x)); await SequenceIdentity(ys); } [Fact] public async Task GroupBy29() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, new EqMod(3)); var g1a = new List { 0, 3, 6, 9 }; var g2a = new List { 1, 4, 7 }; var g3a = new List { 2, 5, 8 }; var gar = await ys.ToList(); Assert.Equal(3, gar.Count); var gg1 = gar[0]; var gg1a = await gg1.ToList(); Assert.Equal(g1a, gg1a); var gg2 = gar[1]; var gg2a = await gg2.ToList(); Assert.Equal(g2a, gg2a); var gg3 = gar[2]; var gg3a = await gg3.ToList(); Assert.Equal(g3a, gg3a); } [Fact] public async Task GroupBy30() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, new EqMod(3)); var gar = await ys.ToList(); Assert.Equal(3, gar.Count); var gg1 = gar[0]; var gg1a = await gg1.Count(); Assert.Equal(4, gg1a); var gg2 = gar[1]; var gg2a = await gg2.Count(); Assert.Equal(3, gg2a); var gg3 = gar[2]; var gg3a = await gg3.Count(); Assert.Equal(3, gg3a); } [Fact] public async Task GroupBy31() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, new EqMod(3)); var g1a = new[] { 0, 3, 6, 9 }; var g2a = new[] { 1, 4, 7 }; var g3a = new[] { 2, 5, 8 }; var gar = await ys.ToArray(); Assert.Equal(3, gar.Length); var gg1 = gar[0]; var gg1a = await gg1.ToArray(); Assert.Equal(g1a, gg1a); var gg2 = gar[1]; var gg2a = await gg2.ToArray(); Assert.Equal(g2a, gg2a); var gg3 = gar[2]; var gg3a = await gg3.ToArray(); Assert.Equal(g3a, gg3a); } [Fact] public async Task GroupBy32() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, new EqMod(3)); var gar = await ys.Count(); Assert.Equal(3, gar); } [Fact] public async Task GroupBy33() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, new EqMod(3)); await SequenceIdentity(ys); } [Fact] public async Task GroupBy34() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result, new EqMod(3)); var arr = new[] { "0 - 0369", "1 - 147", "2 - 258" }; Assert.Equal(arr, await ys.ToArray()); } [Fact] public async Task GroupBy35() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result, new EqMod(3)); var arr = new List { "0 - 0369", "1 - 147", "2 - 258" }; Assert.Equal(arr, await ys.ToList()); } [Fact] public async Task GroupBy36() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result, new EqMod(3)); Assert.Equal(3, await ys.Count()); } [Fact] public async Task GroupBy37() { var xs = AsyncEnumerable.Range(0, 10); var ys = xs.GroupBy(x => x, (k, cs) => k + " - " + cs.Aggregate("", (a, c) => a + c).Result, new EqMod(3)); await SequenceIdentity(ys); } private sealed class EqMod : IEqualityComparer { private readonly int _d; public EqMod(int d) { _d = d; } public bool Equals(int x, int y) { return EqualityComparer.Default.Equals(x % _d, y % _d); } public int GetHashCode(int obj) { return EqualityComparer.Default.GetHashCode(obj % _d); } } private sealed class Kvp : IEquatable { public Kvp(string key, int item) { Key = key; Item = item; } public string Key { get; } public int Item { get; } public bool Equals(Kvp other) { if (other is null) return false; if (ReferenceEquals(this, other)) return true; return string.Equals(Key, other.Key) && Item == other.Item; } public override bool Equals(object obj) { if (obj is null) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != GetType()) return false; return Equals((Kvp)obj); } public override int GetHashCode() { unchecked { return ((Key != null ? Key.GetHashCode() : 0) * 397) ^ Item; } } public static bool operator ==(Kvp left, Kvp right) { return Equals(left, right); } public static bool operator !=(Kvp left, Kvp right) { return !Equals(left, right); } } } }