| File: System\Diagnostics\ActivityTagsCollection.cs | Web Access |
| Project: src\runtime\src\libraries\System.Diagnostics.DiagnosticSource\src\System.Diagnostics.DiagnosticSource.csproj (System.Diagnostics.DiagnosticSource) |
// 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; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace System.Diagnostics { /// <summary> /// ActivityTagsCollection is a collection class used to store tracing tags. /// This collection will be used with classes like <see cref="ActivityEvent"/> and <see cref="ActivityLink"/>. /// This collection behaves as follows: /// - The collection items will be ordered according to how they are added. /// - Don't allow duplication of items with the same key. /// - When using the indexer to store an item in the collection: /// - If the item has a key that previously existed in the collection and the value is null, the collection item matching the key will be removed from the collection. /// - If the item has a key that previously existed in the collection and the value is not null, the new item value will replace the old value stored in the collection. /// - Otherwise, the item will be added to the collection. /// - Add method will add a new item to the collection if an item doesn't already exist with the same key. Otherwise, it will throw an exception. /// </summary> public class ActivityTagsCollection : IDictionary<string, object?> { private readonly List<KeyValuePair<string, object?>> _list = new List<KeyValuePair<string, object?>>(); /// <summary> /// Create a new instance of the collection. /// </summary> public ActivityTagsCollection() { } /// <summary> /// Create a new instance of the collection and store the input list items in the collection. /// </summary> /// <param name="list">Initial list to store in the collection.</param> public ActivityTagsCollection(IEnumerable<KeyValuePair<string, object?>> list) { ArgumentNullException.ThrowIfNull(list); foreach (KeyValuePair<string, object?> kvp in list) { if (kvp.Key != null) { this[kvp.Key] = kvp.Value; } } } /// <summary> /// Get or set collection item /// When setting a value to this indexer property, the following behavior will be observed: /// - If the key previously existed in the collection and the value is null, the collection item matching the key will get removed from the collection. /// - If the key previously existed in the collection and the value is not null, the value will replace the old value stored in the collection. /// - Otherwise, a new item will get added to the collection. /// </summary> /// <value>Object mapped to the key</value> public object? this[string key] { get { int index = FindIndex(key); return index < 0 ? null : _list[index].Value; } set { ArgumentNullException.ThrowIfNull(key); int index = FindIndex(key); if (value == null) { if (index >= 0) { _list.RemoveAt(index); } return; } if (index >= 0) { _list[index] = new KeyValuePair<string, object?>(key, value); } else { _list.Add(new KeyValuePair<string, object?>(key, value)); } } } /// <summary> /// Get the list of the keys of all stored tags. /// </summary> public ICollection<string> Keys { get { List<string> list = new List<string>(_list.Count); foreach (KeyValuePair<string, object?> kvp in _list) { list.Add(kvp.Key); } return list; } } /// <summary> /// Get the list of the values of all stored tags. /// </summary> public ICollection<object?> Values { get { List<object?> list = new List<object?>(_list.Count); foreach (KeyValuePair<string, object?> kvp in _list) { list.Add(kvp.Value); } return list; } } /// <summary> /// Gets a value indicating whether the collection is read-only. /// </summary> public bool IsReadOnly => false; /// <summary> /// Gets the number of elements contained in the collection. /// </summary> public int Count => _list.Count; /// <summary> /// Adds a tag with the provided key and value to the collection. /// This collection doesn't allow adding two tags with the same key. /// </summary> /// <param name="key">The tag key.</param> /// <param name="value">The tag value.</param> public void Add(string key, object? value) { ArgumentNullException.ThrowIfNull(key); int index = FindIndex(key); if (index >= 0) { throw new InvalidOperationException(SR.Format(SR.KeyAlreadyExist, key)); } _list.Add(new KeyValuePair<string, object?>(key, value)); } /// <summary> /// Adds an item to the collection /// </summary> /// <param name="item">Key and value pair of the tag to add to the collection.</param> public void Add(KeyValuePair<string, object?> item) { if (item.Key == null) { throw new ArgumentNullException(nameof(item)); } int index = FindIndex(item.Key); if (index >= 0) { throw new InvalidOperationException(SR.Format(SR.KeyAlreadyExist, item.Key)); } _list.Add(item); } /// <summary> /// Removes all items from the collection. /// </summary> public void Clear() => _list.Clear(); public bool Contains(KeyValuePair<string, object?> item) => _list.Contains(item); /// <summary> /// Determines whether the collection contains an element with the specified key. /// </summary> /// <param name="key"></param> /// <returns>True if the collection contains tag with that key. False otherwise.</returns> public bool ContainsKey(string key) => FindIndex(key) >= 0; /// <summary> /// Copies the elements of the collection to an array, starting at a particular array index. /// </summary> /// <param name="array">The array that is the destination of the elements copied from collection.</param> /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param> public void CopyTo(KeyValuePair<string, object?>[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex); /// <summary> /// Returns an enumerator that iterates through the collection. /// </summary> IEnumerator<KeyValuePair<string, object?>> IEnumerable<KeyValuePair<string, object?>>.GetEnumerator() => new Enumerator(_list); /// <summary> /// Returns an enumerator that iterates through the collection. /// </summary> public Enumerator GetEnumerator() => new Enumerator(_list); /// <summary> /// Returns an enumerator that iterates through the collection. /// </summary> IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_list); /// <summary> /// Removes the tag with the specified key from the collection. /// </summary> /// <param name="key">The tag key</param> /// <returns>True if the item existed and removed. False otherwise.</returns> public bool Remove(string key) { ArgumentNullException.ThrowIfNull(key); int index = FindIndex(key); if (index >= 0) { _list.RemoveAt(index); return true; } return false; } /// <summary> /// Removes the first occurrence of a specific item from the collection. /// </summary> /// <param name="item">The tag key value pair to remove.</param> /// <returns>True if item was successfully removed from the collection; otherwise, false. This method also returns false if item is not found in the original collection.</returns> public bool Remove(KeyValuePair<string, object?> item) => _list.Remove(item); /// <summary> /// Gets the value associated with the specified key. /// </summary> /// <param name="key">The tag key.</param> /// <param name="value">The tag value.</param> /// <returns>When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized.</returns> public bool TryGetValue(string key, out object? value) { int index = FindIndex(key); if (index >= 0) { value = _list[index].Value; return true; } value = null; return false; } /// <summary> /// FindIndex finds the index of item in the list having a key matching the input key. /// We didn't use List.FindIndex to avoid the extra allocation caused by the closure when calling the Predicate delegate. /// </summary> /// <param name="key">The key to search the item in the list</param> /// <returns>The index of the found item, or -1 if the item not found.</returns> private int FindIndex(string key) { for (int i = 0; i < _list.Count; i++) { if (_list[i].Key == key) { return i; } } return -1; } public struct Enumerator : IEnumerator<KeyValuePair<string, object?>>, IEnumerator { private List<KeyValuePair<string, object?>>.Enumerator _enumerator; internal Enumerator(List<KeyValuePair<string, object?>> list) => _enumerator = list.GetEnumerator(); public KeyValuePair<string, object?> Current => _enumerator.Current; object IEnumerator.Current => ((IEnumerator)_enumerator).Current; public void Dispose() => _enumerator.Dispose(); public bool MoveNext() => _enumerator.MoveNext(); void IEnumerator.Reset() => ((IEnumerator)_enumerator).Reset(); } } }