Add additional bool-only ToFrozenDictionary/Set overloads (#81256)
authorStephen Toub <stoub@microsoft.com>
Fri, 27 Jan 2023 11:36:27 +0000 (06:36 -0500)
committerGitHub <noreply@github.com>
Fri, 27 Jan 2023 11:36:27 +0000 (06:36 -0500)
API review decided to include additional overloads to enable only specifying the bool.

src/libraries/System.Collections.Immutable/ref/System.Collections.Immutable.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenDictionary.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenSet.cs
src/libraries/System.Collections.Immutable/tests/Frozen/FrozenDictionaryTests.cs
src/libraries/System.Collections.Immutable/tests/Frozen/FrozenSetTests.cs

index 4641aa6..bb9e96e 100644 (file)
@@ -10,6 +10,7 @@ namespace System.Collections.Frozen
     {
         public static System.Collections.Frozen.FrozenDictionary<TKey, TValue> ToFrozenDictionary<TKey, TValue>(this System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>> source, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) where TKey : notnull { throw null; }
         public static System.Collections.Frozen.FrozenDictionary<TKey, TValue> ToFrozenDictionary<TKey, TValue>(this System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>> source, System.Collections.Generic.IEqualityComparer<TKey>? comparer, bool optimizeForReading) where TKey : notnull { throw null; }
+        public static System.Collections.Frozen.FrozenDictionary<TKey, TValue> ToFrozenDictionary<TKey, TValue>(this System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>> source, bool optimizeForReading) where TKey : notnull { throw null; }
         public static System.Collections.Frozen.FrozenDictionary<TKey, TSource> ToFrozenDictionary<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) where TKey : notnull { throw null; }
         public static System.Collections.Frozen.FrozenDictionary<TKey, TElement> ToFrozenDictionary<TSource, TKey, TElement>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Func<TSource, TElement> elementSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer = null) where TKey : notnull { throw null; }
     }
@@ -73,6 +74,7 @@ namespace System.Collections.Frozen
     {
         public static System.Collections.Frozen.FrozenSet<T> ToFrozenSet<T>(this System.Collections.Generic.IEnumerable<T> source, System.Collections.Generic.IEqualityComparer<T>? comparer = null) { throw null; }
         public static System.Collections.Frozen.FrozenSet<T> ToFrozenSet<T>(this System.Collections.Generic.IEnumerable<T> source, System.Collections.Generic.IEqualityComparer<T>? comparer, bool optimizeForReading) { throw null; }
+        public static System.Collections.Frozen.FrozenSet<T> ToFrozenSet<T>(this System.Collections.Generic.IEnumerable<T> source, bool optimizeForReading) { throw null; }
     }
     public abstract partial class FrozenSet<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.ISet<T>, System.Collections.ICollection, System.Collections.IEnumerable
     {
index 79c65f8..65de1ad 100644 (file)
@@ -40,6 +40,33 @@ namespace System.Collections.Frozen
 
         /// <summary>Creates a <see cref="FrozenDictionary{TKey, TValue}"/> with the specified key/value pairs.</summary>
         /// <param name="source">The key/value pairs to use to populate the dictionary.</param>
+        /// <param name="optimizeForReading">
+        /// <see langword="true"/> to do more work as part of dictionary construction to optimize for subsequent reading of the data;
+        /// <see langword="false"/> to prefer making construction more efficient. The default is <see langword="false"/>.
+        /// </param>
+        /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
+        /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
+        /// <remarks>
+        /// <para>
+        /// Frozen collections are immutable and may be optimized for situations where a collection is created very infrequently but
+        /// is used very frequently at runtime. Setting <paramref name="optimizeForReading"/> to <see langword="true"/> will result in a
+        /// relatively high cost to create the collection in exchange for improved performance when subsequently using the collection.
+        /// Using <see langword="true"/> is ideal for collections that are created once, potentially at the startup of a service, and then
+        /// used throughout the remainder of the lifetime of the service. Because of the high cost of creation, frozen collections should
+        /// only be initialized with trusted input.
+        /// </para>
+        /// <para>
+        /// If the same key appears multiple times in the input, the latter one in the sequence takes precedence. This differs from
+        /// <see cref="M:System.Linq.Enumerable.ToDictionary"/>, with which multiple duplicate keys will result in an exception.
+        /// </para>
+        /// </remarks>
+        /// <returns>A <see cref="FrozenDictionary{TKey, TValue}"/> that contains the specified keys and values.</returns>
+        public static FrozenDictionary<TKey, TValue> ToFrozenDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source, bool optimizeForReading)
+            where TKey : notnull =>
+            ToFrozenDictionary(source, null, optimizeForReading);
+
+        /// <summary>Creates a <see cref="FrozenDictionary{TKey, TValue}"/> with the specified key/value pairs.</summary>
+        /// <param name="source">The key/value pairs to use to populate the dictionary.</param>
         /// <param name="comparer">The comparer implementation to use to compare keys for equality. If null, <see cref="EqualityComparer{TKey}.Default"/> is used.</param>
         /// <param name="optimizeForReading">
         /// <see langword="true"/> to do more work as part of dictionary construction to optimize for subsequent reading of the data;
index 47efdd7..d057036 100644 (file)
@@ -33,6 +33,25 @@ namespace System.Collections.Frozen
 
         /// <summary>Creates a <see cref="FrozenSet{T}"/> with the specified values.</summary>
         /// <param name="source">The values to use to populate the set.</param>
+        /// <param name="optimizeForReading">
+        /// <see langword="true"/> to do more work as part of set construction to optimize for subsequent reading of the data;
+        /// <see langword="false"/> to prefer making construction more efficient. The default is <see langword="false"/>.
+        /// </param>
+        /// <typeparam name="T">The type of the values in the set.</typeparam>
+        /// <returns>A frozen set.</returns>
+        /// <remarks>
+        /// Frozen collections are immutable and may be optimized for situations where a collection is created very infrequently but
+        /// is used very frequently at runtime. Setting <paramref name="optimizeForReading"/> to <see langword="true"/> will result in a
+        /// relatively high cost to create the collection in exchange for improved performance when subsequently using the collection.
+        /// Using <see langword="true"/> is ideal for collections that are created once, potentially at the startup of a service, and then
+        /// used throughout the remainder of the lifetime of the service. Because of the high cost of creation, frozen collections should
+        /// only be initialized with trusted input.
+        /// </remarks>
+        public static FrozenSet<T> ToFrozenSet<T>(this IEnumerable<T> source, bool optimizeForReading) =>
+            ToFrozenSet(source, null, optimizeForReading);
+
+        /// <summary>Creates a <see cref="FrozenSet{T}"/> with the specified values.</summary>
+        /// <param name="source">The values to use to populate the set.</param>
         /// <param name="comparer">The comparer implementation to use to compare values for equality. If null, <see cref="EqualityComparer{T}.Default"/> is used.</param>
         /// <param name="optimizeForReading">
         /// <see langword="true"/> to do more work as part of set construction to optimize for subsequent reading of the data;
index ae1fa7f..12e218f 100644 (file)
@@ -57,6 +57,8 @@ namespace System.Collections.Frozen.Tests
             AssertExtensions.Throws<ArgumentNullException>("source", () => ((Dictionary<TKey, TValue>)null).ToFrozenDictionary());
             AssertExtensions.Throws<ArgumentNullException>("source", () => ((Dictionary<TKey, TValue>)null).ToFrozenDictionary(null));
             AssertExtensions.Throws<ArgumentNullException>("source", () => ((Dictionary<TKey, TValue>)null).ToFrozenDictionary(EqualityComparer<TKey>.Default));
+            AssertExtensions.Throws<ArgumentNullException>("source", () => ((Dictionary<TKey, TValue>)null).ToFrozenDictionary(null, false));
+            AssertExtensions.Throws<ArgumentNullException>("source", () => ((Dictionary<TKey, TValue>)null).ToFrozenDictionary(null, true));
 
             AssertExtensions.Throws<ArgumentNullException>("keySelector", () => Enumerable.Empty<int>().ToFrozenDictionary((Func<int, int>)null));
             AssertExtensions.Throws<ArgumentNullException>("keySelector", () => Enumerable.Empty<int>().ToFrozenDictionary((Func<int, int>)null, EqualityComparer<int>.Default));
@@ -148,6 +150,8 @@ namespace System.Collections.Frozen.Tests
             Assert.Same(FrozenDictionary<TKey, TValue>.Empty, FrozenDictionary<TKey, TValue>.Empty.ToFrozenDictionary(null));
             Assert.Same(FrozenDictionary<TKey, TValue>.Empty, FrozenDictionary<TKey, TValue>.Empty.ToFrozenDictionary(null, false));
             Assert.Same(FrozenDictionary<TKey, TValue>.Empty, FrozenDictionary<TKey, TValue>.Empty.ToFrozenDictionary(null, true));
+            Assert.Same(FrozenDictionary<TKey, TValue>.Empty, FrozenDictionary<TKey, TValue>.Empty.ToFrozenDictionary(false));
+            Assert.Same(FrozenDictionary<TKey, TValue>.Empty, FrozenDictionary<TKey, TValue>.Empty.ToFrozenDictionary(true));
             Assert.Same(FrozenDictionary<TKey, TValue>.Empty, FrozenDictionary<TKey, TValue>.Empty.ToFrozenDictionary(EqualityComparer<TKey>.Default));
             Assert.Same(FrozenDictionary<TKey, TValue>.Empty, FrozenDictionary<TKey, TValue>.Empty.ToFrozenDictionary(EqualityComparer<TKey>.Default, false));
             Assert.Same(FrozenDictionary<TKey, TValue>.Empty, FrozenDictionary<TKey, TValue>.Empty.ToFrozenDictionary(EqualityComparer<TKey>.Default, true));
@@ -161,6 +165,18 @@ namespace System.Collections.Frozen.Tests
             Assert.NotSame(frozen, frozen.ToFrozenDictionary(NonDefaultEqualityComparer<TKey>.Instance));
         }
 
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public void ToFrozenDictionary_BoolArg_UsesDefaultComparer(bool optimizeForReading)
+        {
+            Dictionary<TKey, TValue> source = Enumerable.Range(0, 4).ToDictionary(CreateTKey, CreateTValue);
+
+            FrozenDictionary<TKey, TValue> frozen1 = source.ToFrozenDictionary(optimizeForReading);
+
+            Assert.Same(EqualityComparer<TKey>.Default, frozen1.Comparer);
+        }
+
         [Fact]
         public void ToFrozenDictionary_KeySelector_ResultsAreUsed()
         {
index f37774d..a1190c0 100644 (file)
@@ -55,8 +55,14 @@ namespace System.Collections.Frozen.Tests
         public void NullSource_ThrowsException()
         {
             AssertExtensions.Throws<ArgumentNullException>("source", () => ((HashSet<T>)null).ToFrozenSet());
+            AssertExtensions.Throws<ArgumentNullException>("source", () => ((HashSet<T>)null).ToFrozenSet(false));
+            AssertExtensions.Throws<ArgumentNullException>("source", () => ((HashSet<T>)null).ToFrozenSet(true));
             AssertExtensions.Throws<ArgumentNullException>("source", () => ((HashSet<T>)null).ToFrozenSet(null));
+            AssertExtensions.Throws<ArgumentNullException>("source", () => ((HashSet<T>)null).ToFrozenSet(null, false));
+            AssertExtensions.Throws<ArgumentNullException>("source", () => ((HashSet<T>)null).ToFrozenSet(null, true));
             AssertExtensions.Throws<ArgumentNullException>("source", () => ((HashSet<T>)null).ToFrozenSet(EqualityComparer<T>.Default));
+            AssertExtensions.Throws<ArgumentNullException>("source", () => ((HashSet<T>)null).ToFrozenSet(EqualityComparer<T>.Default, false));
+            AssertExtensions.Throws<ArgumentNullException>("source", () => ((HashSet<T>)null).ToFrozenSet(EqualityComparer<T>.Default, true));
         }
 
         [Fact]
@@ -136,6 +142,8 @@ namespace System.Collections.Frozen.Tests
             Assert.Same(FrozenSet<T>.Empty, FrozenSet<T>.Empty.ToFrozenSet(null));
             Assert.Same(FrozenSet<T>.Empty, FrozenSet<T>.Empty.ToFrozenSet(null, false));
             Assert.Same(FrozenSet<T>.Empty, FrozenSet<T>.Empty.ToFrozenSet(null, true));
+            Assert.Same(FrozenSet<T>.Empty, FrozenSet<T>.Empty.ToFrozenSet(false));
+            Assert.Same(FrozenSet<T>.Empty, FrozenSet<T>.Empty.ToFrozenSet(true));
             Assert.Same(FrozenSet<T>.Empty, FrozenSet<T>.Empty.ToFrozenSet(EqualityComparer<T>.Default));
             Assert.Same(FrozenSet<T>.Empty, FrozenSet<T>.Empty.ToFrozenSet(EqualityComparer<T>.Default, false));
             Assert.Same(FrozenSet<T>.Empty, FrozenSet<T>.Empty.ToFrozenSet(EqualityComparer<T>.Default, true));
@@ -149,6 +157,18 @@ namespace System.Collections.Frozen.Tests
             Assert.NotSame(frozen, frozen.ToFrozenSet(NonDefaultEqualityComparer<T>.Instance));
         }
 
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public void ToFrozenSet_BoolArg_UsesDefaultComparer(bool optimizeForReading)
+        {
+            HashSet<T> source = new HashSet<T>(Enumerable.Range(0, 4).Select(CreateT));
+
+            FrozenSet<T> frozen = source.ToFrozenSet(optimizeForReading);
+
+            Assert.Same(EqualityComparer<T>.Default, frozen.Comparer);
+        }
+
         public static IEnumerable<object[]> LookupItems_AllItemsFoundAsExpected_MemberData() =>
             from size in new[] { 0, 1, 2, 10, 999, 1024 }
             from comparer in new IEqualityComparer<T>[] { null, EqualityComparer<T>.Default, NonDefaultEqualityComparer<T>.Instance }