Use T? for unconstrained nullable types (#25772)

This commit is contained in:
Pranav K 2020-09-10 15:15:11 -07:00 committed by GitHub
parent 36f8642f0b
commit 18d261b20c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 43 additions and 56 deletions

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -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()
{

View File

@ -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);
}