From 74f8733b81974cd242898279346e038936fddf21 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Sun, 5 Jan 2025 14:35:39 +0700 Subject: [PATCH 01/11] #110161 Allow collection expressions for 'System.Collections.ObjectModel' collection types https://github.com/dotnet/runtime/issues/110161 --- .../Generic/ReadOnlySet/ReadOnlySetTests.cs | 19 +++++++++++ .../ObjectModel/ReadOnlyCollection.cs | 33 +++++++++++++++++++ .../Collections/ObjectModel/ReadOnlySet.cs | 2 ++ .../System.Runtime/ref/System.Runtime.cs | 9 +++++ .../ObjectModel/ReadOnlyCollectionTests.cs | 17 ++++++++++ 5 files changed, 80 insertions(+) diff --git a/src/libraries/System.Collections/tests/Generic/ReadOnlySet/ReadOnlySetTests.cs b/src/libraries/System.Collections/tests/Generic/ReadOnlySet/ReadOnlySetTests.cs index 0080ceb603c7e6..171d10ef444334 100644 --- a/src/libraries/System.Collections/tests/Generic/ReadOnlySet/ReadOnlySetTests.cs +++ b/src/libraries/System.Collections/tests/Generic/ReadOnlySet/ReadOnlySetTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Linq; using Xunit; namespace System.Collections.ObjectModel.Tests @@ -21,6 +22,24 @@ public void Ctor_SetProperty_Roundtrips() Assert.Same(set, new DerivedReadOnlySet(set).Set); } + [Fact] + public static void Ctor_CollectionExpressions_Empty() + { + ReadOnlySet set = []; + Assert.IsType>(set); + Assert.Empty(set); + Assert.Same(set, ReadOnlySet.Empty); + } + + [Fact] + public static void Ctor_CollectionExpressions() + { + int[] array = [1, 2, 3, 3, 2, 1]; + ReadOnlySet set = [.. array]; + Assert.IsType>(set); + Assert.Equal(set.Order(), array.Distinct().Order()); + } + [Fact] public void Empty_EmptyAndIdempotent() { diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs index 62e45314a1c7cf..7a6c0e2451b48f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -2,12 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; namespace System.Collections.ObjectModel { [Serializable] + [CollectionBuilder(typeof(ReadOnlyCollection), "CreateCollection")] [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] [DebuggerDisplay("Count = {Count}")] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] @@ -229,4 +231,35 @@ void IList.RemoveAt(int index) ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); } } + + public static class ReadOnlyCollection + { + /// + /// Creates a new from the specified span of values. + /// This method (simplifies collection initialization)[/dotnet/csharp/language-reference/operators/collection-expressions] + /// to create a new with the specified values. + /// + /// The type of elements in the collection. + /// The span of values to include in the collection. + /// A new containing the specified values. + [EditorBrowsable(EditorBrowsableState.Never)] +#pragma warning disable IDE0301 // Simplify collection initialization + public static ReadOnlyCollection CreateCollection(params ReadOnlySpan values) => + values.Length <= 0 ? ReadOnlyCollection.Empty : new ReadOnlyCollection(values.ToArray()); +#pragma warning restore IDE0301 // Simplify collection initialization + + /// + /// Creates a new from the specified span of values. + /// This method (simplifies collection initialization)[/dotnet/csharp/language-reference/operators/collection-expressions] + /// to create a new with the specified values. + /// + /// The type of elements in the collection. + /// The span of values to include in the collection. + /// A new containing the specified values. + [EditorBrowsable(EditorBrowsableState.Never)] +#pragma warning disable IDE0301 // Simplify collection initialization + public static ReadOnlySet CreateSet(params ReadOnlySpan values) => + values.Length <= 0 ? ReadOnlySet.Empty : new ReadOnlySet((HashSet)[.. values]); +#pragma warning restore IDE0301 // Simplify collection initialization + } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs index c9b26b24deeaa0..6eeb927248d104 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs @@ -3,11 +3,13 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; namespace System.Collections.ObjectModel { /// Represents a read-only, generic set of values. /// The type of values in the set. + [CollectionBuilder(typeof(ReadOnlyCollection), "CreateSet")] [DebuggerDisplay("Count = {Count}")] public class ReadOnlySet : IReadOnlySet, ISet, ICollection { diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index da44791d9607cf..6b971b7ee9d3d3 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8366,6 +8366,14 @@ void System.Collections.ICollection.CopyTo(System.Array array, int index) { } void System.Collections.IList.Insert(int index, object? value) { } void System.Collections.IList.Remove(object? value) { } } + public static partial class ReadOnlyCollection + { + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public static System.Collections.ObjectModel.ReadOnlyCollection CreateCollection(params System.ReadOnlySpan values) { throw null; } + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public static System.Collections.ObjectModel.ReadOnlySet CreateSet(params System.ReadOnlySpan values) { throw null; } + } + [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "CreateCollection")] public partial class ReadOnlyCollection : System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList { public ReadOnlyCollection(System.Collections.Generic.IList list) { } @@ -8471,6 +8479,7 @@ void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } } + [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "CreateSet")] public partial class ReadOnlySet : System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlySet, System.Collections.Generic.ISet, System.Collections.ICollection, System.Collections.IEnumerable { public ReadOnlySet(System.Collections.Generic.ISet @set) { } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Collections/ObjectModel/ReadOnlyCollectionTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Collections/ObjectModel/ReadOnlyCollectionTests.cs index d8b1467358a0a3..dd8294a8d16439 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Collections/ObjectModel/ReadOnlyCollectionTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Collections/ObjectModel/ReadOnlyCollectionTests.cs @@ -30,6 +30,23 @@ public static void Ctor_IList() Assert.Same(s_intArray, collection.GetItems()); } + [Fact] + public static void Ctor_CollectionExpressions_Empty() + { + ReadOnlyCollection c = []; + Assert.IsType>(c); + Assert.Empty(c); + Assert.Same(c, ReadOnlyCollection.Empty); + } + + [Fact] + public static void Ctor_CollectionExpressions() + { + ReadOnlyCollection c = [.. s_intArray]; + Assert.IsType>(c); + Assert.Equal(c, s_intArray); + } + [Fact] public static void Count() { From 33826df8aade6020c2af387e7c25fb0c78066463 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Mon, 6 Jan 2025 04:30:24 +0700 Subject: [PATCH 02/11] Code review Enhance ReadOnlyCollection with documentation and updates The `EditorBrowsable` attribute is replaced with a `SuppressMessage` attribute to clarify the intended usage of these methods. Additionally, the condition for checking if input values are empty is updated from `values.Length <= 0` to `values.IsEmpty` for improved readability and performance. --- .../Collections/ObjectModel/ReadOnlyCollection.cs | 15 +++++++-------- .../System.Runtime/ref/System.Runtime.cs | 2 -- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs index 7a6c0e2451b48f..0607293b04739f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -232,6 +232,9 @@ void IList.RemoveAt(int index) } } + /// + /// Provides static methods to create read-only collections and sets. + /// public static class ReadOnlyCollection { /// @@ -242,11 +245,9 @@ public static class ReadOnlyCollection /// The type of elements in the collection. /// The span of values to include in the collection. /// A new containing the specified values. - [EditorBrowsable(EditorBrowsableState.Never)] -#pragma warning disable IDE0301 // Simplify collection initialization + [Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0301:Simplify collection initialization", Justification = "This method simplifies ReadOnlyCollection initialization, so you cannot simplify collection initialization inside it.")] public static ReadOnlyCollection CreateCollection(params ReadOnlySpan values) => - values.Length <= 0 ? ReadOnlyCollection.Empty : new ReadOnlyCollection(values.ToArray()); -#pragma warning restore IDE0301 // Simplify collection initialization + values.IsEmpty ? ReadOnlyCollection.Empty : new ReadOnlyCollection(values.ToArray()); /// /// Creates a new from the specified span of values. @@ -256,10 +257,8 @@ public static ReadOnlyCollection CreateCollection(params ReadOnlySpan v /// The type of elements in the collection. /// The span of values to include in the collection. /// A new containing the specified values. - [EditorBrowsable(EditorBrowsableState.Never)] -#pragma warning disable IDE0301 // Simplify collection initialization + [Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0301:Simplify collection initialization", Justification = "This method simplifies ReadOnlySet initialization, so you cannot simplify collection initialization inside it.")] public static ReadOnlySet CreateSet(params ReadOnlySpan values) => - values.Length <= 0 ? ReadOnlySet.Empty : new ReadOnlySet((HashSet)[.. values]); -#pragma warning restore IDE0301 // Simplify collection initialization + values.IsEmpty ? ReadOnlySet.Empty : new ReadOnlySet((HashSet)[.. values]); } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 6b971b7ee9d3d3..533b2bc75bf620 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8368,9 +8368,7 @@ void System.Collections.IList.Remove(object? value) { } } public static partial class ReadOnlyCollection { - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static System.Collections.ObjectModel.ReadOnlyCollection CreateCollection(params System.ReadOnlySpan values) { throw null; } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static System.Collections.ObjectModel.ReadOnlySet CreateSet(params System.ReadOnlySpan values) { throw null; } } [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "CreateCollection")] From 88183ec763d677f96e586bb5549f4fa037b32ddf Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Mon, 6 Jan 2025 04:47:55 +0700 Subject: [PATCH 03/11] Remove unused namespace from ReadOnlyCollection.cs Eliminated the `using System.ComponentModel;` directive, reducing dependencies and potentially simplifying the code structure. --- .../src/System/Collections/ObjectModel/ReadOnlyCollection.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs index 0607293b04739f..0c0b80570cc06e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; From b1860b126f2fe225c8715116c30e841e1af2442e Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Mon, 6 Jan 2025 23:05:27 +0700 Subject: [PATCH 04/11] Remove suppression attributes from collection methods The `CreateCollection` method in the `ReadOnlyCollection` class had its suppression attribute removed, addressing a style warning related to collection initialization while maintaining its functionality. Similarly, the `CreateSet` method in the `ReadOnlySet` class also had its suppression attribute removed, preserving its original functionality. --- .../src/System/Collections/ObjectModel/ReadOnlyCollection.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs index 0c0b80570cc06e..222a355368c35e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -244,7 +244,6 @@ public static class ReadOnlyCollection /// The type of elements in the collection. /// The span of values to include in the collection. /// A new containing the specified values. - [Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0301:Simplify collection initialization", Justification = "This method simplifies ReadOnlyCollection initialization, so you cannot simplify collection initialization inside it.")] public static ReadOnlyCollection CreateCollection(params ReadOnlySpan values) => values.IsEmpty ? ReadOnlyCollection.Empty : new ReadOnlyCollection(values.ToArray()); @@ -256,7 +255,6 @@ public static ReadOnlyCollection CreateCollection(params ReadOnlySpan v /// The type of elements in the collection. /// The span of values to include in the collection. /// A new containing the specified values. - [Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0301:Simplify collection initialization", Justification = "This method simplifies ReadOnlySet initialization, so you cannot simplify collection initialization inside it.")] public static ReadOnlySet CreateSet(params ReadOnlySpan values) => values.IsEmpty ? ReadOnlySet.Empty : new ReadOnlySet((HashSet)[.. values]); } From e4be17580cf7b69bf76b56b541fdf0c026a07db2 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Wed, 8 Jan 2025 02:38:01 +0700 Subject: [PATCH 05/11] Refactor `(HashSet)[.. values]` to manual loop --- .../Collections/ObjectModel/ReadOnlyCollection.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs index 222a355368c35e..dc0ea900d71bd9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -255,7 +255,16 @@ public static ReadOnlyCollection CreateCollection(params ReadOnlySpan v /// The type of elements in the collection. /// The span of values to include in the collection. /// A new containing the specified values. - public static ReadOnlySet CreateSet(params ReadOnlySpan values) => - values.IsEmpty ? ReadOnlySet.Empty : new ReadOnlySet((HashSet)[.. values]); + public static ReadOnlySet CreateSet(params ReadOnlySpan values) + { + if (values.IsEmpty) + return ReadOnlySet.Empty; + + HashSet hashSet = []; + foreach (T value in values) + hashSet.Add(value); + + return new ReadOnlySet(hashSet); + } } } From e3aa02c53a8ff4776eab8a811f4991d124a4acb6 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Wed, 8 Jan 2025 15:49:39 +0700 Subject: [PATCH 06/11] Marked CreateCollection and CreateSet methods in ReadOnlyCollection with the `[EditorBrowsable(EditorBrowsableState.Never)]` attribute to hide them from IntelliSense. --- .../src/System/Collections/ObjectModel/ReadOnlyCollection.cs | 3 +++ src/libraries/System.Runtime/ref/System.Runtime.cs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs index dc0ea900d71bd9..a21d1ca162fdc1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -244,6 +245,7 @@ public static class ReadOnlyCollection /// The type of elements in the collection. /// The span of values to include in the collection. /// A new containing the specified values. + [EditorBrowsable(EditorBrowsableState.Never)] public static ReadOnlyCollection CreateCollection(params ReadOnlySpan values) => values.IsEmpty ? ReadOnlyCollection.Empty : new ReadOnlyCollection(values.ToArray()); @@ -255,6 +257,7 @@ public static ReadOnlyCollection CreateCollection(params ReadOnlySpan v /// The type of elements in the collection. /// The span of values to include in the collection. /// A new containing the specified values. + [EditorBrowsable(EditorBrowsableState.Never)] public static ReadOnlySet CreateSet(params ReadOnlySpan values) { if (values.IsEmpty) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 533b2bc75bf620..6b971b7ee9d3d3 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8368,7 +8368,9 @@ void System.Collections.IList.Remove(object? value) { } } public static partial class ReadOnlyCollection { + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static System.Collections.ObjectModel.ReadOnlyCollection CreateCollection(params System.ReadOnlySpan values) { throw null; } + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static System.Collections.ObjectModel.ReadOnlySet CreateSet(params System.ReadOnlySpan values) { throw null; } } [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "CreateCollection")] From 3642b180dbc84273ad1e83103703109857900bf4 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Wed, 8 Jan 2025 11:28:56 +0000 Subject: [PATCH 07/11] Apply suggestions from code review --- .../src/System/Collections/ObjectModel/ReadOnlyCollection.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs index a21d1ca162fdc1..329dd47aa56ce0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -261,11 +261,15 @@ public static ReadOnlyCollection CreateCollection(params ReadOnlySpan v public static ReadOnlySet CreateSet(params ReadOnlySpan values) { if (values.IsEmpty) + { return ReadOnlySet.Empty; + } HashSet hashSet = []; foreach (T value in values) + { hashSet.Add(value); + } return new ReadOnlySet(hashSet); } From 8f983495e806c227d74a1b3607ea5d6ca72b961c Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Thu, 9 Jan 2025 01:12:46 +0700 Subject: [PATCH 08/11] Refactor ReadOnlyCollection and ReadOnlySet APIs Updated method names in ReadOnlyCollection and ReadOnlySet from CreateCollection and CreateSet to Create for clarity and consistency. Removed the CreateSet method from ReadOnlyCollection to streamline functionality. Adjusted documentation comments for improved clarity. Updated CollectionBuilder attributes to reflect the new method names, ensuring proper linkage for collection creation. --- .../ObjectModel/ReadOnlyCollection.cs | 33 ++----------------- .../Collections/ObjectModel/ReadOnlySet.cs | 28 +++++++++++++++- .../System.Runtime/ref/System.Runtime.cs | 13 ++++---- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs index 329dd47aa56ce0..ace2a09298861f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -2,14 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; namespace System.Collections.ObjectModel { [Serializable] - [CollectionBuilder(typeof(ReadOnlyCollection), "CreateCollection")] + [CollectionBuilder(typeof(ReadOnlyCollection), "Create")] [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] [DebuggerDisplay("Count = {Count}")] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] @@ -233,7 +232,7 @@ void IList.RemoveAt(int index) } /// - /// Provides static methods to create read-only collections and sets. + /// Provides static methods for read-only collections. /// public static class ReadOnlyCollection { @@ -245,33 +244,7 @@ public static class ReadOnlyCollection /// The type of elements in the collection. /// The span of values to include in the collection. /// A new containing the specified values. - [EditorBrowsable(EditorBrowsableState.Never)] - public static ReadOnlyCollection CreateCollection(params ReadOnlySpan values) => + public static ReadOnlyCollection Create(params ReadOnlySpan values) => values.IsEmpty ? ReadOnlyCollection.Empty : new ReadOnlyCollection(values.ToArray()); - - /// - /// Creates a new from the specified span of values. - /// This method (simplifies collection initialization)[/dotnet/csharp/language-reference/operators/collection-expressions] - /// to create a new with the specified values. - /// - /// The type of elements in the collection. - /// The span of values to include in the collection. - /// A new containing the specified values. - [EditorBrowsable(EditorBrowsableState.Never)] - public static ReadOnlySet CreateSet(params ReadOnlySpan values) - { - if (values.IsEmpty) - { - return ReadOnlySet.Empty; - } - - HashSet hashSet = []; - foreach (T value in values) - { - hashSet.Add(value); - } - - return new ReadOnlySet(hashSet); - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs index 6eeb927248d104..14090c3c6f1a2f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs @@ -9,7 +9,7 @@ namespace System.Collections.ObjectModel { /// Represents a read-only, generic set of values. /// The type of values in the set. - [CollectionBuilder(typeof(ReadOnlyCollection), "CreateSet")] + [CollectionBuilder(typeof(ReadOnlySet), "Create")] [DebuggerDisplay("Count = {Count}")] public class ReadOnlySet : IReadOnlySet, ISet, ICollection { @@ -101,4 +101,30 @@ public IEnumerator GetEnumerator() => /// bool ICollection.Remove(T item) => throw new NotSupportedException(); } + + /// + /// Provides static methods for read-only sets. + /// + public static class ReadOnlySet + { + /// + /// Creates a new from the specified span of values. + /// This method (simplifies collection initialization)[/dotnet/csharp/language-reference/operators/collection-expressions] + /// to create a new with the specified values. + /// + /// The type of elements in the collection. + /// The span of values to include in the collection. + /// A new containing the specified values. + public static ReadOnlySet Create(params ReadOnlySpan values) + { + if (values.IsEmpty) + return ReadOnlySet.Empty; + + HashSet hashSet = []; + foreach (T value in values) + hashSet.Add(value); + + return new ReadOnlySet(hashSet); + } + } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 6b971b7ee9d3d3..86e2abe3598f75 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8368,12 +8368,9 @@ void System.Collections.IList.Remove(object? value) { } } public static partial class ReadOnlyCollection { - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public static System.Collections.ObjectModel.ReadOnlyCollection CreateCollection(params System.ReadOnlySpan values) { throw null; } - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public static System.Collections.ObjectModel.ReadOnlySet CreateSet(params System.ReadOnlySpan values) { throw null; } + public static System.Collections.ObjectModel.ReadOnlyCollection Create(params System.ReadOnlySpan values) { throw null; } } - [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "CreateCollection")] + [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "Create")] public partial class ReadOnlyCollection : System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList { public ReadOnlyCollection(System.Collections.Generic.IList list) { } @@ -8479,7 +8476,11 @@ void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } } - [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "CreateSet")] + public static partial class ReadOnlySet + { + public static System.Collections.ObjectModel.ReadOnlySet Create(params System.ReadOnlySpan values) { throw null; } + } + [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "Create")] public partial class ReadOnlySet : System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlySet, System.Collections.Generic.ISet, System.Collections.ICollection, System.Collections.IEnumerable { public ReadOnlySet(System.Collections.Generic.ISet @set) { } From ae5a8e4d633e62380c940c499afed4f1f575ae34 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Thu, 9 Jan 2025 01:41:49 +0700 Subject: [PATCH 09/11] Updated the `CollectionBuilder` attribute to reference `ReadOnlySet` instead of `ReadOnlyCollection`, reflecting a change in the collection type used in the `ReadOnlySet` class. --- src/libraries/System.Runtime/ref/System.Runtime.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 86e2abe3598f75..065c978dc745d2 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8480,7 +8480,7 @@ public static partial class ReadOnlySet { public static System.Collections.ObjectModel.ReadOnlySet Create(params System.ReadOnlySpan values) { throw null; } } - [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "Create")] + [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlySet), "Create")] public partial class ReadOnlySet : System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlySet, System.Collections.Generic.ISet, System.Collections.ICollection, System.Collections.IEnumerable { public ReadOnlySet(System.Collections.Generic.ISet @set) { } From 884b7b01da030b1990969803bccaff3969711797 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Thu, 9 Jan 2025 01:57:49 +0700 Subject: [PATCH 10/11] Code Style --- .../src/System/Collections/ObjectModel/ReadOnlySet.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs index 14090c3c6f1a2f..14fce74194a9d1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs @@ -118,11 +118,15 @@ public static class ReadOnlySet public static ReadOnlySet Create(params ReadOnlySpan values) { if (values.IsEmpty) + { return ReadOnlySet.Empty; + } HashSet hashSet = []; foreach (T value in values) + { hashSet.Add(value); + } return new ReadOnlySet(hashSet); } From 822639bee106c8ae114127241da7d3092633d8d5 Mon Sep 17 00:00:00 2001 From: Alexander Radchenko Date: Fri, 10 Jan 2025 00:56:21 +0700 Subject: [PATCH 11/11] Rename methods in ReadOnlyCollection and ReadOnlySet Updated `ReadOnlyCollection` to rename `Create` to `CreateCollection` and introduced `CreateSet` for creating `ReadOnlySet` instances from spans. Adjusted `CollectionBuilder` attributes accordingly and removed the static `ReadOnlySet` class, integrating its functionality into the main class. Updated the `ReadOnlyCollection` partial class in `System.Runtime.cs` to maintain API consistency. --- .../ObjectModel/ReadOnlyCollection.cs | 28 ++++++++++++++-- .../Collections/ObjectModel/ReadOnlySet.cs | 32 +------------------ .../System.Runtime/ref/System.Runtime.cs | 11 +++---- 3 files changed, 31 insertions(+), 40 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs index ace2a09298861f..c4fc971b856512 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyCollection.cs @@ -8,7 +8,7 @@ namespace System.Collections.ObjectModel { [Serializable] - [CollectionBuilder(typeof(ReadOnlyCollection), "Create")] + [CollectionBuilder(typeof(ReadOnlyCollection), "CreateCollection")] [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] [DebuggerDisplay("Count = {Count}")] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] @@ -244,7 +244,31 @@ public static class ReadOnlyCollection /// The type of elements in the collection. /// The span of values to include in the collection. /// A new containing the specified values. - public static ReadOnlyCollection Create(params ReadOnlySpan values) => + public static ReadOnlyCollection CreateCollection(params ReadOnlySpan values) => values.IsEmpty ? ReadOnlyCollection.Empty : new ReadOnlyCollection(values.ToArray()); + + /// + /// Creates a new from the specified span of values. + /// This method (simplifies collection initialization)[/dotnet/csharp/language-reference/operators/collection-expressions] + /// to create a new with the specified values. + /// + /// The type of elements in the collection. + /// The span of values to include in the collection. + /// A new containing the specified values. + public static ReadOnlySet CreateSet(params ReadOnlySpan values) + { + if (values.IsEmpty) + { + return ReadOnlySet.Empty; + } + + HashSet hashSet = []; + foreach (T value in values) + { + hashSet.Add(value); + } + + return new ReadOnlySet(hashSet); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs index 14fce74194a9d1..6eeb927248d104 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlySet.cs @@ -9,7 +9,7 @@ namespace System.Collections.ObjectModel { /// Represents a read-only, generic set of values. /// The type of values in the set. - [CollectionBuilder(typeof(ReadOnlySet), "Create")] + [CollectionBuilder(typeof(ReadOnlyCollection), "CreateSet")] [DebuggerDisplay("Count = {Count}")] public class ReadOnlySet : IReadOnlySet, ISet, ICollection { @@ -101,34 +101,4 @@ public IEnumerator GetEnumerator() => /// bool ICollection.Remove(T item) => throw new NotSupportedException(); } - - /// - /// Provides static methods for read-only sets. - /// - public static class ReadOnlySet - { - /// - /// Creates a new from the specified span of values. - /// This method (simplifies collection initialization)[/dotnet/csharp/language-reference/operators/collection-expressions] - /// to create a new with the specified values. - /// - /// The type of elements in the collection. - /// The span of values to include in the collection. - /// A new containing the specified values. - public static ReadOnlySet Create(params ReadOnlySpan values) - { - if (values.IsEmpty) - { - return ReadOnlySet.Empty; - } - - HashSet hashSet = []; - foreach (T value in values) - { - hashSet.Add(value); - } - - return new ReadOnlySet(hashSet); - } - } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 065c978dc745d2..533b2bc75bf620 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -8368,9 +8368,10 @@ void System.Collections.IList.Remove(object? value) { } } public static partial class ReadOnlyCollection { - public static System.Collections.ObjectModel.ReadOnlyCollection Create(params System.ReadOnlySpan values) { throw null; } + public static System.Collections.ObjectModel.ReadOnlyCollection CreateCollection(params System.ReadOnlySpan values) { throw null; } + public static System.Collections.ObjectModel.ReadOnlySet CreateSet(params System.ReadOnlySpan values) { throw null; } } - [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "Create")] + [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "CreateCollection")] public partial class ReadOnlyCollection : System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList { public ReadOnlyCollection(System.Collections.Generic.IList list) { } @@ -8476,11 +8477,7 @@ void System.Collections.ICollection.CopyTo(System.Array array, int index) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } } - public static partial class ReadOnlySet - { - public static System.Collections.ObjectModel.ReadOnlySet Create(params System.ReadOnlySpan values) { throw null; } - } - [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlySet), "Create")] + [System.Runtime.CompilerServices.CollectionBuilder(typeof(System.Collections.ObjectModel.ReadOnlyCollection), "CreateSet")] public partial class ReadOnlySet : System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlySet, System.Collections.Generic.ISet, System.Collections.ICollection, System.Collections.IEnumerable { public ReadOnlySet(System.Collections.Generic.ISet @set) { }