如何为验证对象属性的方法正确使用nullable分析属性
C# 可空属性校验后仍触发空警告的解决方案
问题原因
C# 可空引用类型的静态分析默认不会跨方法校验内部逻辑,你在Validate方法中实现的空检查规则无法被分析器感知,因此调用完Validate后,分析器仍认为可空属性可能为null。此外你现有Validate方法的三元判断逻辑写反了,当前逻辑为属性为空时不抛异常、非空时才抛出,需要先修正这个逻辑错误。
解决步骤
首先引入System.Diagnostics.CodeAnalysis命名空间,使用官方提供的可空分析特性告知分析器你的校验规则:
1. 标记Validate方法的非空承诺
给Validate方法添加[MemberNotNull]特性,声明该方法正常返回(无异常抛出)时,指定的属性一定不为null:
using System.Diagnostics.CodeAnalysis; public class CosmosDbOptions { public string? DatabaseName { get; set; } public string? InitialContainerName { get; set; } #pragma warning disable CA1056 // URI-like properties should not be strings public string? EndpointUrl { get; set; } #pragma warning restore CA1056 // URI-like properties should not be strings public string? AuthorizationKey { get; set; } public string? AzureADTenantId { get; set; } public TimeSpan? Timeout { get; set; } public AuthenticationMode AuthenticationMode => GetAuthenticationMode(); public string? KeyVaultKeyName { get; private set; } private AuthenticationMode GetAuthenticationMode() { if (!string.IsNullOrWhiteSpace(AzureADTenantId)) { return AuthenticationMode.AzureActiveDirectoryManagedIdentity; } else if (!string.IsNullOrWhiteSpace(KeyVaultKeyName)) { return AuthenticationMode.AuhtorizationKeyFromKeyVault; } else if (!string.IsNullOrWhiteSpace(AuthorizationKey)) { return AuthenticationMode.AuthorizationKey; } return AuthenticationMode.None; } [MemberNotNull(nameof(DatabaseName), nameof(InitialContainerName), nameof(EndpointUrl))] public void Validate() { // 修正三元判断逻辑,为空时抛异常 DatabaseName = string.IsNullOrWhiteSpace(DatabaseName) ? throw new ArgumentNullException(nameof(DatabaseName)) : DatabaseName; InitialContainerName = string.IsNullOrWhiteSpace(InitialContainerName) ? throw new ArgumentNullException(nameof(InitialContainerName)) : InitialContainerName; EndpointUrl = string.IsNullOrWhiteSpace(EndpointUrl) ? throw new ArgumentNullException(nameof(EndpointUrl)) : EndpointUrl; if (AuthenticationMode == AuthenticationMode.None) { throw new NotSupportedException("Authentication mode for accessing CosmosDb is not valid!"); } if (AuthenticationMode == AuthenticationMode.AuthorizationKey) { if (string.IsNullOrWhiteSpace(AuthorizationKey)) { throw new ArgumentNullException(nameof(AuthorizationKey)); } } if (AuthenticationMode == AuthenticationMode.AzureActiveDirectoryManagedIdentity) { if (string.IsNullOrWhiteSpace(AzureADTenantId)) { throw new ArgumentNullException(nameof(AzureADTenantId)); } } } // 新增分支判断辅助方法 [MemberNotNullWhen(true, nameof(AuthorizationKey))] public bool UseAuthorizationKeyMode() => AuthenticationMode == AuthenticationMode.AuthorizationKey; [MemberNotNullWhen(true, nameof(AzureADTenantId))] public bool UseAzureAdMode() => AuthenticationMode == AuthenticationMode.AzureActiveDirectoryManagedIdentity; } public enum AuthenticationMode { AuthorizationKey, AuhtorizationKeyFromKeyVault, AzureActiveDirectoryManagedIdentity, None }
2. 调整调用侧判断逻辑
将分支判断改为使用新增的辅助方法,分析器就能识别对应分支下的属性非空:
public class Test { public void DoSomeAction(CosmosDbOptions options) { _ = options ?? throw new ArgumentNullException(nameof(options)); options.Validate(); string x = options.DatabaseName; // 无空警告 if (options.UseAuthorizationKeyMode()) { string y = options.AuthorizationKey; // 无空警告 } } }
上述方案兼容.NET Core 3.0及以上所有版本,无需使用空包容运算符就能消除所有合法场景下的空警告。
内容的提问来源于stack exchange,提问作者Mahdi
相关产品推荐
相关产品推荐

