| File: Collections\ItemDictionarySlim.cs | Web Access |
| Project: src\msbuild\src\Build\Microsoft.Build.csproj (Microsoft.Build) |
// 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 Microsoft.Build.Execution; namespace Microsoft.Build.Collections { /// <summary> /// A simple implementation of ItemDictionary intended for populating Scope tables and merging adds/removes, /// without the overhead of locking and additional lookups. /// </summary> internal sealed class ItemDictionarySlim : IEnumerable<KeyValuePair<string, List<ProjectItemInstance>>> { private readonly Dictionary<string, List<ProjectItemInstance>> _itemLists; public ItemDictionarySlim() => _itemLists = new Dictionary<string, List<ProjectItemInstance>>(MSBuildNameIgnoreCaseComparer.Default); /// <summary> /// Gets all items of the given type, or null if there are none. /// </summary> public List<ProjectItemInstance>? this[string itemType] => _itemLists.TryGetValue(itemType, out List<ProjectItemInstance>? itemsOfType) ? itemsOfType : null; /// <summary> /// Returns true if there are any items of the given type. /// </summary> public bool ContainsKey(string itemType) => _itemLists.ContainsKey(itemType); /// <summary> /// Adds a single item to the matching list for its type. /// </summary> public void Add(ProjectItemInstance projectItem) { if (!_itemLists.TryGetValue(projectItem.ItemType, out List<ProjectItemInstance>? list)) { list = []; _itemLists[projectItem.ItemType] = list; } list.Add(projectItem); } /// <summary> /// Imports and merges all items from an existing item dictionary, appending to any existing list. /// </summary> /// <remarks> /// This should only be called when merging between scopes, as it may take list references from the other dictionary. /// This is safe since we create these internal lists, and the owning Scope will always be discarded after a merge. /// </remarks> public void ImportItems(ItemDictionarySlim other) { foreach (KeyValuePair<string, List<ProjectItemInstance>> kvp in other._itemLists) { string itemType = kvp.Key; List<ProjectItemInstance> itemsToAdd = kvp.Value; if (_itemLists.TryGetValue(itemType, out List<ProjectItemInstance>? list)) { // There are already items of this type, so append to the existing list list.AddRange(itemsToAdd); } else { // Otherwise, steal the reference instead of copying items out. _itemLists[itemType] = itemsToAdd; } } } /// <summary> /// Imports and merges all items of the given type, appending to any existing list. /// </summary> public void ImportItemsOfType(string itemType, IEnumerable<ProjectItemInstance> items) { if (!_itemLists.TryGetValue(itemType, out List<ProjectItemInstance>? list)) { list = []; _itemLists[itemType] = list; } list.AddRange(items); } /// <summary> /// Sets the capacity for the item list matching the given type. /// </summary> /// <remarks> /// Useful for dealing with IEnumerable if we know the upper bound or estimate of items to be added. /// </remarks> internal void EnsureCapacityForItemType(string itemType, int capacity) { if (!_itemLists.TryGetValue(itemType, out List<ProjectItemInstance>? list)) { list = new List<ProjectItemInstance>(capacity); _itemLists[itemType] = list; } else if (capacity > list.Capacity) { // Conditional since List.Capacity will throw if set less than its current value. list.Capacity = capacity; } } public Dictionary<string, List<ProjectItemInstance>>.Enumerator GetEnumerator() => _itemLists.GetEnumerator(); IEnumerator<KeyValuePair<string, List<ProjectItemInstance>>> IEnumerable<KeyValuePair<string, List<ProjectItemInstance>>>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } }