Add .NET debugging attributes (#14097)
I've been working with a large Protobuf model and noticed debugging issues that can easily be improved.
Add debugging attributes to collections, `ByteString`, and descriptors. No impact on runtime behavior. Debugger type proxies are what the debugger displays by default, but full data is available by selecting "Raw View".
`RepeatedField` and `MapField` now display their item count. This is standard across .NET collections. For example, .NET's list and dictionary both display `Count = {Count}`.
Note that previously, Protobuf collections displayed the result of `ToString`, which returned JSON. The JSON debug view isn't useful when there is too much content to display at once because it's truncated. That experience will be fairly common.
Count benefits:
* Always useful
* Makes debugging Protobuf collections feel more like regular .NET collections
* Collection contents is now easier to access with debugger type proxies
No dependency between this PR and https://github.com/protocolbuffers/protobuf/pull/13838. Each can be merged independently.
**MapField before:**

**MapField after:**

**RepeatedField before:**

**RepeatedField after:**

**ByteString before:**

**ByteString after:**

**Descriptor before:**

**Descriptor after:**

**TypeRegistry before:**

**TypeRegistry after:**

Closes #14097
COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/14097 from JamesNK:jamesnk/debugging f0dea3464d471042a59e9c0000a5e65e8c9bc46b
PiperOrigin-RevId: 568444117
diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs
index 2e18976..f0be958 100644
--- a/csharp/src/Google.Protobuf/Collections/MapField.cs
+++ b/csharp/src/Google.Protobuf/Collections/MapField.cs
@@ -11,6 +11,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security;
@@ -43,6 +44,8 @@
/// in future versions.
/// </para>
/// </remarks>
+ [DebuggerDisplay("Count = {Count}")]
+ [DebuggerTypeProxy(typeof(MapField<,>.MapFieldDebugView))]
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary, IReadOnlyDictionary<TKey, TValue>
{
private static readonly EqualityComparer<TValue> ValueEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<TValue>();
@@ -683,5 +686,18 @@
}
}
}
+
+ private sealed class MapFieldDebugView
+ {
+ private readonly MapField<TKey, TValue> map;
+
+ public MapFieldDebugView(MapField<TKey, TValue> map)
+ {
+ this.map = map;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public KeyValuePair<TKey, TValue>[] Items => map.list.ToArray();
+ }
}
}
diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
index 38e7015..8bf410a 100644
--- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
+++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs
@@ -10,7 +10,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Security;
namespace Google.Protobuf.Collections
@@ -24,6 +26,8 @@
/// supported by Protocol Buffers but nor does it guarantee that all operations will work in such cases.
/// </remarks>
/// <typeparam name="T">The element type of the repeated field.</typeparam>
+ [DebuggerDisplay("Count = {Count}")]
+ [DebuggerTypeProxy(typeof(RepeatedField<>.RepeatedFieldDebugView))]
public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IReadOnlyList<T>
{
private static readonly EqualityComparer<T> EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<T>();
@@ -642,5 +646,18 @@
}
}
#endregion
+
+ private sealed class RepeatedFieldDebugView
+ {
+ private readonly RepeatedField<T> list;
+
+ public RepeatedFieldDebugView(RepeatedField<T> list)
+ {
+ this.list = list;
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public T[] Items => list.ToArray();
+ }
}
}