Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve ArraySegment debugging #90488

Merged
merged 10 commits into from
Oct 30, 2023
Merged

Improve ArraySegment debugging #90488

merged 10 commits into from
Oct 30, 2023

Conversation

pedrobsaila
Copy link
Contributor

Fixes #88961

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Aug 13, 2023
@ghost
Copy link

ghost commented Aug 13, 2023

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

Fixes #88961

Author: pedrobsaila
Assignees: -
Labels:

area-System.Runtime

Milestone: -

/// For <see cref="ArraySegment{Char}"/>, returns a new instance of string that represents the characters pointed to by the segment.
/// Otherwise, returns a <see cref="string"/> with the name of the type and the number of elements.
/// </summary>
public override string ToString()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ToString is an observable behavior change. The common approach is using a private property called DebuggerDisplay.

Comment on lines 288 to 299
if (typeof(T) == typeof(char))
{
StringBuilder stringBuilder = new StringBuilder(_count);

for (int i = 0; i < _count; i++)
{
T element = this[i];
stringBuilder.Append(Unsafe.As<T, char>(ref element));
}

return stringBuilder.ToString();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use a StringBuilder, just call string constructor like ReadOnlySpan<T>.ToString.

{
if (typeof(T) == typeof(char))
{
return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _array![_offset]), _count));
Copy link
Member

@stephentoub stephentoub Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _array![_offset]), _count));
return AsSpan().ToString();

Comment on lines 292 to 303
private sealed class ArraySegmentDebugView<U>
{
private readonly U[] _array;

public ArraySegmentDebugView(ArraySegment<U> arraySegments)
{
_array = arraySegments.ToArray();
}

[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public U[] Items => _array;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private sealed class ArraySegmentDebugView<U>
{
private readonly U[] _array;
public ArraySegmentDebugView(ArraySegment<U> arraySegments)
{
_array = arraySegments.ToArray();
}
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public U[] Items => _array;
}
private sealed class ArraySegmentDebugView(ArraySegment<T> array)
{
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public T[] Items => array.AsSpan().ToArray();
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we started to roll-in primary constructor in runtime?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for simple uses like debugger type proxies.

Comment on lines 292 to 296
private sealed class ArraySegmentDebugView<U>(ArraySegment<U> array)
{
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public U[] Items => array.AsSpan().ToArray();
}
Copy link
Member

@stephentoub stephentoub Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a nested type, this should not introduce another generic type parameter.

Suggested change
private sealed class ArraySegmentDebugView<U>(ArraySegment<U> array)
{
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public U[] Items => array.AsSpan().ToArray();
}
private sealed class ArraySegmentDebugView(ArraySegment<T> array)
{
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public T[] Items => array.AsSpan().ToArray();
}

Copy link
Contributor Author

@pedrobsaila pedrobsaila Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It fails on compilation with CS0416 error : '.System.ArraySegment.ArraySegmentDebugView': an attribute argument cannot use type parameters

See https://github.com/dotnet/runtime/runs/15879904568

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The attribute should be:

[DebuggerTypeProxy(typeof(ArraySegment<>.ArraySegmentDebugView))]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the help ^^

return this.AsSpan().ToString();
}

return $"System.ArraySegment<{typeof(T).Name}>[{_count}]";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original issue wanted the elements to be displayed in such case I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure to understand. What is the issue exactly with this implementation ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original issue wanted the elements to be displayed in such case I think.

We don't do that for other collections. I'm not sure why we'd do it here.

Copy link
Contributor Author

@pedrobsaila pedrobsaila Aug 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can someone explains to me what should be done here ? I used the same code as the one for Span

See Span debugging support for ideas of improvement.

/// <summary>
/// For <see cref="Span{Char}"/>, returns a new instance of string that represents the characters pointed to by the span.
/// Otherwise, returns a <see cref="string"/> with the name of the type and the number of elements.
/// </summary>
public override string ToString()
{
if (typeof(T) == typeof(char))
{
return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _reference), _length));
}
return $"System.Span<{typeof(T).Name}>[{_length}]";
}

Copy link
Member

@JamesNK JamesNK Aug 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For types with a Count such as this, the debugger display usually displays Count = XXX.

e.g.

I would lean towards doing that. However, I don't own this code. @stephentoub or someone else on the dotnet/runtime team, could you clarify what the debugger display should be?

Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pedrobsaila thank you for your contribution!

I think that we can simplify the solution, PTAL at my suggestion. Thank you!

Comment on lines 23 to 24
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(ArraySegment<>.ArraySegmentDebugView))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that we should simply mimic what List<T> does:

https://github.com/dotnet/runtime/blob/568ed4125d0ae62c0d82d8f3703854fcc04bf32d/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/List.cs#L17C1-L18

ArraySegment<T> implements IList<T> which extends ICollection<T> so it should work OOTB without any other code changes being required

Suggested change
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(ArraySegment<>.ArraySegmentDebugView))]
[DebuggerTypeProxy(typeof(ICollectionDebugView<>))]
[DebuggerDisplay("Count = {Count}")]

@ghost ghost added the needs-author-action An issue or pull request that requires more info or actions from the author. label Oct 27, 2023
@adamsitnik adamsitnik self-assigned this Oct 27, 2023
@ghost ghost removed the needs-author-action An issue or pull request that requires more info or actions from the author. label Oct 28, 2023
Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you @pedrobsaila !

@adamsitnik adamsitnik merged commit e5c631d into dotnet:main Oct 30, 2023
171 of 175 checks passed
@adamsitnik adamsitnik added this to the 9.0.0 milestone Oct 30, 2023
@pedrobsaila pedrobsaila deleted the 88961 branch October 30, 2023 09:01
@ghost ghost locked as resolved and limited conversation to collaborators Nov 29, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Runtime community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve ArraySegment debugging
6 participants