From eea1f53c2b355dabe31a16a832ef7bcba00df559 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 4 Feb 2024 21:14:00 -0500 Subject: [PATCH 1/4] Implement IList on Enumerable.Cast for IList --- .../System.Linq/src/System/Linq/Cast.cs | 91 +++++++++++++++++++ src/libraries/System.Linq/tests/CastTests.cs | 50 +++++++++- 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Linq/src/System/Linq/Cast.cs b/src/libraries/System.Linq/src/System/Linq/Cast.cs index 0c20609b3eb79d..48d02ad398ede1 100644 --- a/src/libraries/System.Linq/src/System/Linq/Cast.cs +++ b/src/libraries/System.Linq/src/System/Linq/Cast.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.Diagnostics; namespace System.Linq { @@ -45,6 +46,11 @@ public static IEnumerable< ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } + if (source is IList list) + { + return new CastIListIterator(list); + } + return CastIterator(source); } @@ -55,5 +61,90 @@ private static IEnumerable CastIterator(IEnumerable source) yield return (TResult)obj; } } + + [DebuggerDisplay("Count = {Count}")] + private sealed partial class CastIListIterator(IList source) : Iterator, IList, IReadOnlyList + { + private readonly IList _source = source; + private IEnumerator? _enumerator; + + public override Iterator Clone() => new CastIListIterator(_source); + + public override bool MoveNext() + { + switch (_state) + { + case 1: + _enumerator = _source.GetEnumerator(); + _state = 2; + goto case 2; + + case 2: + Debug.Assert(_enumerator != null); + if (_enumerator.MoveNext()) + { + _current = (TResult)_enumerator.Current; + return true; + } + + Dispose(); + break; + } + + return false; + } + + public override void Dispose() + { + if (_enumerator != null) + { + (_enumerator as IDisposable)?.Dispose(); + _enumerator = null; + } + + base.Dispose(); + } + + public int Count => _source.Count; + + public bool IsReadOnly => true; + + public int IndexOf(TResult item) => _source.IndexOf(item); + + public bool Contains(TResult item) => _source.Contains(item); + + public void CopyTo(TResult[] array, int arrayIndex) + { + ArgumentNullException.ThrowIfNull(array); + + IList source = _source; + int count = source.Count; + + if (arrayIndex < 0 || arrayIndex > array.Length - count) + { + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + } + + for (int i = 0; i < count; i++) + { + array[arrayIndex + i] = (TResult)source[i]!; + } + } + + public TResult this[int index] + { + get => (TResult)_source[index]!; + set => throw new NotSupportedException(); + } + + public void Add(TResult item) => throw new NotSupportedException(); + public void Clear() => throw new NotSupportedException(); + public void Insert(int index, TResult item) => throw new NotSupportedException(); + public bool Remove(TResult item) => throw new NotSupportedException(); + public void RemoveAt(int index) => throw new NotSupportedException(); + + public override IEnumerable Select(Func selector) => + new SelectIListIterator(this, selector); + } } } diff --git a/src/libraries/System.Linq/tests/CastTests.cs b/src/libraries/System.Linq/tests/CastTests.cs index 6577d2e72e35d2..b02d36fdb30158 100644 --- a/src/libraries/System.Linq/tests/CastTests.cs +++ b/src/libraries/System.Linq/tests/CastTests.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using Xunit; @@ -235,5 +234,54 @@ public void ForcedToEnumeratorDoesntEnumerate() var en = iterator as IEnumerator; Assert.False(en != null && en.MoveNext()); } + + [Fact] + public void TargetTypeIsSourceType_Nop() + { + object[] values = new string[] { "hello", "world" }; + Assert.Same(values, values.Cast()); + } + + [Fact] + public void CastProducesIListFromIList() + { + object[] values = new object[] { "hello", "world" }; + + IList castList = (IList)values.Cast(); + Assert.NotNull(castList); + + Assert.Equal(2, castList.Count); + Assert.Equal("hello", castList[0]); + Assert.Equal("world", castList[1]); + Assert.True(castList.IsReadOnly); + Assert.True(castList.Contains("hello")); + Assert.True(castList.Contains("world")); + Assert.False(castList.Contains("hello, world")); + Assert.Equal(0, castList.IndexOf("hello")); + Assert.Equal(1, castList.IndexOf("world")); + Assert.Equal(-1, castList.IndexOf("hello, world")); + + Assert.Throws(() => castList.Add("foo")); + Assert.Throws(() => castList.Insert(0, "foo")); + Assert.Throws(() => castList.Remove("foo")); + Assert.Throws(() => castList.RemoveAt(0)); + Assert.Throws(() => castList.Clear()); + Assert.Throws(() => castList[-1]); + Assert.Throws(() => castList[2]); + Assert.Throws(() => castList.CopyTo(null, 0)); + Assert.Throws(() => castList.CopyTo(new string[1], 0)); + + IReadOnlyList castReadOnlyList = (IReadOnlyList)castList; + Assert.Equal(2, castReadOnlyList.Count); + Assert.Equal("hello", castReadOnlyList[0]); + Assert.Equal("world", castReadOnlyList[1]); + } + + [Fact] + public void CastProducesNonIListFromNonIList() + { + IEnumerable e = new object[] { "hello", "world" }.Select(s => s).Cast(); + Assert.IsNotAssignableFrom>(e); + } } } From e6d495d4e9899c554029759a43ae2fa742f42332 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 5 Feb 2024 21:59:18 -0500 Subject: [PATCH 2/4] Downgrade to ICollection --- .../System.Linq/src/System.Linq.csproj | 1 + .../src/System/Linq/Cast.SpeedOpt.cs | 122 +++++++++++++++++ .../System.Linq/src/System/Linq/Cast.cs | 53 +------- src/libraries/System.Linq/tests/CastTests.cs | 125 +++++++++++++----- 4 files changed, 221 insertions(+), 80 deletions(-) create mode 100644 src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs diff --git a/src/libraries/System.Linq/src/System.Linq.csproj b/src/libraries/System.Linq/src/System.Linq.csproj index 2fc4153be6890a..cbc128d961e880 100644 --- a/src/libraries/System.Linq/src/System.Linq.csproj +++ b/src/libraries/System.Linq/src/System.Linq.csproj @@ -18,6 +18,7 @@ + diff --git a/src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs new file mode 100644 index 00000000000000..66f33e36649dfd --- /dev/null +++ b/src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; + +namespace System.Linq +{ + public static partial class Enumerable + { + private sealed partial class CastICollectionIterator : IPartition + { + public int GetCount(bool onlyIfCheap) => Count; + + public TResult[] ToArray() + { + TResult[] array = new TResult[Count]; + + int index = 0; + foreach (TResult item in _source) + { + array[index++] = item; + } + + return array; + } + + public List ToList() + { + List list = new(Count); + + foreach (TResult item in _source) + { + list.Add(item); + } + + return list; + } + + public TResult? TryGetElementAt(int index, out bool found) + { + if (index >= 0) + { + IEnumerator e = _source.GetEnumerator(); + try + { + while (e.MoveNext()) + { + if (index == 0) + { + found = true; + return (TResult)e.Current; + } + + index--; + } + } + finally + { + (e as IDisposable)?.Dispose(); + } + } + + found = false; + return default; + } + + public TResult? TryGetFirst(out bool found) + { + IEnumerator e = _source.GetEnumerator(); + try + { + if (e.MoveNext()) + { + found = true; + return (TResult)e.Current; + } + } + finally + { + (e as IDisposable)?.Dispose(); + } + + found = false; + return default; + } + + public TResult? TryGetLast(out bool found) + { + IEnumerator e = _source.GetEnumerator(); + try + { + if (e.MoveNext()) + { + TResult last = (TResult)e.Current; + while (e.MoveNext()) + { + last = (TResult)e.Current; + } + + found = true; + return last; + } + + found = false; + return default; + } + finally + { + (e as IDisposable)?.Dispose(); + } + } + + public override IEnumerable Select(Func selector) => + new SelectIPartitionIterator(this, selector); + + public IPartition? Skip(int count) => new EnumerablePartition(this, count, -1); + + public IPartition? Take(int count) => new EnumerablePartition(this, 0, count - 1); + } + } +} diff --git a/src/libraries/System.Linq/src/System/Linq/Cast.cs b/src/libraries/System.Linq/src/System/Linq/Cast.cs index 48d02ad398ede1..4fc4ee37e544e5 100644 --- a/src/libraries/System.Linq/src/System/Linq/Cast.cs +++ b/src/libraries/System.Linq/src/System/Linq/Cast.cs @@ -46,9 +46,9 @@ public static IEnumerable< ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } - if (source is IList list) + if (source is ICollection collection) { - return new CastIListIterator(list); + return new CastICollectionIterator(collection); } return CastIterator(source); @@ -63,12 +63,14 @@ private static IEnumerable CastIterator(IEnumerable source) } [DebuggerDisplay("Count = {Count}")] - private sealed partial class CastIListIterator(IList source) : Iterator, IList, IReadOnlyList + private sealed partial class CastICollectionIterator(ICollection source) : Iterator, IReadOnlyCollection { - private readonly IList _source = source; + private readonly ICollection _source = source; private IEnumerator? _enumerator; - public override Iterator Clone() => new CastIListIterator(_source); + public int Count => _source.Count; + + public override Iterator Clone() => new CastICollectionIterator(_source); public override bool MoveNext() { @@ -104,47 +106,6 @@ public override void Dispose() base.Dispose(); } - - public int Count => _source.Count; - - public bool IsReadOnly => true; - - public int IndexOf(TResult item) => _source.IndexOf(item); - - public bool Contains(TResult item) => _source.Contains(item); - - public void CopyTo(TResult[] array, int arrayIndex) - { - ArgumentNullException.ThrowIfNull(array); - - IList source = _source; - int count = source.Count; - - if (arrayIndex < 0 || arrayIndex > array.Length - count) - { - throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - } - - for (int i = 0; i < count; i++) - { - array[arrayIndex + i] = (TResult)source[i]!; - } - } - - public TResult this[int index] - { - get => (TResult)_source[index]!; - set => throw new NotSupportedException(); - } - - public void Add(TResult item) => throw new NotSupportedException(); - public void Clear() => throw new NotSupportedException(); - public void Insert(int index, TResult item) => throw new NotSupportedException(); - public bool Remove(TResult item) => throw new NotSupportedException(); - public void RemoveAt(int index) => throw new NotSupportedException(); - - public override IEnumerable Select(Func selector) => - new SelectIListIterator(this, selector); } } } diff --git a/src/libraries/System.Linq/tests/CastTests.cs b/src/libraries/System.Linq/tests/CastTests.cs index b02d36fdb30158..d1a177f4718225 100644 --- a/src/libraries/System.Linq/tests/CastTests.cs +++ b/src/libraries/System.Linq/tests/CastTests.cs @@ -243,45 +243,102 @@ public void TargetTypeIsSourceType_Nop() } [Fact] - public void CastProducesIListFromIList() + public void CastProducesIReadOnlyCollectionFromICollection() { object[] values = new object[] { "hello", "world" }; - IList castList = (IList)values.Cast(); + IReadOnlyCollection castList = (IReadOnlyCollection)values.Cast(); Assert.NotNull(castList); - Assert.Equal(2, castList.Count); - Assert.Equal("hello", castList[0]); - Assert.Equal("world", castList[1]); - Assert.True(castList.IsReadOnly); - Assert.True(castList.Contains("hello")); - Assert.True(castList.Contains("world")); - Assert.False(castList.Contains("hello, world")); - Assert.Equal(0, castList.IndexOf("hello")); - Assert.Equal(1, castList.IndexOf("world")); - Assert.Equal(-1, castList.IndexOf("hello, world")); - - Assert.Throws(() => castList.Add("foo")); - Assert.Throws(() => castList.Insert(0, "foo")); - Assert.Throws(() => castList.Remove("foo")); - Assert.Throws(() => castList.RemoveAt(0)); - Assert.Throws(() => castList.Clear()); - Assert.Throws(() => castList[-1]); - Assert.Throws(() => castList[2]); - Assert.Throws(() => castList.CopyTo(null, 0)); - Assert.Throws(() => castList.CopyTo(new string[1], 0)); - - IReadOnlyList castReadOnlyList = (IReadOnlyList)castList; - Assert.Equal(2, castReadOnlyList.Count); - Assert.Equal("hello", castReadOnlyList[0]); - Assert.Equal("world", castReadOnlyList[1]); - } - - [Fact] - public void CastProducesNonIListFromNonIList() - { - IEnumerable e = new object[] { "hello", "world" }.Select(s => s).Cast(); - Assert.IsNotAssignableFrom>(e); + } + + [Fact] + public void CastOnMultidimensionalArraySucceeds() + { + Array array = Array.CreateInstance(typeof(int), 2, 3); + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 3; j++) + { + array.SetValue(i * 3 + j, i, j); + } + } + + int[] result = array.Cast().ToArray(); + for (int i = 0; i < 6; i++) + { + Assert.Equal(i, result[i]); + } + } + + [Fact] + public void CastCountReturnsExpectedLength() + { + object[] objects = new object[] { "hello", "world" }; + Assert.Equal(2, objects.Cast().Count()); + } + + [Fact] + public void CastFirstReturnsFirstElement() + { + object[] objects = new object[] { "hello", "world" }; + Assert.Equal("hello", objects.Cast().First()); + } + + [Fact] + public void CastFirstOnEmptySequenceThrows() + { + object[] objects = Array.Empty(); + Assert.Throws(() => objects.Cast().First()); + } + + [Fact] + public void CastLastReturnsLastElement() + { + object[] objects = new object[] { "hello", "world" }; + Assert.Equal("world", objects.Cast().Last()); + } + + [Fact] + public void CastElementAtReturnsExpectedElement() + { + object[] objects = new object[] { "hello", "world" }; + Assert.Equal("world", objects.Cast().ElementAt(1)); + } + + [Fact] + public void CastElementAtOutOfRangeThrows() + { + object[] objects = new object[] { "hello", "world" }; + Assert.Throws(() => objects.Cast().ElementAt(2)); + } + + [Fact] + public void CastLastOnEmptySequenceThrows() + { + object[] objects = Array.Empty(); + Assert.Throws(() => objects.Cast().Last()); + } + + [Fact] + public void CastSelectProcessesEachElement() + { + object[] objects = new object[] { "hello", "world!" }; + Assert.Equal(new[] { 5, 6 }, objects.Cast().Select(s => s.Length)); + } + + [Fact] + public void CastSkipSkipsElements() + { + object[] objects = new object[] { "hello", "there", "world" }; + Assert.Equal(new[] { "world" }, objects.Cast().Skip(2)); + } + + [Fact] + public void CastTakeTakesElements() + { + object[] objects = new object[] { "hello", "there", "world" }; + Assert.Equal(new[] { "hello", "there" }, objects.Cast().Take(2)); } } } From 137460684bb8ce5de68aca393f104e3d84b2908b Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 23 Feb 2024 10:41:30 -0500 Subject: [PATCH 3/4] Address PR feedback --- src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs | 6 +++--- src/libraries/System.Linq/src/System/Linq/Cast.cs | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs index 66f33e36649dfd..6ab50403c9c6d9 100644 --- a/src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs @@ -10,11 +10,11 @@ public static partial class Enumerable { private sealed partial class CastICollectionIterator : IPartition { - public int GetCount(bool onlyIfCheap) => Count; + public int GetCount(bool onlyIfCheap) => _source.Count; public TResult[] ToArray() { - TResult[] array = new TResult[Count]; + TResult[] array = new TResult[_source.Count]; int index = 0; foreach (TResult item in _source) @@ -27,7 +27,7 @@ public TResult[] ToArray() public List ToList() { - List list = new(Count); + List list = new(_source.Count); foreach (TResult item in _source) { diff --git a/src/libraries/System.Linq/src/System/Linq/Cast.cs b/src/libraries/System.Linq/src/System/Linq/Cast.cs index 4fc4ee37e544e5..0f14f47d5f08c3 100644 --- a/src/libraries/System.Linq/src/System/Linq/Cast.cs +++ b/src/libraries/System.Linq/src/System/Linq/Cast.cs @@ -63,13 +63,11 @@ private static IEnumerable CastIterator(IEnumerable source) } [DebuggerDisplay("Count = {Count}")] - private sealed partial class CastICollectionIterator(ICollection source) : Iterator, IReadOnlyCollection + private sealed partial class CastICollectionIterator(ICollection source) : Iterator { private readonly ICollection _source = source; private IEnumerator? _enumerator; - public int Count => _source.Count; - public override Iterator Clone() => new CastICollectionIterator(_source); public override bool MoveNext() From b9a1fb482ea35eb28834db7a6f043e5cf58c4205 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 23 Feb 2024 11:56:40 -0500 Subject: [PATCH 4/4] Remove defunct test --- src/libraries/System.Linq/System.Linq.sln | 24 ++++++++++++------- .../System.Linq/src/System/Linq/Cast.cs | 7 ++---- src/libraries/System.Linq/tests/CastTests.cs | 10 -------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/libraries/System.Linq/System.Linq.sln b/src/libraries/System.Linq/System.Linq.sln index a81e387e856bd6..b5283c72e434cd 100644 --- a/src/libraries/System.Linq/System.Linq.sln +++ b/src/libraries/System.Linq/System.Linq.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34618.27 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{AF1B1B01-A4EC-45F4-AE51-CC1FA7892181}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Collections", "..\System.Collections\ref\System.Collections.csproj", "{3A8560D8-0E79-4BDE-802A-C96C7FE98258}" @@ -35,11 +39,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8CA90AB2-58B EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{84E98F7C-FA2B-4048-AB7C-9FCDEA9CD37E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{34793393-0347-438D-A832-2476F33C1BE3}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{34793393-0347-438D-A832-2476F33C1BE3}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{F8F69023-9ACD-4979-A710-39D16377AEEE}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F8F69023-9ACD-4979-A710-39D16377AEEE}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{18C4E23D-AB0F-45E5-A6A1-A741F6462E85}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{18C4E23D-AB0F-45E5-A6A1-A741F6462E85}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{0ADC596A-5B2E-4E5F-B5B5-DEB65A6C7E9D}" EndProject @@ -111,24 +115,28 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {AF1B1B01-A4EC-45F4-AE51-CC1FA7892181} = {E291F4BF-7B8B-45AD-88F5-FB8B8380C126} - {80A4051B-4A36-4A8B-BA43-A5AB8AA959F3} = {E291F4BF-7B8B-45AD-88F5-FB8B8380C126} {3A8560D8-0E79-4BDE-802A-C96C7FE98258} = {7C5B49B9-F7D9-41FB-A8FA-94328BDDCCD1} {7E4C1F09-B4F2-470E-9E7B-2C386E93D657} = {7C5B49B9-F7D9-41FB-A8FA-94328BDDCCD1} - {D3160C37-FC48-4907-8F4A-F584ED12B275} = {7C5B49B9-F7D9-41FB-A8FA-94328BDDCCD1} {14B966BB-CE23-4432-ADBB-89974389AC1D} = {8CA90AB2-58B9-45E7-A684-EDB60C6924B0} + {80A4051B-4A36-4A8B-BA43-A5AB8AA959F3} = {E291F4BF-7B8B-45AD-88F5-FB8B8380C126} {9A13A12F-C924-43AF-94AF-6F1B33582D27} = {84E98F7C-FA2B-4048-AB7C-9FCDEA9CD37E} {4BEC631E-B5FD-453F-82A0-C95C461798EA} = {84E98F7C-FA2B-4048-AB7C-9FCDEA9CD37E} {C8F0459C-15D5-4624-8CE4-E93ADF96A28C} = {84E98F7C-FA2B-4048-AB7C-9FCDEA9CD37E} + {D3160C37-FC48-4907-8F4A-F584ED12B275} = {7C5B49B9-F7D9-41FB-A8FA-94328BDDCCD1} {E0CA3ED5-EE6C-4F7C-BCE7-EFB1D64A9CD1} = {34793393-0347-438D-A832-2476F33C1BE3} {3EFB74E7-616A-48C1-B43B-3F89AA5013E6} = {34793393-0347-438D-A832-2476F33C1BE3} - {34793393-0347-438D-A832-2476F33C1BE3} = {0ADC596A-5B2E-4E5F-B5B5-DEB65A6C7E9D} {28ABC524-ACEE-4183-A64A-49E3DC830595} = {F8F69023-9ACD-4979-A710-39D16377AEEE} {721DB3D9-8221-424E-BE29-084CDD20D26E} = {F8F69023-9ACD-4979-A710-39D16377AEEE} - {F8F69023-9ACD-4979-A710-39D16377AEEE} = {0ADC596A-5B2E-4E5F-B5B5-DEB65A6C7E9D} {E19B8772-2DBD-4274-8190-F3CC0242A1C0} = {18C4E23D-AB0F-45E5-A6A1-A741F6462E85} + {34793393-0347-438D-A832-2476F33C1BE3} = {0ADC596A-5B2E-4E5F-B5B5-DEB65A6C7E9D} + {F8F69023-9ACD-4979-A710-39D16377AEEE} = {0ADC596A-5B2E-4E5F-B5B5-DEB65A6C7E9D} {18C4E23D-AB0F-45E5-A6A1-A741F6462E85} = {0ADC596A-5B2E-4E5F-B5B5-DEB65A6C7E9D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A4970D79-BF1C-4343-9070-B409DBB69F93} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{3efb74e7-616a-48c1-b43b-3f89aa5013e6}*SharedItemsImports = 5 + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{721db3d9-8221-424e-be29-084cdd20d26e}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/libraries/System.Linq/src/System/Linq/Cast.cs b/src/libraries/System.Linq/src/System/Linq/Cast.cs index 0f14f47d5f08c3..7af5ad058eb0f6 100644 --- a/src/libraries/System.Linq/src/System/Linq/Cast.cs +++ b/src/libraries/System.Linq/src/System/Linq/Cast.cs @@ -96,11 +96,8 @@ public override bool MoveNext() public override void Dispose() { - if (_enumerator != null) - { - (_enumerator as IDisposable)?.Dispose(); - _enumerator = null; - } + (_enumerator as IDisposable)?.Dispose(); + _enumerator = null; base.Dispose(); } diff --git a/src/libraries/System.Linq/tests/CastTests.cs b/src/libraries/System.Linq/tests/CastTests.cs index d1a177f4718225..6f120a13e3c1bc 100644 --- a/src/libraries/System.Linq/tests/CastTests.cs +++ b/src/libraries/System.Linq/tests/CastTests.cs @@ -242,16 +242,6 @@ public void TargetTypeIsSourceType_Nop() Assert.Same(values, values.Cast()); } - [Fact] - public void CastProducesIReadOnlyCollectionFromICollection() - { - object[] values = new object[] { "hello", "world" }; - - IReadOnlyCollection castList = (IReadOnlyCollection)values.Cast(); - Assert.NotNull(castList); - Assert.Equal(2, castList.Count); - } - [Fact] public void CastOnMultidimensionalArraySucceeds() {