diff --git a/src/libraries/Common/tests/System/Collections/DebugView.Tests.cs b/src/libraries/Common/tests/System/Collections/DebugView.Tests.cs index a6479be6e292d..07b447c4c4de0 100644 --- a/src/libraries/Common/tests/System/Collections/DebugView.Tests.cs +++ b/src/libraries/Common/tests/System/Collections/DebugView.Tests.cs @@ -1,7 +1,10 @@ // 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.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; @@ -47,8 +50,8 @@ private static IEnumerable TestDebuggerAttributes_GenericDictionaries( new ("[\"Two\"]", "2"), } }; - CustomKeyedCollection collection = new (); - collection.GetKeyForItemHandler = value => (2 * value).ToString(); + CustomKeyedCollection collection = new(); + collection.GetKeyForItemHandler = value => (2 * value).ToString(); collection.InsertItem(0, 1); collection.InsertItem(1, 3); yield return new object[] { collection, @@ -58,6 +61,53 @@ private static IEnumerable TestDebuggerAttributes_GenericDictionaries( new ("[\"6\"]", "3"), } }; + + yield return new object[] { new ConcurrentDictionary(new KeyValuePair[] { new(1, "One"), new(2, "Two") }), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + } + + private static IEnumerable TestDebuggerAttributes_AdditionalGenericDictionaries() + { + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToFrozenDictionary(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToImmutableDictionary(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToImmutableDictionary().ToBuilder(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToImmutableSortedDictionary(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; + yield return new object[] { new Dictionary { { 1, "One" }, { 2, "Two" } }.ToImmutableSortedDictionary().ToBuilder(), + new KeyValuePair[] + { + new ("[1]", "\"One\""), + new ("[2]", "\"Two\""), + } + }; } private static IEnumerable TestDebuggerAttributes_NonGenericDictionaries() @@ -162,12 +212,14 @@ private static IEnumerable TestDebuggerAttributes_ListInputs() public static IEnumerable TestDebuggerAttributes_InputsPresentedAsDictionary() { + var testCases = TestDebuggerAttributes_NonGenericDictionaries() + .Concat(TestDebuggerAttributes_AdditionalGenericDictionaries()); #if !NETFRAMEWORK - return TestDebuggerAttributes_NonGenericDictionaries() + return testCases .Concat(TestDebuggerAttributes_GenericDictionaries()); #else - // In .Net Framework only non-generic dictionaries are displayed in a dictionary format by the debugger. - return TestDebuggerAttributes_NonGenericDictionaries(); + // In .Net Framework, the generic dictionaries that are part of the framework are displayed in a list format by the debugger. + return testCases; #endif } diff --git a/src/libraries/Common/tests/System/Diagnostics/DebuggerAttributes.cs b/src/libraries/Common/tests/System/Diagnostics/DebuggerAttributes.cs index 0912d647d5e42..07b212f8a24ec 100644 --- a/src/libraries/Common/tests/System/Diagnostics/DebuggerAttributes.cs +++ b/src/libraries/Common/tests/System/Diagnostics/DebuggerAttributes.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Data; using System.Linq; using System.Reflection; using System.Text; diff --git a/src/libraries/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj b/src/libraries/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj index 0dd332a6faead..90b815473b8e8 100644 --- a/src/libraries/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj +++ b/src/libraries/System.Collections.Concurrent/src/System.Collections.Concurrent.csproj @@ -21,6 +21,8 @@ Link="System\Collections\HashHelpers.cs" /> + diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs index 7f63ffb0f8b20..5748725a154a3 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentDictionary.cs @@ -2733,12 +2733,17 @@ public IDictionaryDebugView(IDictionary dictionary) } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public KeyValuePair[] Items + public DebugViewDictionaryItem[] Items { get { - var items = new KeyValuePair[_dictionary.Count]; - _dictionary.CopyTo(items, 0); + var keyValuePairs = new KeyValuePair[_dictionary.Count]; + _dictionary.CopyTo(keyValuePairs, 0); + var items = new DebugViewDictionaryItem[keyValuePairs.Length]; + for (int i = 0; i < items.Length; i++) + { + items[i] = new DebugViewDictionaryItem(keyValuePairs[i]); + } return items; } } diff --git a/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs b/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs index ce05af556da9c..c92689a6b034f 100644 --- a/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs +++ b/src/libraries/System.Collections.Concurrent/tests/ConcurrentDictionary/ConcurrentDictionaryTests.cs @@ -629,27 +629,6 @@ public static void TestConstructor_ConcurrencyLevel(int concurrencyLevel) Assert.Equal(2, dictionary.Count); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(new ConcurrentDictionary()); - ConcurrentDictionary dict = new ConcurrentDictionary(); - dict.TryAdd("One", 1); - dict.TryAdd("Two", 2); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(dict); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(dict, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(new ConcurrentDictionary()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public static void TestNullComparer() { diff --git a/src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csproj b/src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csproj index 8f53404e87df3..47c5898a66c10 100644 --- a/src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csproj +++ b/src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csproj @@ -12,11 +12,11 @@ The System.Collections.Immutable library is built-in as part of the shared frame - - + + diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs index fac2b6163c13b..0476b66430d24 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs @@ -27,7 +27,7 @@ public sealed partial class ImmutableDictionary /// /// [DebuggerDisplay("Count = {Count}")] - [DebuggerTypeProxy(typeof(ImmutableDictionaryBuilderDebuggerProxy<,>))] + [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] public sealed class Builder : IDictionary, IReadOnlyDictionary, IDictionary { /// @@ -709,36 +709,4 @@ private bool Apply(MutationResult result) } } } - - /// - /// A simple view of the immutable collection that the debugger can show to the developer. - /// - internal sealed class ImmutableDictionaryBuilderDebuggerProxy where TKey : notnull - { - /// - /// The collection to be enumerated. - /// - private readonly ImmutableDictionary.Builder _map; - - /// - /// The simple view of the collection. - /// - private KeyValuePair[]? _contents; - - /// - /// Initializes a new instance of the class. - /// - /// The collection to display in the debugger - public ImmutableDictionaryBuilderDebuggerProxy(ImmutableDictionary.Builder map) - { - Requires.NotNull(map, nameof(map)); - _map = map; - } - - /// - /// Gets a simple debugger-viewable collection. - /// - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public KeyValuePair[] Contents => _contents ??= _map.ToArray(_map.Count); - } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs index 3df4cd8e08b4a..1522e0a8f0541 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs @@ -12,16 +12,38 @@ namespace System.Collections.Immutable /// /// The type of the dictionary's keys. /// The type of the dictionary's values. - internal sealed class ImmutableDictionaryDebuggerProxy : ImmutableEnumerableDebuggerProxy> where TKey : notnull + /// + /// This class should only be used with immutable dictionaries, since it + /// caches the dictionary into an array for display in the debugger. + /// + internal sealed class ImmutableDictionaryDebuggerProxy where TKey : notnull { + /// + /// The dictionary to show to the debugger. + /// + private readonly IReadOnlyDictionary _dictionary; + + /// + /// The contents of the dictionary, cached into an array. + /// + private DebugViewDictionaryItem[]? _cachedContents; + /// /// Initializes a new instance of the class. /// - /// The enumerable to show in the debugger. + /// The dictionary to show in the debugger. public ImmutableDictionaryDebuggerProxy(IReadOnlyDictionary dictionary) - : base(enumerable: dictionary) { + Requires.NotNull(dictionary, nameof(dictionary)); + _dictionary = dictionary; } + + /// + /// Gets the contents of the dictionary for display in the debugger. + /// + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public DebugViewDictionaryItem[] Contents => _cachedContents + ??= _dictionary.Select(kv => new DebugViewDictionaryItem(kv)).ToArray(_dictionary.Count); } /// diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs index ce8e4a0f17996..ff5fe9ae03058 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs @@ -25,7 +25,7 @@ public sealed partial class ImmutableSortedDictionary where TKey : /// /// [DebuggerDisplay("Count = {Count}")] - [DebuggerTypeProxy(typeof(ImmutableSortedDictionaryBuilderDebuggerProxy<,>))] + [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] public sealed class Builder : IDictionary, IReadOnlyDictionary, IDictionary { /// @@ -645,35 +645,4 @@ public ImmutableSortedDictionary ToImmutable() #endregion } } - /// - /// A simple view of the immutable collection that the debugger can show to the developer. - /// - internal sealed class ImmutableSortedDictionaryBuilderDebuggerProxy where TKey : notnull - { - /// - /// The collection to be enumerated. - /// - private readonly ImmutableSortedDictionary.Builder _map; - - /// - /// The simple view of the collection. - /// - private KeyValuePair[]? _contents; - - /// - /// Initializes a new instance of the class. - /// - /// The collection to display in the debugger - public ImmutableSortedDictionaryBuilderDebuggerProxy(ImmutableSortedDictionary.Builder map) - { - Requires.NotNull(map, nameof(map)); - _map = map; - } - - /// - /// Gets a simple debugger-viewable collection. - /// - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public KeyValuePair[] Contents => _contents ??= _map.ToArray(_map.Count); - } } diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTest.cs index 60206a7238b82..82084ea9fbfad 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTest.cs @@ -255,27 +255,6 @@ public void GetValueOrDefaultOfConcreteType() Assert.Equal(5, populated.GetValueOrDefault("a", 1)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public void DebuggerAttributesValid() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableDictionary.CreateBuilder()); - ImmutableDictionary.Builder builder = ImmutableDictionary.CreateBuilder(); - builder.Add(1, "One"); - builder.Add(2, "Two"); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(builder); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(builder, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(ImmutableHashSet.Create()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public void ToImmutableDictionary() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTest.cs index 586f4555c3d8b..63dc704208872 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTest.cs @@ -349,28 +349,6 @@ public void EnumeratorRecyclingMisuse() enumerator.Dispose(); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public void DebuggerAttributesValid() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableDictionary.Create()); - ImmutableDictionary dict = ImmutableDictionary.Create().Add("One", 1).Add("Two", 2); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(dict); - - object rootNode = DebuggerAttributes.GetFieldValue(ImmutableDictionary.Create(), "_root"); - DebuggerAttributes.ValidateDebuggerDisplayReferences(rootNode); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(dict, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(ImmutableHashSet.Create()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public void Clear_NoComparer_ReturnsEmptyWithoutComparer() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs index 6d360a4a34395..feb6a75e7233a 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryBuilderTest.cs @@ -255,27 +255,6 @@ public void GetValueOrDefaultOfConcreteType() Assert.Equal(5, populated.GetValueOrDefault("a", 1)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public void DebuggerAttributesValid() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableSortedDictionary.CreateBuilder()); - ImmutableSortedDictionary.Builder builder = ImmutableSortedDictionary.CreateBuilder(); - builder.Add(1, "One"); - builder.Add(2, "Two"); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(builder); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(builder, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(ImmutableSortedDictionary.CreateBuilder()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public void ValueRef() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs index 492d319e2c3db..47725fdb2ac99 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs @@ -402,28 +402,6 @@ public void Remove_EmptyDictionary_DoesNothing() Assert.Equal(0, dictionary.Remove(2).Count); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public void DebuggerAttributesValid() - { - DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableSortedDictionary.Create()); - ImmutableSortedDictionary dict = ImmutableSortedDictionary.Create().Add(1, 2).Add(2, 3).Add(3, 4); - DebuggerAttributeInfo info = DebuggerAttributes.ValidateDebuggerTypeProxyProperties(dict); - - object rootNode = DebuggerAttributes.GetFieldValue(ImmutableSortedDictionary.Create(), "_root"); - DebuggerAttributes.ValidateDebuggerDisplayReferences(rootNode); - PropertyInfo itemProperty = info.Properties.Single(pr => pr.GetCustomAttribute().State == DebuggerBrowsableState.RootHidden); - KeyValuePair[] items = itemProperty.GetValue(info.Instance) as KeyValuePair[]; - Assert.Equal(dict, items); - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDebuggerTypeProxyAttributeSupported))] - public static void TestDebuggerAttributes_Null() - { - Type proxyType = DebuggerAttributes.GetProxyType(ImmutableSortedDictionary.Create()); - TargetInvocationException tie = Assert.Throws(() => Activator.CreateInstance(proxyType, (object)null)); - Assert.IsType(tie.InnerException); - } - [Fact] public void ValueRef() { diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs index c082d31a09ff8..8ec7e86bcd0c6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/IDictionaryDebugView.cs @@ -11,9 +11,7 @@ internal sealed class IDictionaryDebugView where TKey : notnull public IDictionaryDebugView(IDictionary dictionary) { - ArgumentNullException.ThrowIfNull(dictionary); - - _dict = dictionary; + _dict = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] @@ -39,9 +37,7 @@ internal sealed class DictionaryKeyCollectionDebugView public DictionaryKeyCollectionDebugView(ICollection collection) { - ArgumentNullException.ThrowIfNull(collection); - - _collection = collection; + _collection = collection ?? throw new ArgumentNullException(nameof(collection)); } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] @@ -62,9 +58,7 @@ internal sealed class DictionaryValueCollectionDebugView public DictionaryValueCollectionDebugView(ICollection collection) { - ArgumentNullException.ThrowIfNull(collection); - - _collection = collection; + _collection = collection ?? throw new ArgumentNullException(nameof(collection)); } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]