Use T? for unconstrained nullable types (#25772)
This commit is contained in:
parent
36f8642f0b
commit
18d261b20c
|
|
@ -144,8 +144,7 @@ namespace Microsoft.AspNetCore.Authentication
|
||||||
/// <typeparam name="T">Parameter type.</typeparam>
|
/// <typeparam name="T">Parameter type.</typeparam>
|
||||||
/// <param name="key">Parameter key.</param>
|
/// <param name="key">Parameter key.</param>
|
||||||
/// <returns>Retrieved value or the default value if the property is not set.</returns>
|
/// <returns>Retrieved value or the default value if the property is not set.</returns>
|
||||||
[return: MaybeNull]
|
public T? GetParameter<T>(string key)
|
||||||
public T GetParameter<T>(string key)
|
|
||||||
=> Parameters.TryGetValue(key, out var obj) && obj is T value ? value : default;
|
=> Parameters.TryGetValue(key, out var obj) && obj is T value ? value : default;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -154,7 +153,7 @@ namespace Microsoft.AspNetCore.Authentication
|
||||||
/// <typeparam name="T">Parameter type.</typeparam>
|
/// <typeparam name="T">Parameter type.</typeparam>
|
||||||
/// <param name="key">Parameter key.</param>
|
/// <param name="key">Parameter key.</param>
|
||||||
/// <param name="value">Value to set.</param>
|
/// <param name="value">Value to set.</param>
|
||||||
public void SetParameter<T>(string key, [MaybeNull] T value)
|
public void SetParameter<T>(string key, T value)
|
||||||
=> Parameters[key] = value;
|
=> Parameters[key] = value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.Net.Http.Headers
|
namespace Microsoft.Net.Http.Headers
|
||||||
|
|
@ -13,9 +12,9 @@ namespace Microsoft.Net.Http.Headers
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract int GetParsedValueLength(StringSegment value, int startIndex, [MaybeNull] out T parsedValue);
|
protected abstract int GetParsedValueLength(StringSegment value, int startIndex, out T? parsedValue);
|
||||||
|
|
||||||
public sealed override bool TryParseValue(StringSegment value, ref int index, [MaybeNull] out T parsedValue)
|
public sealed override bool TryParseValue(StringSegment value, ref int index, out T? parsedValue)
|
||||||
{
|
{
|
||||||
parsedValue = default;
|
parsedValue = default;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
// Cache-Control headers, only one instance of CacheControlHeaderValue is created (if all headers contain valid
|
// Cache-Control headers, only one instance of CacheControlHeaderValue is created (if all headers contain valid
|
||||||
// values, otherwise we may have multiple strings containing the invalid values).
|
// values, otherwise we may have multiple strings containing the invalid values).
|
||||||
private static readonly HttpHeaderParser<CacheControlHeaderValue> Parser
|
private static readonly HttpHeaderParser<CacheControlHeaderValue> Parser
|
||||||
= new GenericHeaderParser<CacheControlHeaderValue>(true, GetCacheControlLength!);
|
= new GenericHeaderParser<CacheControlHeaderValue>(true, GetCacheControlLength);
|
||||||
|
|
||||||
private static readonly Action<StringSegment> CheckIsValidTokenAction = CheckIsValidToken;
|
private static readonly Action<StringSegment> CheckIsValidTokenAction = CheckIsValidToken;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
private static readonly char[] SingleQuote = new char[] { '\'' };
|
private static readonly char[] SingleQuote = new char[] { '\'' };
|
||||||
|
|
||||||
private static readonly HttpHeaderParser<ContentDispositionHeaderValue> Parser
|
private static readonly HttpHeaderParser<ContentDispositionHeaderValue> Parser
|
||||||
= new GenericHeaderParser<ContentDispositionHeaderValue>(false, GetDispositionTypeLength!);
|
= new GenericHeaderParser<ContentDispositionHeaderValue>(false, GetDispositionTypeLength);
|
||||||
|
|
||||||
// Use list instead of dictionary since we may have multiple parameters with the same name.
|
// Use list instead of dictionary since we may have multiple parameters with the same name.
|
||||||
private ObjectCollection<NameValueHeaderValue>? _parameters;
|
private ObjectCollection<NameValueHeaderValue>? _parameters;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
public class ContentRangeHeaderValue
|
public class ContentRangeHeaderValue
|
||||||
{
|
{
|
||||||
private static readonly HttpHeaderParser<ContentRangeHeaderValue> Parser
|
private static readonly HttpHeaderParser<ContentRangeHeaderValue> Parser
|
||||||
= new GenericHeaderParser<ContentRangeHeaderValue>(false, GetContentRangeLength!);
|
= new GenericHeaderParser<ContentRangeHeaderValue>(false, GetContentRangeLength);
|
||||||
|
|
||||||
private StringSegment _unit;
|
private StringSegment _unit;
|
||||||
private long? _from;
|
private long? _from;
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,13 @@ namespace Microsoft.Net.Http.Headers
|
||||||
// Note that the ETag header does not allow a * but we're not that strict: We allow both '*' and ETag values in a single value.
|
// Note that the ETag header does not allow a * but we're not that strict: We allow both '*' and ETag values in a single value.
|
||||||
// We can't guarantee that a single parsed value will be used directly in an ETag header.
|
// We can't guarantee that a single parsed value will be used directly in an ETag header.
|
||||||
private static readonly HttpHeaderParser<EntityTagHeaderValue> SingleValueParser
|
private static readonly HttpHeaderParser<EntityTagHeaderValue> SingleValueParser
|
||||||
= new GenericHeaderParser<EntityTagHeaderValue>(false, GetEntityTagLength!);
|
= new GenericHeaderParser<EntityTagHeaderValue>(false, GetEntityTagLength);
|
||||||
// Note that if multiple ETag values are allowed (e.g. 'If-Match', 'If-None-Match'), according to the RFC
|
// Note that if multiple ETag values are allowed (e.g. 'If-Match', 'If-None-Match'), according to the RFC
|
||||||
// the value must either be '*' or a list of ETag values. It's not allowed to have both '*' and a list of
|
// the value must either be '*' or a list of ETag values. It's not allowed to have both '*' and a list of
|
||||||
// ETag values. We're not that strict: We allow both '*' and ETag values in a list. If the server sends such
|
// ETag values. We're not that strict: We allow both '*' and ETag values in a list. If the server sends such
|
||||||
// an invalid list, we want to be able to represent it using the corresponding header property.
|
// an invalid list, we want to be able to represent it using the corresponding header property.
|
||||||
private static readonly HttpHeaderParser<EntityTagHeaderValue> MultipleValueParser
|
private static readonly HttpHeaderParser<EntityTagHeaderValue> MultipleValueParser
|
||||||
= new GenericHeaderParser<EntityTagHeaderValue>(true, GetEntityTagLength!);
|
= new GenericHeaderParser<EntityTagHeaderValue>(true, GetEntityTagLength);
|
||||||
|
|
||||||
private static EntityTagHeaderValue? AnyType;
|
private static EntityTagHeaderValue? AnyType;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,13 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.Net.Http.Headers
|
namespace Microsoft.Net.Http.Headers
|
||||||
{
|
{
|
||||||
internal sealed class GenericHeaderParser<T> : BaseHeaderParser<T>
|
internal sealed class GenericHeaderParser<T> : BaseHeaderParser<T>
|
||||||
{
|
{
|
||||||
internal delegate int GetParsedValueLengthDelegate(StringSegment value, int startIndex, [MaybeNull] out T parsedValue);
|
internal delegate int GetParsedValueLengthDelegate(StringSegment value, int startIndex, out T? parsedValue);
|
||||||
|
|
||||||
private GetParsedValueLengthDelegate _getParsedValueLength;
|
private GetParsedValueLengthDelegate _getParsedValueLength;
|
||||||
|
|
||||||
|
|
@ -24,7 +23,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
_getParsedValueLength = getParsedValueLength;
|
_getParsedValueLength = getParsedValueLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override int GetParsedValueLength(StringSegment value, int startIndex, [MaybeNull] out T parsedValue)
|
protected override int GetParsedValueLength(StringSegment value, int startIndex, out T? parsedValue)
|
||||||
{
|
{
|
||||||
return _getParsedValueLength(value, startIndex, out parsedValue);
|
return _getParsedValueLength(value, startIndex, out parsedValue);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,9 @@ namespace Microsoft.Net.Http.Headers
|
||||||
// pointing to the next non-whitespace character after a delimiter. E.g. if called with a start index of 0
|
// pointing to the next non-whitespace character after a delimiter. E.g. if called with a start index of 0
|
||||||
// for string "value , second_value", then after the call completes, 'index' must point to 's', i.e. the first
|
// for string "value , second_value", then after the call completes, 'index' must point to 's', i.e. the first
|
||||||
// non-whitespace after the separator ','.
|
// non-whitespace after the separator ','.
|
||||||
public abstract bool TryParseValue(StringSegment value, ref int index, [MaybeNull] out T parsedValue);
|
public abstract bool TryParseValue(StringSegment value, ref int index, out T? parsedValue);
|
||||||
|
|
||||||
[return: MaybeNull]
|
public T? ParseValue(StringSegment value, ref int index)
|
||||||
public T ParseValue(StringSegment value, ref int index)
|
|
||||||
{
|
{
|
||||||
// Index may be value.Length (e.g. both 0). This may be allowed for some headers (e.g. Accept but not
|
// Index may be value.Length (e.g. both 0). This may be allowed for some headers (e.g. Accept but not
|
||||||
// allowed by others (e.g. Content-Length). The parser has to decide if this is valid or not.
|
// allowed by others (e.g. Content-Length). The parser has to decide if this is valid or not.
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@ namespace Microsoft.Net.Http.Headers
|
||||||
private static readonly char[] PeriodCharacterArray = new char[] { PeriodCharacter };
|
private static readonly char[] PeriodCharacterArray = new char[] { PeriodCharacter };
|
||||||
|
|
||||||
private static readonly HttpHeaderParser<MediaTypeHeaderValue> SingleValueParser
|
private static readonly HttpHeaderParser<MediaTypeHeaderValue> SingleValueParser
|
||||||
= new GenericHeaderParser<MediaTypeHeaderValue>(false, GetMediaTypeLength!);
|
= new GenericHeaderParser<MediaTypeHeaderValue>(false, GetMediaTypeLength);
|
||||||
private static readonly HttpHeaderParser<MediaTypeHeaderValue> MultipleValueParser
|
private static readonly HttpHeaderParser<MediaTypeHeaderValue> MultipleValueParser
|
||||||
= new GenericHeaderParser<MediaTypeHeaderValue>(true, GetMediaTypeLength!);
|
= new GenericHeaderParser<MediaTypeHeaderValue>(true, GetMediaTypeLength);
|
||||||
|
|
||||||
// Use a collection instead of a dictionary since we may have multiple parameters with the same name.
|
// Use a collection instead of a dictionary since we may have multiple parameters with the same name.
|
||||||
private ObjectCollection<NameValueHeaderValue>? _parameters;
|
private ObjectCollection<NameValueHeaderValue>? _parameters;
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ namespace Microsoft.Net.Http.Headers
|
||||||
public class NameValueHeaderValue
|
public class NameValueHeaderValue
|
||||||
{
|
{
|
||||||
private static readonly HttpHeaderParser<NameValueHeaderValue> SingleValueParser
|
private static readonly HttpHeaderParser<NameValueHeaderValue> SingleValueParser
|
||||||
= new GenericHeaderParser<NameValueHeaderValue>(false, GetNameValueLength!);
|
= new GenericHeaderParser<NameValueHeaderValue>(false, GetNameValueLength);
|
||||||
internal static readonly HttpHeaderParser<NameValueHeaderValue> MultipleValueParser
|
internal static readonly HttpHeaderParser<NameValueHeaderValue> MultipleValueParser
|
||||||
= new GenericHeaderParser<NameValueHeaderValue>(true, GetNameValueLength!);
|
= new GenericHeaderParser<NameValueHeaderValue>(true, GetNameValueLength);
|
||||||
|
|
||||||
private StringSegment _name;
|
private StringSegment _name;
|
||||||
private StringSegment _value;
|
private StringSegment _value;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
public class RangeConditionHeaderValue
|
public class RangeConditionHeaderValue
|
||||||
{
|
{
|
||||||
private static readonly HttpHeaderParser<RangeConditionHeaderValue> Parser
|
private static readonly HttpHeaderParser<RangeConditionHeaderValue> Parser
|
||||||
= new GenericHeaderParser<RangeConditionHeaderValue>(false, GetRangeConditionLength!);
|
= new GenericHeaderParser<RangeConditionHeaderValue>(false, GetRangeConditionLength);
|
||||||
|
|
||||||
private DateTimeOffset? _lastModified;
|
private DateTimeOffset? _lastModified;
|
||||||
private EntityTagHeaderValue? _entityTag;
|
private EntityTagHeaderValue? _entityTag;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
public class RangeHeaderValue
|
public class RangeHeaderValue
|
||||||
{
|
{
|
||||||
private static readonly HttpHeaderParser<RangeHeaderValue> Parser
|
private static readonly HttpHeaderParser<RangeHeaderValue> Parser
|
||||||
= new GenericHeaderParser<RangeHeaderValue>(false, GetRangeLength!);
|
= new GenericHeaderParser<RangeHeaderValue>(false, GetRangeLength);
|
||||||
|
|
||||||
private StringSegment _unit;
|
private StringSegment _unit;
|
||||||
private ICollection<RangeItemHeaderValue>? _ranges;
|
private ICollection<RangeItemHeaderValue>? _ranges;
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@ namespace Microsoft.Net.Http.Headers
|
||||||
private const string ExpiresDateFormat = "r";
|
private const string ExpiresDateFormat = "r";
|
||||||
|
|
||||||
private static readonly HttpHeaderParser<SetCookieHeaderValue> SingleValueParser
|
private static readonly HttpHeaderParser<SetCookieHeaderValue> SingleValueParser
|
||||||
= new GenericHeaderParser<SetCookieHeaderValue>(false, GetSetCookieLength!);
|
= new GenericHeaderParser<SetCookieHeaderValue>(false, GetSetCookieLength);
|
||||||
private static readonly HttpHeaderParser<SetCookieHeaderValue> MultipleValueParser
|
private static readonly HttpHeaderParser<SetCookieHeaderValue> MultipleValueParser
|
||||||
= new GenericHeaderParser<SetCookieHeaderValue>(true, GetSetCookieLength!);
|
= new GenericHeaderParser<SetCookieHeaderValue>(true, GetSetCookieLength);
|
||||||
|
|
||||||
private StringSegment _name;
|
private StringSegment _name;
|
||||||
private StringSegment _value;
|
private StringSegment _value;
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@ namespace Microsoft.Net.Http.Headers
|
||||||
public class StringWithQualityHeaderValue
|
public class StringWithQualityHeaderValue
|
||||||
{
|
{
|
||||||
private static readonly HttpHeaderParser<StringWithQualityHeaderValue> SingleValueParser
|
private static readonly HttpHeaderParser<StringWithQualityHeaderValue> SingleValueParser
|
||||||
= new GenericHeaderParser<StringWithQualityHeaderValue>(false, GetStringWithQualityLength!);
|
= new GenericHeaderParser<StringWithQualityHeaderValue>(false, GetStringWithQualityLength);
|
||||||
private static readonly HttpHeaderParser<StringWithQualityHeaderValue> MultipleValueParser
|
private static readonly HttpHeaderParser<StringWithQualityHeaderValue> MultipleValueParser
|
||||||
= new GenericHeaderParser<StringWithQualityHeaderValue>(true, GetStringWithQualityLength!);
|
= new GenericHeaderParser<StringWithQualityHeaderValue>(true, GetStringWithQualityLength);
|
||||||
|
|
||||||
private StringSegment _value;
|
private StringSegment _value;
|
||||||
private double? _quality;
|
private double? _quality;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
[SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
|
[SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
|
||||||
public static Task WriteAsJsonAsync<TValue>(
|
public static Task WriteAsJsonAsync<TValue>(
|
||||||
this HttpResponse response,
|
this HttpResponse response,
|
||||||
[AllowNull] TValue value,
|
TValue value,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return response.WriteAsJsonAsync<TValue>(value, options: null, contentType: null, cancellationToken);
|
return response.WriteAsJsonAsync<TValue>(value, options: null, contentType: null, cancellationToken);
|
||||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
[SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
|
[SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
|
||||||
public static Task WriteAsJsonAsync<TValue>(
|
public static Task WriteAsJsonAsync<TValue>(
|
||||||
this HttpResponse response,
|
this HttpResponse response,
|
||||||
[AllowNull] TValue value,
|
TValue value,
|
||||||
JsonSerializerOptions? options,
|
JsonSerializerOptions? options,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
|
@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
[SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
|
[SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
|
||||||
public static Task WriteAsJsonAsync<TValue>(
|
public static Task WriteAsJsonAsync<TValue>(
|
||||||
this HttpResponse response,
|
this HttpResponse response,
|
||||||
[AllowNull] TValue value,
|
TValue value,
|
||||||
JsonSerializerOptions? options,
|
JsonSerializerOptions? options,
|
||||||
string? contentType,
|
string? contentType,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
options ??= ResolveSerializerOptions(response.HttpContext);
|
options ??= ResolveSerializerOptions(response.HttpContext);
|
||||||
|
|
||||||
response.ContentType = contentType ?? JsonConstants.JsonContentTypeWithCharset;
|
response.ContentType = contentType ?? JsonConstants.JsonContentTypeWithCharset;
|
||||||
return JsonSerializer.SerializeAsync<TValue>(response.Body, value!, options, cancellationToken);
|
return JsonSerializer.SerializeAsync<TValue>(response.Body, value, options, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Http.Extensions.Tests
|
||||||
context.Response.Body = body;
|
context.Response.Body = body;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await context.Response.WriteAsJsonAsync<Uri>(value: null);
|
await context.Response.WriteAsJsonAsync<Uri?>(value: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(JsonConstants.JsonContentTypeWithCharset, context.Response.ContentType);
|
Assert.Equal(JsonConstants.JsonContentTypeWithCharset, context.Response.ContentType);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Http.Features
|
namespace Microsoft.AspNetCore.Http.Features
|
||||||
|
|
@ -94,10 +93,9 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[return: MaybeNull]
|
public TFeature? Get<TFeature>()
|
||||||
public TFeature Get<TFeature>()
|
|
||||||
{
|
{
|
||||||
return (TFeature)this[typeof(TFeature)];
|
return (TFeature?)this[typeof(TFeature)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Set<TFeature>(TFeature instance)
|
public void Set<TFeature>(TFeature instance)
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,28 @@
|
||||||
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Http.Features
|
namespace Microsoft.AspNetCore.Http.Features
|
||||||
{
|
{
|
||||||
public struct FeatureReference<T>
|
public struct FeatureReference<T>
|
||||||
{
|
{
|
||||||
private T _feature;
|
private T? _feature;
|
||||||
private int _revision;
|
private int _revision;
|
||||||
|
|
||||||
private FeatureReference(T feature, int revision)
|
private FeatureReference(T? feature, int revision)
|
||||||
{
|
{
|
||||||
_feature = feature;
|
_feature = feature;
|
||||||
_revision = revision;
|
_revision = revision;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly FeatureReference<T> Default = new FeatureReference<T>(default(T)!, -1);
|
public static readonly FeatureReference<T> Default = new FeatureReference<T>(default(T), -1);
|
||||||
|
|
||||||
[return: MaybeNull]
|
public T? Fetch(IFeatureCollection features)
|
||||||
public T Fetch(IFeatureCollection features)
|
|
||||||
{
|
{
|
||||||
if (_revision == features.Revision)
|
if (_revision == features.Revision)
|
||||||
{
|
{
|
||||||
return _feature;
|
return _feature;
|
||||||
}
|
}
|
||||||
_feature = (T)features[typeof(T)]!;
|
_feature = (T)features[typeof(T)];
|
||||||
_revision = features.Revision;
|
_revision = features.Revision;
|
||||||
return _feature;
|
return _feature;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Http.Features
|
namespace Microsoft.AspNetCore.Http.Features
|
||||||
|
|
@ -37,8 +36,7 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
// be able to pass ref values that "dot through" the TCache struct memory,
|
// be able to pass ref values that "dot through" the TCache struct memory,
|
||||||
// if it was a Property then that getter would return a copy of the memory
|
// if it was a Property then that getter would return a copy of the memory
|
||||||
// preventing the use of "ref"
|
// preventing the use of "ref"
|
||||||
[AllowNull, MaybeNull]
|
public TCache? Cache;
|
||||||
public TCache Cache;
|
|
||||||
|
|
||||||
// Careful with modifications to the Fetch method; it is carefully constructed for inlining
|
// Careful with modifications to the Fetch method; it is carefully constructed for inlining
|
||||||
// See: https://github.com/aspnet/HttpAbstractions/pull/704
|
// See: https://github.com/aspnet/HttpAbstractions/pull/704
|
||||||
|
|
@ -61,10 +59,10 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
//
|
//
|
||||||
// Generally Fetch is called at a ratio > x4 of UpdateCached so this is a large gain
|
// Generally Fetch is called at a ratio > x4 of UpdateCached so this is a large gain
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public TFeature Fetch<TFeature, TState>(
|
public TFeature? Fetch<TFeature, TState>(
|
||||||
[AllowNull, MaybeNull] ref TFeature cached,
|
ref TFeature? cached,
|
||||||
TState state,
|
TState state,
|
||||||
Func<TState, TFeature> factory) where TFeature : class?
|
Func<TState, TFeature?> factory) where TFeature : class?
|
||||||
{
|
{
|
||||||
var flush = false;
|
var flush = false;
|
||||||
var revision = Collection?.Revision ?? ContextDisposed();
|
var revision = Collection?.Revision ?? ContextDisposed();
|
||||||
|
|
@ -80,7 +78,7 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update and cache clearing logic, when the fast-path in Fetch isn't applicable
|
// Update and cache clearing logic, when the fast-path in Fetch isn't applicable
|
||||||
private TFeature UpdateCached<TFeature, TState>(ref TFeature cached, TState state, Func<TState, TFeature> factory, int revision, bool flush) where TFeature : class?
|
private TFeature? UpdateCached<TFeature, TState>(ref TFeature? cached, TState state, Func<TState, TFeature?> factory, int revision, bool flush) where TFeature : class?
|
||||||
{
|
{
|
||||||
if (flush)
|
if (flush)
|
||||||
{
|
{
|
||||||
|
|
@ -108,8 +106,8 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TFeature Fetch<TFeature>([AllowNull, MaybeNull] ref TFeature cached, Func<IFeatureCollection, TFeature> factory)
|
public TFeature? Fetch<TFeature>(ref TFeature? cached, Func<IFeatureCollection, TFeature?> factory)
|
||||||
where TFeature : class? => Fetch(ref cached!, Collection, factory);
|
where TFeature : class? => Fetch(ref cached, Collection, factory);
|
||||||
|
|
||||||
private static int ContextDisposed()
|
private static int ContextDisposed()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
@ -58,8 +57,7 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
|
|
||||||
public IDictionary<string, object?> Properties { get; }
|
public IDictionary<string, object?> Properties { get; }
|
||||||
|
|
||||||
[return: MaybeNull]
|
private T? GetProperty<T>(string key)
|
||||||
private T GetProperty<T>(string key)
|
|
||||||
{
|
{
|
||||||
return Properties.TryGetValue(key, out var value) ? (T)value : default(T);
|
return Properties.TryGetValue(key, out var value) ? (T)value : default(T);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue