diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs index 23834958a4f4cb..5addc00ee27dd6 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs @@ -255,9 +255,6 @@ public string? RootId /// public IEnumerable> TagObjects { -#if ALLOW_PARTIALLY_TRUSTED_CALLERS - [System.Security.SecuritySafeCriticalAttribute] -#endif get => _tags ?? s_emptyTagObjects; } @@ -1272,9 +1269,10 @@ public void Add(T value) } } + // Note: Some customers use this GetEnumerator dynamically to avoid allocations. public Enumerator GetEnumerator() => new Enumerator(_first); - IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_first); - IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_first); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } private class TagsLinkedList : IEnumerable> @@ -1379,9 +1377,10 @@ public void Set(KeyValuePair value) } } + // Note: Some customers use this GetEnumerator dynamically to avoid allocations. public Enumerator> GetEnumerator() => new Enumerator>(_first); - IEnumerator> IEnumerable>.GetEnumerator() => new Enumerator>(_first); - IEnumerator IEnumerable.GetEnumerator() => new Enumerator>(_first); + IEnumerator> IEnumerable>.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public IEnumerable> EnumerateStringValues() { @@ -1399,15 +1398,15 @@ public void Set(KeyValuePair value) } } + // Note: Some customers use this Enumerator dynamically to avoid allocations. private struct Enumerator : IEnumerator { - private readonly LinkedListNode? _head; private LinkedListNode? _nextNode; [AllowNull, MaybeNull] private T _currentItem; public Enumerator(LinkedListNode? head) { - _nextNode = _head = head; + _nextNode = head; _currentItem = default; } @@ -1428,7 +1427,7 @@ public bool MoveNext() return true; } - public void Reset() => _nextNode = _head; + public void Reset() => throw new NotSupportedException(); public void Dispose() { diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs index a085d3103bdbd7..cdd6f15f5682c6 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs @@ -1508,6 +1508,40 @@ public void TestInsertingFirstTag(string key, object value, bool add, int result Assert.Equal(resultCount, a.TagObjects.Count()); } + [Fact] + public void StructEnumerator_TagsLinkedList() + { + // Note: This test verifies the presence of the struct Enumerator on TagsLinkedList used by customers dynamically to avoid allocations. + + Activity a = new Activity("TestActivity"); + a.AddTag("Tag1", true); + + IEnumerable> enumerable = a.TagObjects; + + MethodInfo method = enumerable.GetType().GetMethod("GetEnumerator", BindingFlags.Instance | BindingFlags.Public); + + Assert.NotNull(method); + Assert.False(method.ReturnType.IsInterface); + Assert.True(method.ReturnType.IsValueType); + } + + [Fact] + public void StructEnumerator_GenericLinkedList() + { + // Note: This test verifies the presence of the struct Enumerator on LinkedList used by customers dynamically to avoid allocations. + + Activity a = new Activity("TestActivity"); + a.AddEvent(new ActivityEvent()); + + IEnumerable enumerable = a.Events; + + MethodInfo method = enumerable.GetType().GetMethod("GetEnumerator", BindingFlags.Instance | BindingFlags.Public); + + Assert.NotNull(method); + Assert.False(method.ReturnType.IsInterface); + Assert.True(method.ReturnType.IsValueType); + } + public void Dispose() { Activity.Current = null;