File: Internal\ItemsDictionary.cs
Web Access
Project: src\src\Http\Http\src\Microsoft.AspNetCore.Http.csproj (Microsoft.AspNetCore.Http)
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Shared;
 
namespace Microsoft.AspNetCore.Http;
 
[DebuggerTypeProxy(typeof(DictionaryDebugView<object, object>))]
[DebuggerDisplay("Count = {Items.Count}")]
internal sealed class ItemsDictionary : IDictionary<object, object?>
{
    private IDictionary<object, object?>? _items;
 
    public ItemsDictionary()
    { }
 
    public ItemsDictionary(IDictionary<object, object?> items)
    {
        _items = items;
    }
 
    public IDictionary<object, object?> Items => this;
 
    // Replace the indexer with one that returns null for missing values
    object? IDictionary<object, object?>.this[object key]
    {
        get
        {
            if (_items != null && _items.TryGetValue(key, out var value))
            {
                return value;
            }
            return null;
        }
        set
        {
            EnsureDictionary();
            _items[key] = value;
        }
    }
 
    void IDictionary<object, object?>.Add(object key, object? value)
    {
        EnsureDictionary();
        _items.Add(key, value);
    }
 
    bool IDictionary<object, object?>.ContainsKey(object key)
        => _items != null && _items.ContainsKey(key);
 
    ICollection<object> IDictionary<object, object?>.Keys
    {
        get
        {
            if (_items == null)
            {
                return EmptyDictionary.Dictionary.Keys;
            }
 
            return _items.Keys;
        }
    }
 
    bool IDictionary<object, object?>.Remove(object key)
        => _items != null && _items.Remove(key);
 
    bool IDictionary<object, object?>.TryGetValue(object key, out object? value)
    {
        value = null;
        return _items != null && _items.TryGetValue(key, out value);
    }
 
    ICollection<object?> IDictionary<object, object?>.Values
    {
        get
        {
            if (_items == null)
            {
                return EmptyDictionary.Dictionary.Values;
            }
 
            return _items.Values;
        }
    }
 
    void ICollection<KeyValuePair<object, object?>>.Add(KeyValuePair<object, object?> item)
    {
        EnsureDictionary();
        _items.Add(item);
    }
 
    void ICollection<KeyValuePair<object, object?>>.Clear() => _items?.Clear();
 
    bool ICollection<KeyValuePair<object, object?>>.Contains(KeyValuePair<object, object?> item)
        => _items != null && _items.Contains(item);
 
    void ICollection<KeyValuePair<object, object?>>.CopyTo(KeyValuePair<object, object?>[] array, int arrayIndex)
    {
        if (_items == null)
        {
            //Delegate to Empty Dictionary to do the argument checking.
            EmptyDictionary.Collection.CopyTo(array, arrayIndex);
        }
 
        _items?.CopyTo(array, arrayIndex);
    }
 
    int ICollection<KeyValuePair<object, object?>>.Count => _items?.Count ?? 0;
 
    bool ICollection<KeyValuePair<object, object?>>.IsReadOnly => _items?.IsReadOnly ?? false;
 
    bool ICollection<KeyValuePair<object, object?>>.Remove(KeyValuePair<object, object?> item)
    {
        if (_items == null)
        {
            return false;
        }
 
        if (_items.TryGetValue(item.Key, out var value) && Equals(item.Value, value))
        {
            return _items.Remove(item.Key);
        }
        return false;
    }
 
    [MemberNotNull(nameof(_items))]
    private void EnsureDictionary()
    {
        if (_items == null)
        {
            _items = new Dictionary<object, object?>();
        }
    }
 
    IEnumerator<KeyValuePair<object, object?>> IEnumerable<KeyValuePair<object, object?>>.GetEnumerator()
        => _items?.GetEnumerator() ?? EmptyEnumerator.Instance;
 
    IEnumerator IEnumerable.GetEnumerator() => _items?.GetEnumerator() ?? EmptyEnumerator.Instance;
 
    private sealed class EmptyEnumerator : IEnumerator<KeyValuePair<object, object?>>
    {
        // In own class so only initialized if GetEnumerator is called on an empty ItemsDictionary
        public static readonly IEnumerator<KeyValuePair<object, object?>> Instance = new EmptyEnumerator();
        public KeyValuePair<object, object?> Current => default;
 
        object? IEnumerator.Current => null;
 
        public void Dispose()
        { }
 
        public bool MoveNext() => false;
 
        public void Reset()
        { }
    }
 
    private static class EmptyDictionary
    {
        // In own class so only initialized if CopyTo is called on an empty ItemsDictionary
        public static readonly IDictionary<object, object?> Dictionary = new Dictionary<object, object?>();
        public static ICollection<KeyValuePair<object, object?>> Collection => Dictionary;
    }
}