Make GetOrdereredMetadata return something good
This commit is contained in:
parent
f45250ce5e
commit
434bf614d5
|
|
@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.Http
|
|||
public object this[int index] { get { throw null; } }
|
||||
public Microsoft.AspNetCore.Http.EndpointMetadataCollection.Enumerator GetEnumerator() { throw null; }
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public T GetMetadata<T>() where T : class { throw null; }
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public System.Collections.Generic.IEnumerable<T> GetOrderedMetadata<T>() where T : class { throw null; }
|
||||
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]public Microsoft.AspNetCore.Http.OrderedEndpointMetadataCollection<T> GetOrderedMetadata<T>() where T : class { throw null; }
|
||||
System.Collections.Generic.IEnumerator<object> System.Collections.Generic.IEnumerable<System.Object>.GetEnumerator() { throw null; }
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
|
||||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||
|
|
@ -327,6 +327,30 @@ namespace Microsoft.AspNetCore.Http
|
|||
Microsoft.AspNetCore.Http.IMiddleware Create(System.Type middlewareType);
|
||||
void Release(Microsoft.AspNetCore.Http.IMiddleware middleware);
|
||||
}
|
||||
public sealed partial class OrderedEndpointMetadataCollection<T> : System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.IEnumerable where T : class
|
||||
{
|
||||
internal OrderedEndpointMetadataCollection() { }
|
||||
public int Count { get { throw null; } }
|
||||
public T this[int index] { get { throw null; } }
|
||||
public Microsoft.AspNetCore.Http.OrderedEndpointMetadataCollection<T>.Enumerator GetEnumerator() { throw null; }
|
||||
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator() { throw null; }
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
|
||||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||
public partial struct Enumerator : System.Collections.Generic.IEnumerator<T>, System.Collections.IEnumerator, System.IDisposable
|
||||
{
|
||||
private T[] _items;
|
||||
[System.Diagnostics.DebuggerBrowsableAttribute(System.Diagnostics.DebuggerBrowsableState.Never)]
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute]
|
||||
private T _Current_k__BackingField;
|
||||
private object _dummy;
|
||||
private int _dummyPrimitive;
|
||||
public T Current { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||
object System.Collections.IEnumerator.Current { get { throw null; } }
|
||||
public void Dispose() { }
|
||||
public bool MoveNext() { throw null; }
|
||||
public void Reset() { }
|
||||
}
|
||||
}
|
||||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||
public readonly partial struct PathString : System.IEquatable<Microsoft.AspNetCore.Http.PathString>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Http
|
|||
public static readonly EndpointMetadataCollection Empty = new EndpointMetadataCollection(Array.Empty<object>());
|
||||
|
||||
private readonly object[] _items;
|
||||
private readonly ConcurrentDictionary<Type, object[]> _cache;
|
||||
private readonly ConcurrentDictionary<Type, object> _cache;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="EndpointMetadataCollection"/>.
|
||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Http
|
|||
}
|
||||
|
||||
_items = items.ToArray();
|
||||
_cache = new ConcurrentDictionary<Type, object[]>();
|
||||
_cache = new ConcurrentDictionary<Type, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -74,10 +74,11 @@ namespace Microsoft.AspNetCore.Http
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T GetMetadata<T>() where T : class
|
||||
{
|
||||
if (_cache.TryGetValue(typeof(T), out var result))
|
||||
if (_cache.TryGetValue(typeof(T), out var obj))
|
||||
{
|
||||
var length = result.Length;
|
||||
return length > 0 ? (T)result[length - 1] : default;
|
||||
var result = (OrderedEndpointMetadataCollection<T>)obj;
|
||||
var count = result.Count;
|
||||
return count > 0 ? result[count - 1] : default;
|
||||
}
|
||||
|
||||
return GetMetadataSlow<T>();
|
||||
|
|
@ -85,9 +86,9 @@ namespace Microsoft.AspNetCore.Http
|
|||
|
||||
private T GetMetadataSlow<T>() where T : class
|
||||
{
|
||||
var array = GetOrderedMetadataSlow<T>();
|
||||
var length = array.Length;
|
||||
return length > 0 ? array[length - 1] : default;
|
||||
var result = GetOrderedMetadataSlow<T>();
|
||||
var count = result.Count;
|
||||
return count > 0 ? result[count - 1] : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -97,30 +98,34 @@ namespace Microsoft.AspNetCore.Http
|
|||
/// <typeparam name="T">The type of metadata.</typeparam>
|
||||
/// <returns>A sequence of metadata items of <typeparamref name="T"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public IEnumerable<T> GetOrderedMetadata<T>() where T : class
|
||||
public OrderedEndpointMetadataCollection<T> GetOrderedMetadata<T>() where T : class
|
||||
{
|
||||
if (_cache.TryGetValue(typeof(T), out var result))
|
||||
{
|
||||
return (T[])result;
|
||||
return (OrderedEndpointMetadataCollection<T>)result;
|
||||
}
|
||||
|
||||
return GetOrderedMetadataSlow<T>();
|
||||
}
|
||||
|
||||
private T[] GetOrderedMetadataSlow<T>() where T : class
|
||||
private OrderedEndpointMetadataCollection<T> GetOrderedMetadataSlow<T>() where T : class
|
||||
{
|
||||
var items = new List<T>();
|
||||
for (var i = 0; i < _items.Length; i++)
|
||||
// Perf: avoid allocations totally for the common case where there are no matching metadata.
|
||||
List<T> matches = null;
|
||||
|
||||
var items = _items;
|
||||
for (var i = 0; i < items.Length; i++)
|
||||
{
|
||||
if (_items[i] is T item)
|
||||
if (items[i] is T item)
|
||||
{
|
||||
items.Add(item);
|
||||
matches ??= new List<T>();
|
||||
matches.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
var array = items.ToArray();
|
||||
_cache.TryAdd(typeof(T), array);
|
||||
return array;
|
||||
var results = matches == null ? OrderedEndpointMetadataCollection<T>.Empty : new OrderedEndpointMetadataCollection<T>(matches.ToArray());
|
||||
_cache.TryAdd(typeof(T), results);
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of ordered, strongly-typed metadata associated with an <see cref="Endpoint"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The metadata type.</typeparam>
|
||||
public sealed class OrderedEndpointMetadataCollection<T> : IReadOnlyList<T>
|
||||
where T : class
|
||||
{
|
||||
internal static readonly OrderedEndpointMetadataCollection<T> Empty = new OrderedEndpointMetadataCollection<T>(Array.Empty<T>());
|
||||
|
||||
private readonly T[] _items;
|
||||
|
||||
internal OrderedEndpointMetadataCollection(T[] items)
|
||||
{
|
||||
_items = items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item at <paramref name="index"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item to retrieve.</param>
|
||||
/// <returns>The item at <paramref name="index"/>.</returns>
|
||||
public T this[int index] => _items[index];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of metadata items.
|
||||
/// </summary>
|
||||
public int Count => _items.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IEnumerator"/> of all metadata items.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IEnumerator"/> of all metadata items.</returns>
|
||||
public Enumerator GetEnumerator() => new Enumerator(this);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IEnumerator{T}"/> of all metadata items.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IEnumerator{T}"/> of all metadata items.</returns>
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="IEnumerator"/> of all metadata items.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IEnumerator"/> of all metadata items.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the elements of an <see cref="OrderedEndpointMetadataCollection{T}"/>.
|
||||
/// </summary>
|
||||
public struct Enumerator : IEnumerator<T>
|
||||
{
|
||||
// Intentionally not readonly to prevent defensive struct copies
|
||||
private T[] _items;
|
||||
private int _index;
|
||||
|
||||
internal Enumerator(OrderedEndpointMetadataCollection<T> collection)
|
||||
{
|
||||
_items = collection._items;
|
||||
_index = 0;
|
||||
Current = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at the current position of the enumerator
|
||||
/// </summary>
|
||||
public T Current { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element at the current position of the enumerator
|
||||
/// </summary>
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
/// <summary>
|
||||
/// Releases all resources used by the <see cref="Enumerator"/>.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances the enumerator to the next element of the <see cref="Enumerator"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the enumerator was successfully advanced to the next element;
|
||||
/// <c>false</c> if the enumerator has passed the end of the collection.
|
||||
/// </returns>
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_index < _items.Length)
|
||||
{
|
||||
Current = _items[_index++];
|
||||
return true;
|
||||
}
|
||||
|
||||
Current = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the enumerator to its initial position, which is before the first element in the collection.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_index = 0;
|
||||
Current = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
|
@ -45,5 +45,31 @@ namespace Microsoft.AspNetCore.Routing
|
|||
value => Assert.Equal(2, value),
|
||||
value => Assert.Equal(3, value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrderedMetadata_CanReturnEmptyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = new EndpointMetadataCollection(1, 2, 3);
|
||||
|
||||
// Act
|
||||
var ordered = metadata.GetOrderedMetadata<string>();
|
||||
|
||||
Assert.Same(OrderedEndpointMetadataCollection<string>.Empty, ordered);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrderedMetadata_CanReturnNonEmptyCollection()
|
||||
{
|
||||
// Arrange
|
||||
var metadata = new EndpointMetadataCollection("1", "2");
|
||||
|
||||
// Act
|
||||
var ordered1 = metadata.GetOrderedMetadata<string>();
|
||||
var ordered2 = metadata.GetOrderedMetadata<string>();
|
||||
|
||||
Assert.Same(ordered1, ordered2);
|
||||
Assert.Equal(new string[] { "1", "2" }, ordered1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Abstractions.Tests
|
||||
{
|
||||
public class OrderedEndpointMetadataCollectionTest
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_Enumeration_ContainsValues()
|
||||
{
|
||||
// Arrange & Act
|
||||
var metadata = new OrderedEndpointMetadataCollection<string>(new string[]
|
||||
{
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, metadata.Count);
|
||||
|
||||
Assert.Collection(metadata,
|
||||
value => Assert.Equal("1", value),
|
||||
value => Assert.Equal("2", value),
|
||||
value => Assert.Equal("3", value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_Loop_ContainsValues()
|
||||
{
|
||||
// Arrange & Act
|
||||
var metadata = new OrderedEndpointMetadataCollection<string>(new string[]
|
||||
{
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, metadata.Count);
|
||||
for (var i = 0; i < metadata.Count; i++)
|
||||
{
|
||||
Assert.Equal((i + 1).ToString(), metadata[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue