From 521023372b9ecda89522f44974cea45adf2ce6aa Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 11 Jan 2022 11:47:31 +0100 Subject: [PATCH] Fix extension methods --- src/Umbraco.Core/Attempt.cs | 4 +- src/Umbraco.Core/AttemptOfTResult.cs | 18 +-- .../Extensions/AssemblyExtensions.cs | 22 +-- .../Extensions/ClaimsIdentityExtensions.cs | 46 ++++--- .../Extensions/ContentVariationExtensions.cs | 6 +- .../Extensions/DelegateExtensions.cs | 12 +- .../Extensions/DictionaryExtensions.cs | 22 +-- .../Extensions/EnumerableExtensions.cs | 9 +- .../NameValueCollectionExtensions.cs | 10 +- .../Extensions/ObjectExtensions.cs | 127 +++++++++--------- .../Extensions/PublishedContentExtensions.cs | 42 +++--- .../Extensions/PublishedPropertyExtension.cs | 4 +- .../Extensions/StringExtensions.cs | 33 +++-- src/Umbraco.Core/Extensions/TypeExtensions.cs | 38 +++--- src/Umbraco.Core/Extensions/UriExtensions.cs | 2 +- .../Extensions/WaitHandleExtensions.cs | 6 +- src/Umbraco.Core/Extensions/XmlExtensions.cs | 58 ++++---- src/Umbraco.Core/Xml/XmlNodeListFactory.cs | 16 +-- 18 files changed, 255 insertions(+), 220 deletions(-) diff --git a/src/Umbraco.Core/Attempt.cs b/src/Umbraco.Core/Attempt.cs index eddd2c8785..0a8987726a 100644 --- a/src/Umbraco.Core/Attempt.cs +++ b/src/Umbraco.Core/Attempt.cs @@ -17,9 +17,9 @@ namespace Umbraco.Cms.Core /// The type of the attempted operation result. /// The result of the attempt. /// The successful attempt. - public static Attempt Succeed(TResult result) + public static Attempt Succeed(TResult? result) { - return Attempt.Succeed(result); + return Attempt.Succeed(result); } /// diff --git a/src/Umbraco.Core/AttemptOfTResult.cs b/src/Umbraco.Core/AttemptOfTResult.cs index cebabe214b..ebe7d57d7f 100644 --- a/src/Umbraco.Core/AttemptOfTResult.cs +++ b/src/Umbraco.Core/AttemptOfTResult.cs @@ -10,7 +10,7 @@ namespace Umbraco.Cms.Core public struct Attempt { // private - use Succeed() or Fail() methods to create attempts - private Attempt(bool success, TResult result, Exception exception) + private Attempt(bool? success, TResult? result, Exception? exception) { Success = success; Result = result; @@ -20,22 +20,22 @@ namespace Umbraco.Cms.Core /// /// Gets a value indicating whether this was successful. /// - public bool Success { get; } + public bool? Success { get; } /// /// Gets the exception associated with an unsuccessful attempt. /// - public Exception Exception { get; } + public Exception? Exception { get; } /// /// Gets the attempt result. /// - public TResult Result { get; } + public TResult? Result { get; } /// /// Gets the attempt result, if successful, else a default value. /// - public TResult ResultOr(TResult value) => Success ? Result : value; + public TResult? ResultOr(TResult? value) => Success.HasValue && Success.Value ? Result : value; // optimize, use a singleton failed attempt private static readonly Attempt Failed = new Attempt(false, default(TResult), null); @@ -54,7 +54,7 @@ namespace Umbraco.Cms.Core /// /// The result of the attempt. /// The successful attempt. - public static Attempt Succeed(TResult result) + public static Attempt Succeed(TResult? result) { return new Attempt(true, result, null); } @@ -73,7 +73,7 @@ namespace Umbraco.Cms.Core /// /// The exception causing the failure of the attempt. /// The failed attempt. - public static Attempt Fail(Exception exception) + public static Attempt Fail(Exception? exception) { return new Attempt(false, default(TResult), exception); } @@ -115,7 +115,7 @@ namespace Umbraco.Cms.Core /// A value indicating whether the attempt is successful. /// The result of the attempt. /// The attempt. - public static Attempt If(bool condition, TResult result) + public static Attempt If(bool condition, TResult? result) { return new Attempt(condition, result, null); } @@ -125,7 +125,7 @@ namespace Umbraco.Cms.Core /// /// /// - public static implicit operator bool(Attempt a) + public static implicit operator bool?(Attempt a) { return a.Success; } diff --git a/src/Umbraco.Core/Extensions/AssemblyExtensions.cs b/src/Umbraco.Core/Extensions/AssemblyExtensions.cs index cefaae5b86..aea0f847ab 100644 --- a/src/Umbraco.Core/Extensions/AssemblyExtensions.cs +++ b/src/Umbraco.Core/Extensions/AssemblyExtensions.cs @@ -23,8 +23,7 @@ namespace Umbraco.Extensions { return _rootDir; } - - var codeBase = executingAssembly.CodeBase; + var codeBase = executingAssembly.Location; var uri = new Uri(codeBase); var path = uri.LocalPath; var baseDirectory = Path.GetDirectoryName(path); @@ -45,7 +44,7 @@ namespace Umbraco.Extensions /// public static FileInfo GetAssemblyFile(this Assembly assembly) { - var codeBase = assembly.CodeBase; + var codeBase = assembly.Location; var uri = new Uri(codeBase); var path = uri.LocalPath; return new FileInfo(path); @@ -58,7 +57,7 @@ namespace Umbraco.Extensions /// public static bool IsAppCodeAssembly(this Assembly assembly) { - if (assembly.FullName.StartsWith("App_Code")) + if (assembly.FullName!.StartsWith("App_Code")) { try { @@ -82,7 +81,7 @@ namespace Umbraco.Extensions public static bool IsGlobalAsaxAssembly(this Assembly assembly) { //only way I can figure out how to test is by the name - return assembly.FullName.StartsWith("App_global.asax"); + return assembly.FullName!.StartsWith("App_global.asax"); } /// @@ -90,12 +89,17 @@ namespace Umbraco.Extensions /// /// /// - public static FileInfo GetAssemblyFile(this AssemblyName assemblyName) + public static FileInfo? GetAssemblyFile(this AssemblyName assemblyName) { var codeBase = assemblyName.CodeBase; - var uri = new Uri(codeBase); - var path = uri.LocalPath; - return new FileInfo(path); + if (!string.IsNullOrEmpty(codeBase)) + { + var uri = new Uri(codeBase); + var path = uri.LocalPath; + return new FileInfo(path); + } + + return null; } } diff --git a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs index 9b3674b07b..19d2de1c85 100644 --- a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs +++ b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs @@ -13,7 +13,7 @@ namespace Umbraco.Extensions { public static class ClaimsIdentityExtensions { - public static T GetUserId(this IIdentity identity) + public static T? GetUserId(this IIdentity identity) { var strId = identity.GetUserId(); var converted = strId.TryConvertTo(); @@ -27,11 +27,11 @@ namespace Umbraco.Extensions /// /// The string value of the user id if found otherwise null /// - public static string GetUserId(this IIdentity identity) + public static string? GetUserId(this IIdentity identity) { if (identity == null) throw new ArgumentNullException(nameof(identity)); - string userId = null; + string? userId = null; if (identity is ClaimsIdentity claimsIdentity) { userId = claimsIdentity.FindFirstValue(ClaimTypes.NameIdentifier) @@ -48,11 +48,11 @@ namespace Umbraco.Extensions /// /// The string value of the user name if found otherwise null /// - public static string GetUserName(this IIdentity identity) + public static string? GetUserName(this IIdentity identity) { if (identity == null) throw new ArgumentNullException(nameof(identity)); - string username = null; + string? username = null; if (identity is ClaimsIdentity claimsIdentity) { username = claimsIdentity.FindFirstValue(ClaimTypes.Name) @@ -70,7 +70,7 @@ namespace Umbraco.Extensions /// /// The string value of the claim if found otherwise null /// - public static string FindFirstValue(this ClaimsIdentity identity, string claimType) + public static string? FindFirstValue(this ClaimsIdentity identity, string claimType) { if (identity == null) throw new ArgumentNullException(nameof(identity)); @@ -100,7 +100,7 @@ namespace Umbraco.Extensions /// /// Verified identity wrapped in a ClaimsIdentity with BackOfficeAuthentication type /// True if ClaimsIdentity - public static bool VerifyBackOfficeIdentity(this ClaimsIdentity identity, out ClaimsIdentity verifiedIdentity) + public static bool VerifyBackOfficeIdentity(this ClaimsIdentity identity, out ClaimsIdentity? verifiedIdentity) { if (identity is null) { @@ -119,7 +119,7 @@ namespace Umbraco.Extensions } } - verifiedIdentity = identity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType ? identity : new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType); + verifiedIdentity = identity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType ? identity : new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType); return true; } @@ -294,35 +294,44 @@ namespace Umbraco.Extensions /// /// /// User ID as integer - public static int GetId(this ClaimsIdentity identity) => int.Parse(identity.FindFirstValue(ClaimTypes.NameIdentifier), CultureInfo.InvariantCulture); + public static int? GetId(this ClaimsIdentity identity) + { + var firstValue = identity.FindFirstValue(ClaimTypes.NameIdentifier); + if (firstValue is not null) + { + int.Parse(firstValue, CultureInfo.InvariantCulture); + } + + return null; + } /// /// Get the real name belonging to the user from a ClaimsIdentity /// /// /// Real name of the user - public static string GetRealName(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.GivenName); + public static string? GetRealName(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.GivenName); /// /// Get the username of the user from a ClaimsIdentity /// /// /// Username of the user - public static string GetUsername(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Name); + public static string? GetUsername(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Name); /// /// Get the culture string from a ClaimsIdentity /// /// /// Culture string - public static string GetCultureString(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Locality); + public static string? GetCultureString(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Locality); /// /// Get the security stamp from a ClaimsIdentity /// /// /// Security stamp - public static string GetSecurityStamp(this ClaimsIdentity identity) => identity.FindFirstValue(Constants.Security.SecurityStampClaimType); + public static string? GetSecurityStamp(this ClaimsIdentity identity) => identity.FindFirstValue(Constants.Security.SecurityStampClaimType); /// /// Get the roles assigned to a user from a ClaimsIdentity @@ -336,17 +345,20 @@ namespace Umbraco.Extensions /// /// Adds or updates and existing claim. /// - public static void AddOrUpdateClaim(this ClaimsIdentity identity, Claim claim) + public static void AddOrUpdateClaim(this ClaimsIdentity identity, Claim? claim) { if (identity == null) { throw new ArgumentNullException(nameof(identity)); } - Claim existingClaim = identity.Claims.FirstOrDefault(x => x.Type == claim.Type); - identity.TryRemoveClaim(existingClaim); + if (claim is not null) + { + Claim? existingClaim = identity.Claims.FirstOrDefault(x => x.Type == claim.Type); + identity.TryRemoveClaim(existingClaim); - identity.AddClaim(claim); + identity.AddClaim(claim); + } } } } diff --git a/src/Umbraco.Core/Extensions/ContentVariationExtensions.cs b/src/Umbraco.Core/Extensions/ContentVariationExtensions.cs index c6d6b2c557..4469683acb 100644 --- a/src/Umbraco.Core/Extensions/ContentVariationExtensions.cs +++ b/src/Umbraco.Core/Extensions/ContentVariationExtensions.cs @@ -289,10 +289,10 @@ namespace Umbraco.Extensions /// Basically, exact is for one content type, or one property type, and !exact is for "all property types" of one content type. /// Both and can be "*" to indicate "all of them". /// - public static bool ValidateVariation(this ContentVariation variation, string culture, string segment, bool exact, bool wildcards, bool throwIfInvalid) + public static bool ValidateVariation(this ContentVariation variation, string? culture, string? segment, bool exact, bool wildcards, bool throwIfInvalid) { - culture = culture.NullOrWhiteSpaceAsNull(); - segment = segment.NullOrWhiteSpaceAsNull(); + culture = culture?.NullOrWhiteSpaceAsNull(); + segment = segment?.NullOrWhiteSpaceAsNull(); // if wildcards are disabled, do not allow "*" if (!wildcards && (culture == "*" || segment == "*")) diff --git a/src/Umbraco.Core/Extensions/DelegateExtensions.cs b/src/Umbraco.Core/Extensions/DelegateExtensions.cs index 43e3c8947b..40ee011641 100644 --- a/src/Umbraco.Core/Extensions/DelegateExtensions.cs +++ b/src/Umbraco.Core/Extensions/DelegateExtensions.cs @@ -10,7 +10,7 @@ namespace Umbraco.Extensions { public static class DelegateExtensions { - public static Attempt RetryUntilSuccessOrTimeout(this Func> task, TimeSpan timeout, TimeSpan pause) + public static Attempt RetryUntilSuccessOrTimeout(this Func> task, TimeSpan timeout, TimeSpan pause) { if (pause.TotalMilliseconds < 0) { @@ -20,14 +20,14 @@ namespace Umbraco.Extensions do { var result = task(); - if (result) { return result; } + if (result.Success.HasValue && result.Success.Value) { return result; } Thread.Sleep((int)pause.TotalMilliseconds); } while (stopwatch.Elapsed < timeout); - return Attempt.Fail(); + return Attempt.Fail(); } - public static Attempt RetryUntilSuccessOrMaxAttempts(this Func> task, int totalAttempts, TimeSpan pause) + public static Attempt RetryUntilSuccessOrMaxAttempts(this Func> task, int totalAttempts, TimeSpan pause) { if (pause.TotalMilliseconds < 0) { @@ -38,11 +38,11 @@ namespace Umbraco.Extensions { attempts++; var result = task(attempts); - if (result) { return result; } + if (result.Success.HasValue && result.Success.Value) { return result; } Thread.Sleep((int)pause.TotalMilliseconds); } while (attempts < totalAttempts); - return Attempt.Fail(); + return Attempt.Fail(); } } } diff --git a/src/Umbraco.Core/Extensions/DictionaryExtensions.cs b/src/Umbraco.Core/Extensions/DictionaryExtensions.cs index 6227d506f3..906f12282e 100644 --- a/src/Umbraco.Core/Extensions/DictionaryExtensions.cs +++ b/src/Umbraco.Core/Extensions/DictionaryExtensions.cs @@ -53,8 +53,9 @@ namespace Umbraco.Extensions /// If there is an item in the dictionary with the key, it will keep trying to update it until it can /// public static bool TryUpdate(this ConcurrentDictionary dict, TKey key, Func updateFactory) + where TKey : notnull { - TValue curValue; + TValue? curValue; while (dict.TryGetValue(key, out curValue)) { if (dict.TryUpdate(key, updateFactory(curValue), curValue)) @@ -80,8 +81,9 @@ namespace Umbraco.Extensions /// WARNING: If the value changes after we've retrieved it, then the item will not be updated /// public static bool TryUpdateOptimisitic(this ConcurrentDictionary dict, TKey key, Func updateFactory) + where TKey : notnull { - TValue curValue; + TValue? curValue; if (!dict.TryGetValue(key, out curValue)) return false; dict.TryUpdate(key, updateFactory(curValue), curValue); @@ -96,11 +98,12 @@ namespace Umbraco.Extensions /// /// public static IDictionary ConvertTo(this IDictionary d) + where TKeyOut : notnull { var result = new Dictionary(); foreach (DictionaryEntry v in d) { - result.Add((TKeyOut)v.Key, (TValOut)v.Value); + result.Add((TKeyOut)v.Key, (TValOut)v.Value!); } return result; } @@ -115,11 +118,12 @@ namespace Umbraco.Extensions /// /// public static IDictionary ConvertTo(this IDictionary d, Func keyConverter, Func valConverter) + where TKeyOut : notnull { var result = new Dictionary(); foreach (DictionaryEntry v in d) { - result.Add(keyConverter(v.Key), valConverter(v.Value)); + result.Add(keyConverter(v.Key), valConverter(v.Value!)); } return result; } @@ -205,7 +209,7 @@ namespace Umbraco.Extensions /// /// /// - public static string GetValueAsString(this IDictionary d, TKey key) + public static string? GetValueAsString(this IDictionary d, TKey key) => d.ContainsKey(key) ? d[key]!.ToString() : string.Empty; /// @@ -215,7 +219,7 @@ namespace Umbraco.Extensions /// /// /// - public static string GetValueAsString(this IDictionary d, TKey key, string defaultValue) + public static string? GetValueAsString(this IDictionary d, TKey key, string defaultValue) { if (d.ContainsKey(key)) { @@ -269,12 +273,13 @@ namespace Umbraco.Extensions /// The default value. /// The type /// The entry - public static TValue GetValueIgnoreCase(this IDictionary dictionary, string key, TValue defaultValue) + public static TValue GetValueIgnoreCase(this IDictionary dictionary, string? key, TValue + defaultValue) { key = dictionary.Keys.FirstOrDefault(i => i.InvariantEquals(key)); return key.IsNullOrWhiteSpace() == false - ? dictionary[key] + ? dictionary[key!] : defaultValue; } @@ -282,6 +287,7 @@ namespace Umbraco.Extensions this IEnumerable enumerable, Func syncKeySelector, Func> asyncValueSelector) + where TKey : notnull { Dictionary dictionary = new Dictionary(); diff --git a/src/Umbraco.Core/Extensions/EnumerableExtensions.cs b/src/Umbraco.Core/Extensions/EnumerableExtensions.cs index 6120c569d4..7abfcf0546 100644 --- a/src/Umbraco.Core/Extensions/EnumerableExtensions.cs +++ b/src/Umbraco.Core/Extensions/EnumerableExtensions.cs @@ -39,7 +39,7 @@ namespace Umbraco.Extensions yield return item; } - public static IEnumerable> InGroupsOf(this IEnumerable source, int groupSize) + public static IEnumerable> InGroupsOf(this IEnumerable? source, int groupSize) { if (source == null) throw new ArgumentNullException("source"); @@ -49,7 +49,7 @@ namespace Umbraco.Extensions // following code derived from MoreLinq and does not allocate bazillions of tuples - T[] temp = null; + T[]? temp = null; var count = 0; foreach (var item in source) @@ -234,9 +234,8 @@ namespace Umbraco.Extensions return sequence.Select( x => { - if (x is TActual) + if (x is TActual casted) { - var casted = x as TActual; projection.Invoke(casted); } return x; @@ -342,7 +341,7 @@ namespace Umbraco.Extensions // this is to support filtering with multiple types public static IEnumerable OfTypes(this IEnumerable contents, params Type[] types) { - return contents.Where(x => types.Contains(x.GetType())); + return contents.Where(x => types.Contains(x?.GetType())); } public static IEnumerable SkipLast(this IEnumerable source) diff --git a/src/Umbraco.Core/Extensions/NameValueCollectionExtensions.cs b/src/Umbraco.Core/Extensions/NameValueCollectionExtensions.cs index 1d9b093ef1..9ebc218929 100644 --- a/src/Umbraco.Core/Extensions/NameValueCollectionExtensions.cs +++ b/src/Umbraco.Core/Extensions/NameValueCollectionExtensions.cs @@ -9,11 +9,11 @@ namespace Umbraco.Extensions { public static class NameValueCollectionExtensions { - public static IEnumerable> AsEnumerable(this NameValueCollection nvc) + public static IEnumerable> AsEnumerable(this NameValueCollection nvc) { - foreach (string key in nvc.AllKeys) + foreach (string? key in nvc.AllKeys) { - yield return new KeyValuePair(key, nvc[key]); + yield return new KeyValuePair(key, nvc[key]); } } @@ -22,7 +22,7 @@ namespace Umbraco.Extensions return collection.Keys.Cast().Any(k => (string) k == key); } - public static T GetValue(this NameValueCollection collection, string key, T defaultIfNotFound) + public static T? GetValue(this NameValueCollection collection, string key, T defaultIfNotFound) { if (collection.ContainsKey(key) == false) { @@ -37,7 +37,7 @@ namespace Umbraco.Extensions var result = val.TryConvertTo(); - return result.Success ? result.Result : defaultIfNotFound; + return result.Success.HasValue && result.Success.Value ? result.Result : defaultIfNotFound; } } } diff --git a/src/Umbraco.Core/Extensions/ObjectExtensions.cs b/src/Umbraco.Core/Extensions/ObjectExtensions.cs index fae727b8f3..2013c35e24 100644 --- a/src/Umbraco.Core/Extensions/ObjectExtensions.cs +++ b/src/Umbraco.Core/Extensions/ObjectExtensions.cs @@ -22,9 +22,9 @@ namespace Umbraco.Extensions /// public static class ObjectExtensions { - private static readonly ConcurrentDictionary NullableGenericCache = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary InputTypeConverterCache = new ConcurrentDictionary(); - private static readonly ConcurrentDictionary DestinationTypeConverterCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary NullableGenericCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary InputTypeConverterCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary DestinationTypeConverterCache = new ConcurrentDictionary(); private static readonly ConcurrentDictionary AssignableTypeCache = new ConcurrentDictionary(); private static readonly ConcurrentDictionary BoolConvertCache = new ConcurrentDictionary(); @@ -61,7 +61,7 @@ namespace Umbraco.Extensions /// /// The input. /// - public static T SafeCast(this object input) + public static T? SafeCast(this object input) { if (ReferenceEquals(null, input) || ReferenceEquals(default(T), input)) return default; if (input is T variable) return variable; @@ -77,11 +77,11 @@ namespace Umbraco.Extensions /// The public static Attempt TryConvertTo(this object? input) { - Attempt result = TryConvertTo(input, typeof(T)); + Attempt result = TryConvertTo(input, typeof(T)); - if (result.Success) + if (result.Success.HasValue && result.Success.Value) { - return Attempt.Succeed((T)result.Result); + return Attempt.Succeed((T?)result.Result); } if (input == null) @@ -94,7 +94,7 @@ namespace Umbraco.Extensions else { // sure, null can be any object - return Attempt.Succeed((T)input); + return Attempt.Succeed((T)input!); } } @@ -116,11 +116,11 @@ namespace Umbraco.Extensions /// The input. /// The type to convert to /// The - public static Attempt TryConvertTo(this object input, Type target) + public static Attempt TryConvertTo(this object? input, Type target) { if (target == null) { - return Attempt.Fail(); + return Attempt.Fail(); } try @@ -130,11 +130,11 @@ namespace Umbraco.Extensions // Nullable is ok if (target.IsGenericType && GetCachedGenericNullableType(target) != null) { - return Attempt.Succeed(null); + return Attempt.Succeed(null); } // Reference types are ok - return Attempt.If(target.IsValueType == false, null); + return Attempt.If(target.IsValueType == false, null); } var inputType = input.GetType(); @@ -148,7 +148,7 @@ namespace Umbraco.Extensions // Check for string so that overloaders of ToString() can take advantage of the conversion. if (target == typeof(string)) { - return Attempt.Succeed(input.ToString()); + return Attempt.Succeed(input.ToString()); } // If we've got a nullable of something, we try to convert directly to that thing. @@ -165,7 +165,7 @@ namespace Umbraco.Extensions // TODO: Why the check against only bool/date when a string is null/empty? In what scenario can we convert to another type when the string is null or empty other than just being null? if (string.IsNullOrEmpty(inputString) && (underlying == typeof(DateTime) || underlying == typeof(bool))) { - return Attempt.Succeed(null); + return Attempt.Succeed(null); } } @@ -173,13 +173,13 @@ namespace Umbraco.Extensions var inner = input.TryConvertTo(underlying); // And if successful, fall on through to rewrap in a nullable; if failed, pass on the exception - if (inner.Success) + if (inner.Success.HasValue && inner.Success.Value) { input = inner.Result; // Now fall on through... } else { - return Attempt.Fail(inner.Exception); + return Attempt.Fail(inner.Exception); } } } @@ -211,7 +211,7 @@ namespace Umbraco.Extensions { if (GetCachedCanConvertToBoolean(inputType)) { - return Attempt.Succeed(CustomBooleanTypeConverter.ConvertFrom(input)); + return Attempt.Succeed(CustomBooleanTypeConverter.ConvertFrom(input!)); } } @@ -224,7 +224,7 @@ namespace Umbraco.Extensions var outputConverter = GetCachedTargetTypeConverter(inputType, target); if (outputConverter != null) { - return Attempt.Succeed(outputConverter.ConvertFrom(input)); + return Attempt.Succeed(outputConverter.ConvertFrom(input!)); } if (target.IsGenericType && GetCachedGenericNullableType(target) != null) @@ -243,10 +243,10 @@ namespace Umbraco.Extensions } catch (Exception e) { - return Attempt.Fail(e); + return Attempt.Fail(e); } - return Attempt.Fail(); + return Attempt.Fail(); } /// @@ -256,12 +256,12 @@ namespace Umbraco.Extensions /// The input. /// The type to convert to /// The - private static Attempt? TryConvertToFromString(this string input, Type target) + private static Attempt? TryConvertToFromString(this string input, Type target) { // Easy if (target == typeof(string)) { - return Attempt.Succeed(input); + return Attempt.Succeed(input); } // Null, empty, whitespaces @@ -270,13 +270,13 @@ namespace Umbraco.Extensions if (target == typeof(bool)) { // null/empty = bool false - return Attempt.Succeed(false); + return Attempt.Succeed(false); } if (target == typeof(DateTime)) { // null/empty = min DateTime value - return Attempt.Succeed(DateTime.MinValue); + return Attempt.Succeed(DateTime.MinValue); } // Cannot decide here, @@ -296,25 +296,25 @@ namespace Umbraco.Extensions { if (int.TryParse(input, out var value)) { - return Attempt.Succeed(value); + return Attempt.Succeed(value); } // Because decimal 100.01m will happily convert to integer 100, it // makes sense that string "100.01" *also* converts to integer 100. var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.If(decimal.TryParse(input2, out var value2), Convert.ToInt32(value2)); + return Attempt.If(decimal.TryParse(input2, out var value2), Convert.ToInt32(value2)); } if (target == typeof(long)) { if (long.TryParse(input, out var value)) { - return Attempt.Succeed(value); + return Attempt.Succeed(value); } // Same as int var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.If(decimal.TryParse(input2, out var value2), Convert.ToInt64(value2)); + return Attempt.If(decimal.TryParse(input2, out var value2), Convert.ToInt64(value2)); } // TODO: Should we do the decimal trick for short, byte, unsigned? @@ -323,7 +323,7 @@ namespace Umbraco.Extensions { if (bool.TryParse(input, out var value)) { - return Attempt.Succeed(value); + return Attempt.Succeed(value); } // Don't declare failure so the CustomBooleanTypeConverter can try @@ -334,38 +334,38 @@ namespace Umbraco.Extensions switch (Type.GetTypeCode(target)) { case TypeCode.Int16: - return Attempt.If(short.TryParse(input, out var value), value); + return Attempt.If(short.TryParse(input, out var value), value); case TypeCode.Double: var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.If(double.TryParse(input2, out var valueD), valueD); + return Attempt.If(double.TryParse(input2, out var valueD), valueD); case TypeCode.Single: var input3 = NormalizeNumberDecimalSeparator(input); - return Attempt.If(float.TryParse(input3, out var valueF), valueF); + return Attempt.If(float.TryParse(input3, out var valueF), valueF); case TypeCode.Char: - return Attempt.If(char.TryParse(input, out var valueC), valueC); + return Attempt.If(char.TryParse(input, out var valueC), valueC); case TypeCode.Byte: - return Attempt.If(byte.TryParse(input, out var valueB), valueB); + return Attempt.If(byte.TryParse(input, out var valueB), valueB); case TypeCode.SByte: - return Attempt.If(sbyte.TryParse(input, out var valueSb), valueSb); + return Attempt.If(sbyte.TryParse(input, out var valueSb), valueSb); case TypeCode.UInt32: - return Attempt.If(uint.TryParse(input, out var valueU), valueU); + return Attempt.If(uint.TryParse(input, out var valueU), valueU); case TypeCode.UInt16: - return Attempt.If(ushort.TryParse(input, out var valueUs), valueUs); + return Attempt.If(ushort.TryParse(input, out var valueUs), valueUs); case TypeCode.UInt64: - return Attempt.If(ulong.TryParse(input, out var valueUl), valueUl); + return Attempt.If(ulong.TryParse(input, out var valueUl), valueUl); } } else if (target == typeof(Guid)) { - return Attempt.If(Guid.TryParse(input, out var value), value); + return Attempt.If(Guid.TryParse(input, out var value), value); } else if (target == typeof(DateTime)) { @@ -375,34 +375,34 @@ namespace Umbraco.Extensions { case DateTimeKind.Unspecified: case DateTimeKind.Utc: - return Attempt.Succeed(value); + return Attempt.Succeed(value); case DateTimeKind.Local: - return Attempt.Succeed(value.ToUniversalTime()); + return Attempt.Succeed(value.ToUniversalTime()); default: throw new ArgumentOutOfRangeException(); } } - return Attempt.Fail(); + return Attempt.Fail(); } else if (target == typeof(DateTimeOffset)) { - return Attempt.If(DateTimeOffset.TryParse(input, out var value), value); + return Attempt.If(DateTimeOffset.TryParse(input, out var value), value); } else if (target == typeof(TimeSpan)) { - return Attempt.If(TimeSpan.TryParse(input, out var value), value); + return Attempt.If(TimeSpan.TryParse(input, out var value), value); } else if (target == typeof(decimal)) { var input2 = NormalizeNumberDecimalSeparator(input); - return Attempt.If(decimal.TryParse(input2, out var value), value); + return Attempt.If(decimal.TryParse(input2, out var value), value); } else if (input != null && target == typeof(Version)) { - return Attempt.If(Version.TryParse(input, out var value), value); + return Attempt.If(Version.TryParse(input, out var value), value); } // E_NOTIMPL IPAddress, BigInteger @@ -489,9 +489,9 @@ namespace Umbraco.Extensions /// /// /// - public static IDictionary ToDictionary(this T o, params Expression>[] ignoreProperties) + public static IDictionary? ToDictionary(this T o, params Expression>[] ignoreProperties) { - return o.ToDictionary(ignoreProperties.Select(e => o.GetPropertyInfo(e)).Select(propInfo => propInfo.Name).ToArray()); + return o?.ToDictionary(ignoreProperties.Select(e => o.GetPropertyInfo(e)).Select(propInfo => propInfo.Name).ToArray()); } /// @@ -521,7 +521,7 @@ namespace Umbraco.Extensions - internal static string ToDebugString(this object obj, int levels = 0) + internal static string? ToDebugString(this object? obj, int levels = 0) { if (obj == null) return "{null}"; try @@ -589,7 +589,7 @@ namespace Umbraco.Extensions /// /// /// - internal static Attempt TryConvertToXmlString(this object value, Type type) + internal static Attempt TryConvertToXmlString(this object value, Type type) { try { @@ -598,7 +598,7 @@ namespace Umbraco.Extensions } catch (NotSupportedException ex) { - return Attempt.Fail(ex); + return Attempt.Fail(ex); } } @@ -608,7 +608,7 @@ namespace Umbraco.Extensions /// /// The Type can only be a primitive type or Guid and byte[] otherwise an exception is thrown /// - public static string ToXmlString(this object value, Type type) + public static string? ToXmlString(this object value, Type type) { if (value == null) return string.Empty; if (type == typeof(string)) return (value.ToString().IsNullOrWhiteSpace() ? "" : value.ToString()); @@ -640,12 +640,12 @@ namespace Umbraco.Extensions /// /// /// - public static string ToXmlString(this object value) + public static string? ToXmlString(this object value) { return value.ToXmlString(typeof (T)); } - private static string GetEnumPropertyDebugString(object enumItem, int levels) + private static string? GetEnumPropertyDebugString(object enumItem, int levels) { try { @@ -657,7 +657,7 @@ namespace Umbraco.Extensions } } - private static string GetPropertyDebugString(PropertyInfo propertyInfo, object obj, int levels) + private static string? GetPropertyDebugString(PropertyInfo propertyInfo, object obj, int levels) { try { @@ -683,7 +683,7 @@ namespace Umbraco.Extensions // gets a converter for source, that can convert to target, or null if none exists [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static TypeConverter GetCachedSourceTypeConverter(Type source, Type target) + private static TypeConverter? GetCachedSourceTypeConverter(Type source, Type target) { var key = new CompositeTypeTypeKey(source, target); @@ -698,12 +698,13 @@ namespace Umbraco.Extensions return InputTypeConverterCache[key] = converter; } - return InputTypeConverterCache[key] = null; + InputTypeConverterCache[key] = null; + return null; } // gets a converter for target, that can convert from source, or null if none exists [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static TypeConverter GetCachedTargetTypeConverter(Type source, Type target) + private static TypeConverter? GetCachedTargetTypeConverter(Type source, Type target) { var key = new CompositeTypeTypeKey(source, target); @@ -718,12 +719,13 @@ namespace Umbraco.Extensions return DestinationTypeConverterCache[key] = converter; } - return DestinationTypeConverterCache[key] = null; + DestinationTypeConverterCache[key] = null; + return null; } // gets the underlying type of a nullable type, or null if the type is not nullable [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Type GetCachedGenericNullableType(Type type) + private static Type? GetCachedGenericNullableType(Type type) { if (NullableGenericCache.TryGetValue(type, out var underlyingType)) { @@ -732,11 +734,12 @@ namespace Umbraco.Extensions if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) { - Type underlying = Nullable.GetUnderlyingType(type); + Type? underlying = Nullable.GetUnderlyingType(type); return NullableGenericCache[type] = underlying; } - return NullableGenericCache[type] = null; + NullableGenericCache[type] = null; + return null; } // gets an IConvertible from source to target type, or null if none exists diff --git a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs index b241316242..3f1b60fa13 100644 --- a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs @@ -523,7 +523,7 @@ namespace Umbraco.Extensions /// The level. /// The nearest (in down-top order) ancestor of the content, at a level lesser or equal to the specified level. /// Does not consider the content itself. May return null. - public static IPublishedContent Ancestor(this IPublishedContent content, int maxLevel) + public static IPublishedContent? Ancestor(this IPublishedContent content, int maxLevel) { return content.EnumerateAncestors(false).FirstOrDefault(x => x.Level <= maxLevel); } @@ -535,7 +535,7 @@ namespace Umbraco.Extensions /// The content type alias. /// The nearest (in down-top order) ancestor of the content, of the specified content type. /// Does not consider the content itself. May return null. - public static IPublishedContent Ancestor(this IPublishedContent content, string contentTypeAlias) + public static IPublishedContent? Ancestor(this IPublishedContent content, string contentTypeAlias) { return content.EnumerateAncestors(false).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); } @@ -547,7 +547,7 @@ namespace Umbraco.Extensions /// The content. /// The nearest (in down-top order) ancestor of the content, of the specified content type. /// Does not consider the content itself. May return null. - public static T Ancestor(this IPublishedContent content) + public static T? Ancestor(this IPublishedContent content) where T : class, IPublishedContent { return content.Ancestors().FirstOrDefault(); @@ -562,7 +562,7 @@ namespace Umbraco.Extensions /// The ancestor of the content, at the specified level and of the specified content type. /// Does not consider the content itself. If the ancestor at the specified level is /// not of the specified type, returns null. - public static T Ancestor(this IPublishedContent content, int maxLevel) + public static T? Ancestor(this IPublishedContent content, int maxLevel) where T : class, IPublishedContent { return content.Ancestors(maxLevel).FirstOrDefault(); @@ -586,7 +586,7 @@ namespace Umbraco.Extensions /// The level. /// The content or its nearest (in down-top order) ancestor, at a level lesser or equal to the specified level. /// May or may not return the content itself depending on its level. May return null. - public static IPublishedContent AncestorOrSelf(this IPublishedContent content, int maxLevel) + public static IPublishedContent? AncestorOrSelf(this IPublishedContent content, int maxLevel) { return content.EnumerateAncestors(true).FirstOrDefault(x => x.Level <= maxLevel); } @@ -598,7 +598,7 @@ namespace Umbraco.Extensions /// The content type. /// The content or its nearest (in down-top order) ancestor, of the specified content type. /// May or may not return the content itself depending on its content type. May return null. - public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string contentTypeAlias) + public static IPublishedContent? AncestorOrSelf(this IPublishedContent content, string contentTypeAlias) { return content.EnumerateAncestors(true).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); } @@ -610,7 +610,7 @@ namespace Umbraco.Extensions /// The content. /// The content or its nearest (in down-top order) ancestor, of the specified content type. /// May or may not return the content itself depending on its content type. May return null. - public static T AncestorOrSelf(this IPublishedContent content) + public static T? AncestorOrSelf(this IPublishedContent content) where T : class, IPublishedContent { return content.AncestorsOrSelf().FirstOrDefault(); @@ -623,7 +623,7 @@ namespace Umbraco.Extensions /// The content. /// The level. /// - public static T AncestorOrSelf(this IPublishedContent content, int maxLevel) + public static T? AncestorOrSelf(this IPublishedContent content, int maxLevel) where T : class, IPublishedContent { return content.AncestorsOrSelf(maxLevel).FirstOrDefault(); @@ -817,17 +817,17 @@ namespace Umbraco.Extensions return content.DescendantsOrSelf(variationContextAccessor, level, culture).OfType(); } - public static IPublishedContent Descendant(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) + public static IPublishedContent? Descendant(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) { return content.Children(variationContextAccessor, culture).FirstOrDefault(); } - public static IPublishedContent Descendant(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string? culture = null) + public static IPublishedContent? Descendant(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string? culture = null) { return content.EnumerateDescendants(variationContextAccessor, false, culture).FirstOrDefault(x => x.Level == level); } - public static IPublishedContent DescendantOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string? culture = null) + public static IPublishedContent? DescendantOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string? culture = null) { return content.EnumerateDescendants(variationContextAccessor, false, culture).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); } @@ -849,12 +849,12 @@ namespace Umbraco.Extensions return content; } - public static IPublishedContent DescendantOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string? culture = null) + public static IPublishedContent? DescendantOrSelf(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string? culture = null) { return content.EnumerateDescendants(variationContextAccessor, true, culture).FirstOrDefault(x => x.Level == level); } - public static IPublishedContent DescendantOrSelfOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string? culture = null) + public static IPublishedContent? DescendantOrSelfOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string? culture = null) { return content.EnumerateDescendants(variationContextAccessor, true, culture).FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAlias)); } @@ -978,7 +978,7 @@ namespace Umbraco.Extensions return content.Children(variationContextAccessor, culture).OfType(); } - public static IPublishedContent FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) + public static IPublishedContent? FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) { return content.Children(variationContextAccessor, culture).FirstOrDefault(); } @@ -986,28 +986,28 @@ namespace Umbraco.Extensions /// /// Gets the first child of the content, of a given content type. /// - public static IPublishedContent FirstChildOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string? culture = null) + public static IPublishedContent? FirstChildOfType(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, string? culture = null) { return content.ChildrenOfType(variationContextAccessor, contentTypeAlias, culture).FirstOrDefault(); } - public static IPublishedContent FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, string? culture = null) + public static IPublishedContent? FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, string? culture = null) { return content.Children(variationContextAccessor, predicate, culture).FirstOrDefault(); } - public static IPublishedContent FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Guid uniqueId, string? culture = null) + public static IPublishedContent? FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Guid uniqueId, string? culture = null) { return content.Children(variationContextAccessor, x => x.Key == uniqueId, culture).FirstOrDefault(); } - public static T FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) + public static T? FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) where T : class, IPublishedContent { return content.Children(variationContextAccessor, culture).FirstOrDefault(); } - public static T FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, string? culture = null) + public static T? FirstChild(this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, string? culture = null) where T : class, IPublishedContent { return content.Children(variationContextAccessor, culture).FirstOrDefault(predicate); @@ -1149,7 +1149,7 @@ namespace Umbraco.Extensions /// /// This is the same as calling with maxLevel set to 1. /// - public static IPublishedContent Root(this IPublishedContent content) + public static IPublishedContent? Root(this IPublishedContent content) { return content.AncestorOrSelf(1); } @@ -1165,7 +1165,7 @@ namespace Umbraco.Extensions /// /// This is the same as calling with maxLevel set to 1. /// - public static T Root(this IPublishedContent content) + public static T? Root(this IPublishedContent content) where T : class, IPublishedContent { return content.AncestorOrSelf(1); diff --git a/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs b/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs index f9bca6effd..903633e9e4 100644 --- a/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs +++ b/src/Umbraco.Core/Extensions/PublishedPropertyExtension.cs @@ -38,7 +38,7 @@ namespace Umbraco.Extensions } var valueConverted = value.TryConvertTo(); - if (valueConverted) + if (valueConverted.Success.HasValue && valueConverted.Success.Value) { return valueConverted.Result; } @@ -63,7 +63,7 @@ namespace Umbraco.Extensions } var noValueConverted = noValue.TryConvertTo(); - if (noValueConverted) + if (noValueConverted.Success.HasValue && noValueConverted.Success.Value) { return noValueConverted.Result; } diff --git a/src/Umbraco.Core/Extensions/StringExtensions.cs b/src/Umbraco.Core/Extensions/StringExtensions.cs index 69ccda66d5..3202b3a253 100644 --- a/src/Umbraco.Core/Extensions/StringExtensions.cs +++ b/src/Umbraco.Core/Extensions/StringExtensions.cs @@ -43,7 +43,7 @@ namespace Umbraco.Extensions { var nodeIds = path.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries) .Select(x => int.TryParse(x, NumberStyles.Integer, CultureInfo.InvariantCulture, out var output) ? Attempt.Succeed(output) : Attempt.Fail()) - .Where(x => x.Success) + .Where(x => x.Success ?? false) .Select(x=>x.Result) .Reverse() .ToArray(); @@ -393,7 +393,7 @@ namespace Umbraco.Extensions /// The enum try parse. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "By Design")] [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "By Design")] - public static bool EnumTryParse(this string strType, bool ignoreCase, out T result) + public static bool EnumTryParse(this string strType, bool ignoreCase, out T? result) { try { @@ -498,7 +498,7 @@ namespace Umbraco.Extensions /// /// /// - public static string FromUrlBase64(this string input) + public static string? FromUrlBase64(this string input) { if (input == null) throw new ArgumentNullException(nameof(input)); @@ -522,9 +522,9 @@ namespace Umbraco.Extensions /// The format. /// The args. /// - public static string InvariantFormat(this string format, params object[] args) + public static string InvariantFormat(this string? format, params object?[] args) { - return String.Format(CultureInfo.InvariantCulture, format, args); + return string.Format(CultureInfo.InvariantCulture, format ?? string.Empty, args); } /// @@ -548,7 +548,7 @@ namespace Umbraco.Extensions /// The compare. /// The compare to. /// - public static bool InvariantEquals(this string compare, string compareTo) + public static bool InvariantEquals(this string compare, string? compareTo) { return String.Equals(compare, compareTo, StringComparison.InvariantCultureIgnoreCase); } @@ -590,9 +590,9 @@ namespace Umbraco.Extensions /// /// /// - public static T ParseInto(this string val) + public static T? ParseInto(this string val) { - return (T)val.ParseInto(typeof(T)); + return (T?)val.ParseInto(typeof(T)); } /// @@ -601,7 +601,7 @@ namespace Umbraco.Extensions /// /// /// - public static object ParseInto(this string val, Type type) + public static object? ParseInto(this string val, Type type) { if (string.IsNullOrEmpty(val) == false) { @@ -641,10 +641,15 @@ namespace Umbraco.Extensions /// Refers to itself /// String with the hash type. See remarks section of the CryptoConfig Class in MSDN docs for a list of possible values. /// The hashed string - private static string GenerateHash(this string str, string hashType) + private static string GenerateHash(this string str, string? hashType) { + HashAlgorithm? hasher = null; //create an instance of the correct hashing provider based on the type passed in - var hasher = HashAlgorithm.Create(hashType); + if (hashType is not null) + { + hasher = HashAlgorithm.Create(hashType); + } + if (hasher == null) throw new InvalidOperationException("No hashing type found by name " + hashType); using (hasher) { @@ -1184,7 +1189,7 @@ namespace Umbraco.Extensions { algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, null, 0); algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length); - hash = algorithm.Hash; + hash = algorithm.Hash!; } // most bytes from the hash are copied straight to the bytes of the new GUID (steps 5-7, 9, 11-12) @@ -1221,7 +1226,7 @@ namespace Umbraco.Extensions /// /// Turns an null-or-whitespace string into a null string. /// - public static string NullOrWhiteSpaceAsNull(this string text) + public static string? NullOrWhiteSpaceAsNull(this string text) => string.IsNullOrWhiteSpace(text) ? null : text; @@ -1236,7 +1241,7 @@ namespace Umbraco.Extensions return string.IsNullOrWhiteSpace(path) == false && path.IndexOfAny(Path.GetInvalidPathChars().ToArray()) == -1 && Path.IsPathRooted(path) - && Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) == false; + && Path.GetPathRoot(path)?.Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) == false; } // FORMAT STRINGS diff --git a/src/Umbraco.Core/Extensions/TypeExtensions.cs b/src/Umbraco.Core/Extensions/TypeExtensions.cs index 67a6dd1dce..272a80a69e 100644 --- a/src/Umbraco.Core/Extensions/TypeExtensions.cs +++ b/src/Umbraco.Core/Extensions/TypeExtensions.cs @@ -15,14 +15,14 @@ namespace Umbraco.Extensions { public static class TypeExtensions { - public static object GetDefaultValue(this Type t) + public static object? GetDefaultValue(this Type t) { return t.IsValueType ? Activator.CreateInstance(t) : null; } - internal static MethodInfo GetGenericMethod(this Type type, string name, params Type[] parameterTypes) + internal static MethodInfo? GetGenericMethod(this Type type, string name, params Type[] parameterTypes) { var methods = type.GetMethods().Where(method => method.Name == name); @@ -75,12 +75,12 @@ namespace Umbraco.Extensions return true; } - public static IEnumerable GetBaseTypes(this Type type, bool andSelf) + public static IEnumerable GetBaseTypes(this Type? type, bool andSelf) { if (andSelf) yield return type; - while ((type = type.BaseType) != null) + while ((type = type?.BaseType) != null) yield return type; } @@ -121,7 +121,7 @@ namespace Umbraco.Extensions /// public static bool IsOfGenericType(this Type type, Type genericType) { - Type[] args; + Type[]? args; return type.TryGetGenericArguments(genericType, out args); } @@ -132,7 +132,7 @@ namespace Umbraco.Extensions /// /// /// - public static bool TryGetGenericArguments(this Type type, Type genericType, out Type[] genericArgType) + public static bool TryGetGenericArguments(this Type type, Type genericType, out Type[]? genericArgType) { if (type == null) { @@ -147,7 +147,7 @@ namespace Umbraco.Extensions throw new ArgumentException("genericType must be a generic type"); } - Func checkGenericType = (@int, t) => + Func checkGenericType = (@int, t) => { if (@int.IsGenericType) { @@ -313,34 +313,34 @@ namespace Umbraco.Extensions return typeof(TInterface).IsAssignableFrom(type); } - public static TAttribute FirstAttribute(this Type type) + public static TAttribute? FirstAttribute(this Type type) { return type.FirstAttribute(true); } - public static TAttribute FirstAttribute(this Type type, bool inherit) + public static TAttribute? FirstAttribute(this Type type, bool inherit) { var attrs = type.GetCustomAttributes(typeof(TAttribute), inherit); - return (TAttribute)(attrs.Length > 0 ? attrs[0] : null); + return (TAttribute?)(attrs.Length > 0 ? attrs[0] : null); } - public static TAttribute FirstAttribute(this PropertyInfo propertyInfo) + public static TAttribute? FirstAttribute(this PropertyInfo propertyInfo) { return propertyInfo.FirstAttribute(true); } - public static TAttribute FirstAttribute(this PropertyInfo propertyInfo, bool inherit) + public static TAttribute? FirstAttribute(this PropertyInfo propertyInfo, bool inherit) { var attrs = propertyInfo.GetCustomAttributes(typeof(TAttribute), inherit); - return (TAttribute)(attrs.Length > 0 ? attrs[0] : null); + return (TAttribute?)(attrs.Length > 0 ? attrs[0] : null); } - public static IEnumerable MultipleAttribute(this PropertyInfo propertyInfo) + public static IEnumerable? MultipleAttribute(this PropertyInfo propertyInfo) { return propertyInfo.MultipleAttribute(true); } - public static IEnumerable MultipleAttribute(this PropertyInfo propertyInfo, bool inherit) + public static IEnumerable? MultipleAttribute(this PropertyInfo propertyInfo, bool inherit) { var attrs = propertyInfo.GetCustomAttributes(typeof(TAttribute), inherit); return (attrs.Length > 0 ? attrs.ToList().ConvertAll(input => (TAttribute)input) : null); @@ -389,8 +389,8 @@ namespace Umbraco.Extensions var t = c; while (t != typeof(object)) { - if (t.IsGenericType && t.GetGenericTypeDefinition() == type) return true; - t = t.BaseType; + if (t is not null && t.IsGenericType && t.GetGenericTypeDefinition() == type) return true; + t = t?.BaseType; } } @@ -403,7 +403,7 @@ namespace Umbraco.Extensions /// /// the source type /// - public static Type GetEnumeratedType(this Type type) + public static Type? GetEnumeratedType(this Type type) { if (typeof(IEnumerable).IsAssignableFrom(type) == false) return null; @@ -420,7 +420,7 @@ namespace Umbraco.Extensions return null; } - public static T GetCustomAttribute(this Type type, bool inherit) + public static T? GetCustomAttribute(this Type type, bool inherit) where T : Attribute { return type.GetCustomAttributes(inherit).SingleOrDefault(); diff --git a/src/Umbraco.Core/Extensions/UriExtensions.cs b/src/Umbraco.Core/Extensions/UriExtensions.cs index 858069edcf..52adbc6b67 100644 --- a/src/Umbraco.Core/Extensions/UriExtensions.cs +++ b/src/Umbraco.Core/Extensions/UriExtensions.cs @@ -154,7 +154,7 @@ namespace Umbraco.Extensions return new Uri(baseUri.GetLeftPart(UriPartial.Authority) + uri.GetSafeAbsolutePath() + uri.GetSafeQuery()); } - static string GetSafeQuery(this Uri uri) + static string? GetSafeQuery(this Uri uri) { if (uri.IsAbsoluteUri) return uri.Query; diff --git a/src/Umbraco.Core/Extensions/WaitHandleExtensions.cs b/src/Umbraco.Core/Extensions/WaitHandleExtensions.cs index 6058ef2974..5cb7639497 100644 --- a/src/Umbraco.Core/Extensions/WaitHandleExtensions.cs +++ b/src/Umbraco.Core/Extensions/WaitHandleExtensions.cs @@ -15,11 +15,11 @@ namespace Umbraco.Extensions public static Task WaitOneAsync(this WaitHandle handle, int millisecondsTimeout = Timeout.Infinite) { - var tcs = new TaskCompletionSource(); + var tcs = new TaskCompletionSource(); var callbackHandleInitLock = new object(); lock (callbackHandleInitLock) { - RegisteredWaitHandle callbackHandle = null; + RegisteredWaitHandle? callbackHandle = null; // ReSharper disable once RedundantAssignment callbackHandle = ThreadPool.RegisterWaitForSingleObject( handle, @@ -34,7 +34,7 @@ namespace Umbraco.Extensions { // ReSharper disable once PossibleNullReferenceException // ReSharper disable once AccessToModifiedClosure - callbackHandle.Unregister(null); + callbackHandle?.Unregister(null); } }, /*state:*/ null, diff --git a/src/Umbraco.Core/Extensions/XmlExtensions.cs b/src/Umbraco.Core/Extensions/XmlExtensions.cs index aa3fe25eef..423039a86a 100644 --- a/src/Umbraco.Core/Extensions/XmlExtensions.cs +++ b/src/Umbraco.Core/Extensions/XmlExtensions.cs @@ -35,7 +35,7 @@ namespace Umbraco.Extensions /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public static XmlNodeList SelectNodes(this XmlNode source, string expression, IEnumerable variables) + public static XmlNodeList? SelectNodes(this XmlNode source, string expression, IEnumerable? variables) { var av = variables == null ? null : variables.ToArray(); return SelectNodes(source, expression, av); @@ -53,7 +53,7 @@ namespace Umbraco.Extensions /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public static XmlNodeList SelectNodes(this XmlNode source, XPathExpression expression, IEnumerable variables) + public static XmlNodeList? SelectNodes(this XmlNode source, XPathExpression expression, IEnumerable? variables) { var av = variables == null ? null : variables.ToArray(); return SelectNodes(source, expression, av); @@ -71,12 +71,12 @@ namespace Umbraco.Extensions /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public static XmlNodeList SelectNodes(this XmlNode source, string expression, params XPathVariable[] variables) + public static XmlNodeList? SelectNodes(this XmlNode source, string? expression, params XPathVariable[]? variables) { if (variables == null || variables.Length == 0 || variables[0] == null) - return source.SelectNodes(expression); + return source.SelectNodes(expression ?? ""); - var iterator = source.CreateNavigator().Select(expression, variables); + var iterator = source.CreateNavigator()?.Select(expression ?? "", variables); return XmlNodeListFactory.CreateNodeList(iterator); } @@ -92,12 +92,12 @@ namespace Umbraco.Extensions /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public static XmlNodeList SelectNodes(this XmlNode source, XPathExpression expression, params XPathVariable[] variables) + public static XmlNodeList SelectNodes(this XmlNode source, XPathExpression expression, params XPathVariable[]? variables) { if (variables == null || variables.Length == 0 || variables[0] == null) return source.SelectNodes(expression); - var iterator = source.CreateNavigator().Select(expression, variables); + var iterator = source.CreateNavigator()?.Select(expression, variables); return XmlNodeListFactory.CreateNodeList(iterator); } @@ -113,7 +113,7 @@ namespace Umbraco.Extensions /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public static XmlNode SelectSingleNode(this XmlNode source, string expression, IEnumerable variables) + public static XmlNode? SelectSingleNode(this XmlNode source, string expression, IEnumerable? variables) { var av = variables == null ? null : variables.ToArray(); return SelectSingleNode(source, expression, av); @@ -131,7 +131,7 @@ namespace Umbraco.Extensions /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public static XmlNode SelectSingleNode(this XmlNode source, XPathExpression expression, IEnumerable variables) + public static XmlNode? SelectSingleNode(this XmlNode source, XPathExpression expression, IEnumerable? variables) { var av = variables == null ? null : variables.ToArray(); return SelectSingleNode(source, expression, av); @@ -149,12 +149,12 @@ namespace Umbraco.Extensions /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public static XmlNode SelectSingleNode(this XmlNode source, string expression, params XPathVariable[] variables) + public static XmlNode? SelectSingleNode(this XmlNode source, string expression, params XPathVariable[]? variables) { if (variables == null || variables.Length == 0 || variables[0] == null) return source.SelectSingleNode(expression); - return SelectNodes(source, expression, variables).Cast().FirstOrDefault(); + return SelectNodes(source, expression, variables)?.Cast().FirstOrDefault(); } /// @@ -169,7 +169,7 @@ namespace Umbraco.Extensions /// value which itself is null, then variables are ignored. /// The XPath expression should reference variables as $var. /// - public static XmlNode SelectSingleNode(this XmlNode source, XPathExpression expression, params XPathVariable[] variables) + public static XmlNode? SelectSingleNode(this XmlNode source, XPathExpression expression, params XPathVariable[]? variables) { if (variables == null || variables.Length == 0 || variables[0] == null) return source.SelectSingleNode(expression); @@ -211,7 +211,7 @@ namespace Umbraco.Extensions ///// ///// ///// - public static XmlNode ToXmlElement(this XContainer xElement) + public static XmlNode? ToXmlElement(this XContainer xElement) { var xmlDocument = new XmlDocument(); using (var xmlReader = xElement.CreateReader()) @@ -235,7 +235,7 @@ namespace Umbraco.Extensions } } - public static T RequiredAttributeValue(this XElement xml, string attributeName) + public static T? RequiredAttributeValue(this XElement xml, string attributeName) { if (xml == null) { @@ -247,14 +247,14 @@ namespace Umbraco.Extensions throw new InvalidOperationException($"{attributeName} not found in xml"); } - XAttribute attribute = xml.Attribute(attributeName); + XAttribute? attribute = xml.Attribute(attributeName); if (attribute is null) { throw new InvalidOperationException($"{attributeName} not found in xml"); } Attempt result = attribute.Value.TryConvertTo(); - if (result.Success) + if (result.Success.HasValue && result.Success.Value) { return result.Result; } @@ -262,7 +262,7 @@ namespace Umbraco.Extensions throw new InvalidOperationException($"{attribute.Value} attribute value cannot be converted to {typeof(T)}"); } - public static T AttributeValue(this XElement xml, string attributeName) + public static T? AttributeValue(this XElement xml, string attributeName) { if (xml == null) throw new ArgumentNullException("xml"); if (xml.HasAttributes == false) return default(T); @@ -270,15 +270,15 @@ namespace Umbraco.Extensions if (xml.Attribute(attributeName) == null) return default(T); - var val = xml.Attribute(attributeName).Value; + var val = xml.Attribute(attributeName)?.Value; var result = val.TryConvertTo(); - if (result.Success) + if (result.Success.HasValue && result.Success.Value) return result.Result; return default(T); } - public static T AttributeValue(this XmlNode xml, string attributeName) + public static T? AttributeValue(this XmlNode xml, string attributeName) { if (xml == null) throw new ArgumentNullException("xml"); if (xml.Attributes == null) return default(T); @@ -286,15 +286,15 @@ namespace Umbraco.Extensions if (xml.Attributes[attributeName] == null) return default(T); - var val = xml.Attributes[attributeName].Value; + var val = xml.Attributes[attributeName]?.Value; var result = val.TryConvertTo(); - if (result.Success) + if (result.Success.HasValue && result.Success.Value) return result.Result; return default(T); } - public static XElement GetXElement(this XmlNode node) + public static XElement? GetXElement(this XmlNode node) { XDocument xDoc = new XDocument(); using (XmlWriter xmlWriter = xDoc.CreateWriter()) @@ -302,7 +302,7 @@ namespace Umbraco.Extensions return xDoc.Root; } - public static XmlNode GetXmlNode(this XContainer element) + public static XmlNode? GetXmlNode(this XContainer element) { using (var xmlReader = element.CreateReader()) { @@ -312,9 +312,15 @@ namespace Umbraco.Extensions } } - public static XmlNode GetXmlNode(this XContainer element, XmlDocument xmlDoc) + public static XmlNode? GetXmlNode(this XContainer element, XmlDocument xmlDoc) { - return xmlDoc.ImportNode(element.GetXmlNode(), true); + var node = element.GetXmlNode(); + if (node is not null) + { + return xmlDoc.ImportNode(node, true); + } + + return null; } // this exists because diff --git a/src/Umbraco.Core/Xml/XmlNodeListFactory.cs b/src/Umbraco.Core/Xml/XmlNodeListFactory.cs index b0ab0dd3be..29797fc59a 100644 --- a/src/Umbraco.Core/Xml/XmlNodeListFactory.cs +++ b/src/Umbraco.Core/Xml/XmlNodeListFactory.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Core.Xml /// The underlying XML store used to issue the query must be /// an object inheriting , such as /// . - public static XmlNodeList CreateNodeList(XPathNodeIterator iterator) + public static XmlNodeList CreateNodeList(XPathNodeIterator? iterator) { return new XmlNodeListIterator(iterator); } @@ -34,12 +34,12 @@ namespace Umbraco.Cms.Core.Xml private class XmlNodeListIterator : XmlNodeList { - readonly XPathNodeIterator _iterator; + readonly XPathNodeIterator? _iterator; readonly IList _nodes = new List(); - public XmlNodeListIterator(XPathNodeIterator iterator) + public XmlNodeListIterator(XPathNodeIterator? iterator) { - _iterator = iterator.Clone(); + _iterator = iterator?.Clone(); } public override System.Collections.IEnumerator GetEnumerator() @@ -47,7 +47,7 @@ namespace Umbraco.Cms.Core.Xml return new XmlNodeListEnumerator(this); } - public override XmlNode Item(int index) + public override XmlNode? Item(int index) { if (index >= _nodes.Count) @@ -73,7 +73,7 @@ namespace Umbraco.Cms.Core.Xml /// private void ReadToEnd() { - while (_iterator.MoveNext()) + while (_iterator is not null && _iterator.MoveNext()) { var node = _iterator.Current as IHasXmlNode; // Check IHasXmlNode interface. @@ -92,7 +92,7 @@ namespace Umbraco.Cms.Core.Xml { while (_nodes.Count <= to) { - if (_iterator.MoveNext()) + if (_iterator is not null && _iterator.MoveNext()) { var node = _iterator.Current as IHasXmlNode; // Check IHasXmlNode interface. @@ -159,7 +159,7 @@ namespace Umbraco.Cms.Core.Xml return true; } - object System.Collections.IEnumerator.Current + object? System.Collections.IEnumerator.Current { get {