File: src\Shared\EmptyCollections\EmptyCollectionExtensions.cs
Web Access
Project: src\src\Libraries\Microsoft.Extensions.AI.Abstractions\Microsoft.Extensions.AI.Abstractions.csproj (Microsoft.Extensions.AI.Abstractions)
// 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;
 
#pragma warning disable CA1716
namespace Microsoft.Shared.Collections;
#pragma warning restore CA1716
 
/// <summary>
/// Defines static methods used to optimize the use of empty collections.
/// </summary>
#if !SHARED_PROJECT
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
#endif
 
internal static class EmptyCollectionExtensions
{
    /// <summary>
    /// Returns an optimized empty collection if the input is <see langword="null"/> or empty, otherwise returns the input.
    /// </summary>
    /// <typeparam name="T">The type of the collection.</typeparam>
    /// <param name="collection">The collection to check for <see langword="null"/> or empty.</param>
    /// <returns>Returns a static instance of an empty type if the input collection is <see langword="null"/> or empty, otherwise the collection.</returns>
    /// <remarks>
    /// Substituting a static collection whenever an empty collection is needed helps in two ways. First,
    /// it allows the original empty collection to be garbage collected, freeing memory. Second, the
    /// empty collection that is returned is optimized to not allocated memory whenever the collection is
    /// enumerated.
    /// </remarks>
    public static IReadOnlyCollection<T> EmptyIfNull<T>(this IReadOnlyCollection<T>? collection)
        => collection == null || collection.Count == 0 ? EmptyReadOnlyList<T>.Instance : collection;
 
    /// <summary>
    /// Returns an optimized empty collection if the input is <see langword="null"/> or empty, otherwise returns the input.
    /// </summary>
    /// <typeparam name="T">The type of the collection.</typeparam>
    /// <param name="collection">The collection to check for <see langword="null"/> or empty.</param>
    /// <returns>Returns a static instance of an empty type if the input collection is <see langword="null"/> or empty, otherwise the collection.</returns>
    /// <remarks>
    /// Substituting a static collection whenever an empty collection is needed helps in two ways. First,
    /// it allows the original empty collection to be garbage collected, freeing memory. Second, the
    /// empty collection that is returned is optimized to not allocated memory whenever the collection is
    /// enumerated.
    /// </remarks>
    public static IEnumerable<T> EmptyIfNull<T>(this ICollection<T>? collection)
        => collection == null || collection.Count == 0 ? EmptyReadOnlyList<T>.Instance : collection;
 
    /// <summary>
    /// Returns an optimized empty collection if the input is <see langword="null"/> or empty, otherwise returns the input.
    /// </summary>
    /// <typeparam name="T">The type of the collection.</typeparam>
    /// <param name="list">The collection to check for <see langword="null"/> or empty.</param>
    /// <returns>Returns a static instance of an empty type if the input collection is <see langword="null"/> or empty, otherwise the collection.</returns>
    /// <remarks>
    /// Substituting a static collection whenever an empty collection is needed helps in two ways. First,
    /// it allows the original empty collection to be garbage collected, freeing memory. Second, the
    /// empty collection that is returned is optimized to not allocated memory whenever the collection is
    /// enumerated.
    /// </remarks>
    public static IReadOnlyList<T> EmptyIfNull<T>(this IReadOnlyList<T>? list)
        => list == null || list.Count == 0 ? EmptyReadOnlyList<T>.Instance : list;
 
    /// <summary>
    /// Returns an optimized empty list if the input is <see langword="null"/> or empty, otherwise returns the input.
    /// </summary>
    /// <typeparam name="T">The type of the collection.</typeparam>
    /// <param name="list">The list to check for <see langword="null"/> or empty.</param>
    /// <returns>Returns a static instance of an empty type if the input collection is <see langword="null"/> or empty, otherwise the collection.</returns>
    /// <remarks>
    /// Substituting a static list whenever an empty collection is needed helps in two ways. First,
    /// it allows the original empty collection to be garbage collected, freeing memory. Second, the
    /// empty collection that is returned is optimized to not allocated memory whenever the collection is
    /// enumerated.
    /// </remarks>
    public static IEnumerable<T> EmptyIfNull<T>(this IList<T>? list)
        => list == null || list.Count == 0 ? EmptyReadOnlyList<T>.Instance : list;
 
    /// <summary>
    /// Returns an optimized empty array if the input is <see langword="null"/> or empty, otherwise returns the input.
    /// </summary>
    /// <typeparam name="T">The type of the array.</typeparam>
    /// <param name="array">The array to check for <see langword="null"/> or empty.</param>
    /// <returns>Returns a static instance of an empty array if the input array is <see langword="null"/> or empty, otherwise the array.</returns>
    public static T[] EmptyIfNull<T>(this T[]? array)
        => array == null || array.Length == 0 ? Array.Empty<T>() : array;
 
    /// <summary>
    /// Returns an optimized empty collection if the input is <see langword="null"/> or can be determined to be empty, otherwise returns the input.
    /// </summary>
    /// <typeparam name="T">The type of the collection.</typeparam>
    /// <param name="enumerable">The collection to check for <see langword="null"/> or empty.</param>
    /// <returns>Returns a static instance of an empty type if the input collection is <see langword="null"/> or empty, otherwise the collection.</returns>
    /// <remarks>
    /// This method does not enumerate the collection.
    /// </remarks>
    public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T>? enumerable)
    {
        if (enumerable == null)
        {
            return EmptyReadOnlyList<T>.Instance;
        }
 
        // note this takes care of the IReadOnlyList<T> case too
        if (enumerable is IReadOnlyCollection<T> rc && rc.Count == 0)
        {
            return EmptyReadOnlyList<T>.Instance;
        }
 
        // note this takes care of the IList<T> case too
        if (enumerable is ICollection<T> c && c.Count == 0)
        {
            return EmptyReadOnlyList<T>.Instance;
        }
 
        return enumerable;
    }
 
    /// <summary>
    /// Returns an optimized empty dictionary if the input is <see langword="null"/> or can be determined to be empty, otherwise returns the input.
    /// </summary>
    /// <typeparam name="TKey">The key type of the dictionary.</typeparam>
    /// <typeparam name="TValue">The value type of the dictionary.</typeparam>
    /// <param name="dictionary">The dictionary to check for <see langword="null"/> or empty.</param>
    /// <returns>Returns a static instance of an empty type if the input dictionary is <see langword="null"/> or empty, otherwise the dictionary.</returns>
    /// <remarks>
    /// Note that this method does not enumerate the dictionary.
    /// </remarks>
    public static IReadOnlyDictionary<TKey, TValue> EmptyIfNull<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue>? dictionary)
        where TKey : notnull
    {
        if (dictionary == null || dictionary.Count == 0)
        {
            return EmptyReadOnlyDictionary<TKey, TValue>.Instance;
        }
 
        return dictionary;
    }
}