静态类报Readonly field _logger is never assigned错误的原因
报错触发原因
这个报错是C#编译器的可空引用类型检测触发的,当项目开启可空引用类型校验、且配置了将相关警告视为构建错误时,就会阻断编译,核心触发逻辑有两点:
- 静态类
LicenceSetter中的私有静态字段_logger仅做了声明,全代码路径中没有任何给它赋值有效实例的逻辑,字段始终保持默认值null。编译器检测到该字段从未被写入,会将其识别为未赋值的只读状态字段,抛出对应提示。 - 你在catch分支中直接调用
_logger.LogError方法,由于字段始终为null,运行到该逻辑时必然抛出空引用异常,编译器检测到这个运行时风险也会同步触发校验报错。
就算关闭构建时的警告校验,现有代码只要进入异常分支,就会因为_logger为null触发新的空引用错误,根本无法输出预期的错误日志。
解决方法
根据项目的日志接入架构,选以下任意一种方案即可:
方案1:静态构造函数全局初始化
适合项目有全局静态日志工厂的场景,在类的静态构造函数中一次性初始化Logger实例,类第一次被访问时会自动执行构造逻辑,不存在空引用风险:
internal static class LicenceSetter { private static readonly ILogger _logger; static LicenceSetter() { // 替换为你项目实际的日志工厂获取逻辑,以下是控制台日志的示例 var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); _logger = loggerFactory.CreateLogger(nameof(LicenceSetter)); } internal static void WorkWithStream(Action<Stream> setLicence, string name = null) { try { Assembly assembly = Assembly.GetExecutingAssembly(); var declaringType = MethodBase.GetCurrentMethod().DeclaringType; Debug.Assert(declaringType != null, "declaringType != null"); name = name ?? $"{declaringType.Namespace}.{AsposeTotalLic2017FileName}"; using (Stream stream = assembly.GetManifestResourceStream(name)) { setLicence(stream); } } catch (Exception exception) { _logger.LogError(exception,"Error loading embedded resource for aspose licence"); Trace.TraceError("Error loading embedded resource for aspose licence"); throw; } } }
方案2:启动时显式初始化静态字段
适合使用依赖注入容器的项目,在应用启动阶段从DI容器中取出ILogger实例,显式赋值给静态字段:
internal static class LicenceSetter { private static ILogger _logger; // 应用启动(Program.cs启动流程中)调用一次,传入DI解析出的ILogger实例 internal static void ConfigureLogger(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } // 原有WorkWithStream方法逻辑保持不变 }
注意:必须保证ConfigureLogger方法在第一次调用WorkWithStream之前执行,否则仍会出现空引用问题。
方案3:方法参数传入ILogger实例
最符合依赖注入设计原则的方案,完全去掉静态字段的全局状态,每次调用方法时将ILogger作为参数传入,没有初始化时序问题:
// 调整方法签名,增加ILogger入参 internal static void WorkWithStream(Action<Stream> setLicence, ILogger logger, string name = null) { if (logger == null) throw new ArgumentNullException(nameof(logger)); try { // 原有业务逻辑保持不变 Assembly assembly = Assembly.GetExecutingAssembly(); var declaringType = MethodBase.GetCurrentMethod().DeclaringType; Debug.Assert(declaringType != null, "declaringType != null"); name = name ?? $"{declaringType.Namespace}.{AsposeTotalLic2017FileName}"; using (Stream stream = assembly.GetManifestResourceStream(name)) { setLicence(stream); } } catch (Exception exception) { logger.LogError(exception,"Error loading embedded resource for aspose licence"); Trace.TraceError("Error loading embedded resource for aspose licence"); throw; } }
避坑提示:不要为了快速消报错直接写
private static ILogger _logger = null!;,这种写法只是用null原谅运算符跳过了编译器的空检查,实际运行时_logger依然是null,进入异常分支时还是会抛出空引用异常,没有任何实际作用。
内容的提问来源于stack exchange,提问作者Masoud
相关产品推荐
相关产品推荐

