| File: RequirementsOfType.cs | Web Access |
| Project: Microsoft.AspNetCore.Authorization.csproj (Microsoft.AspNetCore.Authorization) |
// 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.ObjectModel; using System.Linq; namespace Microsoft.AspNetCore.Authorization; /// <summary> /// Equivalent to <c>requirements.OfType<T>()</c>, but avoids allocating on the common path. /// </summary> /// <remarks> /// <para> /// Fast path: <see cref="AuthorizationPolicy.Requirements"/> is created with <see cref="List{T}.AsReadOnly"/>, so the /// requirements passed to <see cref="AuthorizationHandlerContext"/> are usually a <see cref="ReadOnlyCollection{T}"/>. /// In that case the enumerator indexes into the collection directly and creates no enumerator. /// </para> /// <para> /// What's saved: <c>OfType<T>()</c> allocates an iterator plus a boxed enumerator over the source on every /// <see cref="IAuthorizationHandler.HandleAsync(AuthorizationHandlerContext)"/> call. This struct enumerable avoids /// both, and is safe to use in <c>foreach</c> inside async handlers because it is not a ref struct. /// </para> /// <para> /// Fallback: any other <see cref="IEnumerable{T}"/> goes through <c>OfType<T>()</c> unchanged. /// </para> /// <para> /// <typeparamref name="T"/> is intentionally unconstrained because <see cref="Infrastructure.PassThroughAuthorizationHandler"/> /// uses it with <see cref="IAuthorizationHandler"/>, which does not derive from <see cref="IAuthorizationRequirement"/>. /// </para> /// </remarks> internal readonly struct RequirementsOfType<T> { private readonly IEnumerable<IAuthorizationRequirement> _requirements; public RequirementsOfType(IEnumerable<IAuthorizationRequirement> requirements) { _requirements = requirements; } public Enumerator GetEnumerator() => new(_requirements); public struct Enumerator : IDisposable { private readonly ReadOnlyCollection<IAuthorizationRequirement>? _list; private readonly IEnumerator<T>? _fallback; private int _index; private T _current; internal Enumerator(IEnumerable<IAuthorizationRequirement> requirements) { _list = requirements as ReadOnlyCollection<IAuthorizationRequirement>; _fallback = _list is null ? requirements.OfType<T>().GetEnumerator() : null; _index = -1; _current = default!; } public readonly T Current => _list is null ? _fallback!.Current : _current; public bool MoveNext() { if (_list is null) { return _fallback!.MoveNext(); } while (++_index < _list.Count) { if (_list[_index] is T requirement) { _current = requirement; return true; } } return false; } public readonly void Dispose() => _fallback?.Dispose(); } }