File: ImmutableDictionaryExtensions.cs
Web Access
Project: ..\..\..\src\Framework\Microsoft.Build.Framework.csproj (Microsoft.Build.Framework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.Build.Collections;
 
namespace Microsoft.Build.Framework
{
    internal static class ImmutableDictionaryExtensions
    {
        /// <summary>
        /// An empty dictionary pre-configured with a comparer for metadata dictionaries.
        /// </summary>
        public static readonly ImmutableDictionary<string, string> EmptyMetadata =
            ImmutableDictionary<string, string>.Empty.WithComparers(MSBuildNameIgnoreCaseComparer.Default);
 
        /// <summary>
        /// Sets the given items while running a validation function on each key.
        /// </summary>
        /// <remarks>
        /// ProjectItemInstance.TaskItem exposes dictionary values as ProjectMetadataInstance. For perf reasons,
        /// we don't want to internally store ProjectMetadataInstance since it prevents us from sharing immutable
        /// dictionaries with Utilities.TaskItem, and it results in more than 2x memory allocated per-entry.
        /// </remarks>
        public static ImmutableDictionary<string, string> SetItems(
            this ImmutableDictionary<string, string> dictionary,
            IEnumerable<KeyValuePair<string, string>> items,
            Action<string> verifyThrowKey)
        {
            return dictionary.SetItems(ValidateItems(items, verifyThrowKey));
 
            // PERF: This extra allocation is still faster than enumerating ImmutableDictionary after modification.
            static IEnumerable<KeyValuePair<string, string>> ValidateItems(
                IEnumerable<KeyValuePair<string, string>> items,
                Action<string> verifyThrowKey)
            {
                foreach (KeyValuePair<string, string> item in items)
                {
                    verifyThrowKey(item.Key);
 
                    // Set null as empty string to match behavior with ProjectMetadataInstance.
                    yield return item.Value == null
                        ? new KeyValuePair<string, string>(item.Key, string.Empty)
                        : item;
                }
            }
        }
    }
}