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>
|
||||
/// <param name="key">Parameter key.</param>
|
||||
/// <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;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -154,7 +153,7 @@ namespace Microsoft.AspNetCore.Authentication
|
|||
/// <typeparam name="T">Parameter type.</typeparam>
|
||||
/// <param name="key">Parameter key.</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;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// 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.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
// 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).
|
||||
private static readonly HttpHeaderParser<CacheControlHeaderValue> Parser
|
||||
= new GenericHeaderParser<CacheControlHeaderValue>(true, GetCacheControlLength!);
|
||||
= new GenericHeaderParser<CacheControlHeaderValue>(true, GetCacheControlLength);
|
||||
|
||||
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 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.
|
||||
private ObjectCollection<NameValueHeaderValue>? _parameters;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
public class ContentRangeHeaderValue
|
||||
{
|
||||
private static readonly HttpHeaderParser<ContentRangeHeaderValue> Parser
|
||||
= new GenericHeaderParser<ContentRangeHeaderValue>(false, GetContentRangeLength!);
|
||||
= new GenericHeaderParser<ContentRangeHeaderValue>(false, GetContentRangeLength);
|
||||
|
||||
private StringSegment _unit;
|
||||
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.
|
||||
// We can't guarantee that a single parsed value will be used directly in an ETag header.
|
||||
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
|
||||
// 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
|
||||
// an invalid list, we want to be able to represent it using the corresponding header property.
|
||||
private static readonly HttpHeaderParser<EntityTagHeaderValue> MultipleValueParser
|
||||
= new GenericHeaderParser<EntityTagHeaderValue>(true, GetEntityTagLength!);
|
||||
= new GenericHeaderParser<EntityTagHeaderValue>(true, GetEntityTagLength);
|
||||
|
||||
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.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.Net.Http.Headers
|
||||
{
|
||||
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;
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
_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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// for string "value , second_value", then after the call completes, 'index' must point to 's', i.e. the first
|
||||
// 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
|
||||
// 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 HttpHeaderParser<MediaTypeHeaderValue> SingleValueParser
|
||||
= new GenericHeaderParser<MediaTypeHeaderValue>(false, GetMediaTypeLength!);
|
||||
= new GenericHeaderParser<MediaTypeHeaderValue>(false, GetMediaTypeLength);
|
||||
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.
|
||||
private ObjectCollection<NameValueHeaderValue>? _parameters;
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
public class NameValueHeaderValue
|
||||
{
|
||||
private static readonly HttpHeaderParser<NameValueHeaderValue> SingleValueParser
|
||||
= new GenericHeaderParser<NameValueHeaderValue>(false, GetNameValueLength!);
|
||||
= new GenericHeaderParser<NameValueHeaderValue>(false, GetNameValueLength);
|
||||
internal static readonly HttpHeaderParser<NameValueHeaderValue> MultipleValueParser
|
||||
= new GenericHeaderParser<NameValueHeaderValue>(true, GetNameValueLength!);
|
||||
= new GenericHeaderParser<NameValueHeaderValue>(true, GetNameValueLength);
|
||||
|
||||
private StringSegment _name;
|
||||
private StringSegment _value;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
public class RangeConditionHeaderValue
|
||||
{
|
||||
private static readonly HttpHeaderParser<RangeConditionHeaderValue> Parser
|
||||
= new GenericHeaderParser<RangeConditionHeaderValue>(false, GetRangeConditionLength!);
|
||||
= new GenericHeaderParser<RangeConditionHeaderValue>(false, GetRangeConditionLength);
|
||||
|
||||
private DateTimeOffset? _lastModified;
|
||||
private EntityTagHeaderValue? _entityTag;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.Net.Http.Headers
|
|||
public class RangeHeaderValue
|
||||
{
|
||||
private static readonly HttpHeaderParser<RangeHeaderValue> Parser
|
||||
= new GenericHeaderParser<RangeHeaderValue>(false, GetRangeLength!);
|
||||
= new GenericHeaderParser<RangeHeaderValue>(false, GetRangeLength);
|
||||
|
||||
private StringSegment _unit;
|
||||
private ICollection<RangeItemHeaderValue>? _ranges;
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
private const string ExpiresDateFormat = "r";
|
||||
|
||||
private static readonly HttpHeaderParser<SetCookieHeaderValue> SingleValueParser
|
||||
= new GenericHeaderParser<SetCookieHeaderValue>(false, GetSetCookieLength!);
|
||||
= new GenericHeaderParser<SetCookieHeaderValue>(false, GetSetCookieLength);
|
||||
private static readonly HttpHeaderParser<SetCookieHeaderValue> MultipleValueParser
|
||||
= new GenericHeaderParser<SetCookieHeaderValue>(true, GetSetCookieLength!);
|
||||
= new GenericHeaderParser<SetCookieHeaderValue>(true, GetSetCookieLength);
|
||||
|
||||
private StringSegment _name;
|
||||
private StringSegment _value;
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ namespace Microsoft.Net.Http.Headers
|
|||
public class StringWithQualityHeaderValue
|
||||
{
|
||||
private static readonly HttpHeaderParser<StringWithQualityHeaderValue> SingleValueParser
|
||||
= new GenericHeaderParser<StringWithQualityHeaderValue>(false, GetStringWithQualityLength!);
|
||||
= new GenericHeaderParser<StringWithQualityHeaderValue>(false, GetStringWithQualityLength);
|
||||
private static readonly HttpHeaderParser<StringWithQualityHeaderValue> MultipleValueParser
|
||||
= new GenericHeaderParser<StringWithQualityHeaderValue>(true, GetStringWithQualityLength!);
|
||||
= new GenericHeaderParser<StringWithQualityHeaderValue>(true, GetStringWithQualityLength);
|
||||
|
||||
private StringSegment _value;
|
||||
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")]
|
||||
public static Task WriteAsJsonAsync<TValue>(
|
||||
this HttpResponse response,
|
||||
[AllowNull] TValue value,
|
||||
TValue value,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
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")]
|
||||
public static Task WriteAsJsonAsync<TValue>(
|
||||
this HttpResponse response,
|
||||
[AllowNull] TValue value,
|
||||
TValue value,
|
||||
JsonSerializerOptions? options,
|
||||
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")]
|
||||
public static Task WriteAsJsonAsync<TValue>(
|
||||
this HttpResponse response,
|
||||
[AllowNull] TValue value,
|
||||
TValue value,
|
||||
JsonSerializerOptions? options,
|
||||
string? contentType,
|
||||
CancellationToken cancellationToken = default)
|
||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Http
|
|||
options ??= ResolveSerializerOptions(response.HttpContext);
|
||||
|
||||
response.ContentType = contentType ?? JsonConstants.JsonContentTypeWithCharset;
|
||||
return JsonSerializer.SerializeAsync<TValue>(response.Body, value!, options, cancellationToken);
|
||||
return JsonSerializer.SerializeAsync<TValue>(response.Body, value, options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Http.Extensions.Tests
|
|||
context.Response.Body = body;
|
||||
|
||||
// Act
|
||||
await context.Response.WriteAsJsonAsync<Uri>(value: null);
|
||||
await context.Response.WriteAsJsonAsync<Uri?>(value: null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(JsonConstants.JsonContentTypeWithCharset, context.Response.ContentType);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,31 +1,28 @@
|
|||
// 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.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Features
|
||||
{
|
||||
public struct FeatureReference<T>
|
||||
{
|
||||
private T _feature;
|
||||
private T? _feature;
|
||||
private int _revision;
|
||||
|
||||
private FeatureReference(T feature, int revision)
|
||||
private FeatureReference(T? feature, int revision)
|
||||
{
|
||||
_feature = feature;
|
||||
_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)
|
||||
{
|
||||
return _feature;
|
||||
}
|
||||
_feature = (T)features[typeof(T)]!;
|
||||
_feature = (T)features[typeof(T)];
|
||||
_revision = features.Revision;
|
||||
return _feature;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
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,
|
||||
// if it was a Property then that getter would return a copy of the memory
|
||||
// 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
|
||||
// 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
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TFeature Fetch<TFeature, TState>(
|
||||
[AllowNull, MaybeNull] ref TFeature cached,
|
||||
public TFeature? Fetch<TFeature, TState>(
|
||||
ref TFeature? cached,
|
||||
TState state,
|
||||
Func<TState, TFeature> factory) where TFeature : class?
|
||||
Func<TState, TFeature?> factory) where TFeature : class?
|
||||
{
|
||||
var flush = false;
|
||||
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
|
||||
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)
|
||||
{
|
||||
|
|
@ -108,8 +106,8 @@ namespace Microsoft.AspNetCore.Http.Features
|
|||
return cached;
|
||||
}
|
||||
|
||||
public TFeature Fetch<TFeature>([AllowNull, MaybeNull] ref TFeature cached, Func<IFeatureCollection, TFeature> factory)
|
||||
where TFeature : class? => Fetch(ref cached!, Collection, factory);
|
||||
public TFeature? Fetch<TFeature>(ref TFeature? cached, Func<IFeatureCollection, TFeature?> factory)
|
||||
where TFeature : class? => Fetch(ref cached, Collection, factory);
|
||||
|
||||
private static int ContextDisposed()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
|
@ -58,8 +57,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue