You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

静态类报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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 11:57:11