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

为何Autofac无法实例化含泛型接口参数的Consumer类?

问题:Autofac注入Consumer时触发空引用异常

我使用Autofac实现依赖注入,定义了泛型接口IStateMachine<TState, TTrigger>和它的实现类ConcreteStateMachine,注册代码如下:

builder.RegisterType<ConcreteStateMachine>().As<IStateMachine<MachineState, Trigger>>();

Consumer类的构造函数依赖IStateMachine<MachineState, Trigger>,但调用container.Resolve<Consumer>()时抛出空引用异常,错误信息如下:

Unhandled exception. 
Autofac.Core.DependencyResolutionException: An exception was thrown while activating Consumer.
 ---> Autofac.Core.DependencyResolutionException: An exception was thrown while invoking the constructor 'Void .ctor(IStateMachine`2[MachineState,Trigger])' on type 'Consumer'.
 ---> System.NullReferenceException: Object reference not set to an instance of an object.

测试发现直接调用container.Resolve<IStateMachine<MachineState, Trigger>>()能正常获取实例,但Autofac无法将其注入到Consumer中。完整代码如下:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Autofac;
using Stateless;
                    
public class Program
{
    public static void Main()
    {
        var builder = new ContainerBuilder();
        
        builder.RegisterType<Consumer>()
               .InstancePerLifetimeScope();
        
        builder.RegisterType<ConcreteStateMachine>()
               .As<IStateMachine<MachineState, Trigger>>()
               .InstancePerLifetimeScope();

        
        var container = builder.Build();
        
        using (var scope = container.BeginLifetimeScope())
        {
            var consumer = scope.Resolve<Consumer>();
            
            consumer.TurnOn();
            
            consumer.GetData();
            
            consumer.TurnOff();
        }
            
    }
}

public class Consumer
{
    protected IStateMachine<MachineState, Trigger> Machine;
    
    public Consumer(IStateMachine<MachineState, Trigger> machine)
    {
        Machine = machine;
        
        Machine.Register(MachineState.Off, () => Debug.WriteLine("State Off"));
        Machine.Register(MachineState.On, () => Debug.WriteLine("State On"));
    }
    
    public List<string> GetData()
    {
        if (!Machine.IsInState(MachineState.On)) throw new InvalidOperationException("Can't GetData when machine is off!");
        
        Debug.WriteLine("Getting Data");
        
        return new List<String> {"Data", "Data", "Data"};
    }
    
    public void TurnOn()
    {
        Machine.Fire(Trigger.TurnOn);
    }

    public void TurnOff()
    {
        Machine.Fire(Trigger.TurnOff);
    }
}

public class ConcreteStateMachine : IStateMachine<MachineState, Trigger>
{
    protected StateMachine<MachineState, Trigger> Machine;

    public MachineState CurrentState { get; set; }

    public void Fire(Trigger trigger)
    {
        Machine.Fire(trigger);
    }

    public async Task FireAsync(Trigger trigger)
    {
        await Machine.FireAsync(trigger);
    }

    public bool IsInState(MachineState state)
    {
        return Machine.IsInState(state);
    }

    public void Register(MachineState state, Action callback)
    {
        Machine.Configure(state)
               .OnEntry(() => callback.Invoke());
    }

    public void RegisterAsync(MachineState state, Func<Task> callback)
    {
        Machine.Configure(state)
               .OnEntryAsync(async () => await callback.Invoke());
    }

    public void Start()
    {
        ConfigureMachine();
        Machine.Activate();
    }
    
    protected void ConfigureMachine()
    {
        Machine = new StateMachine<MachineState, Trigger>(MachineState.Off);
    
        Machine.Configure(MachineState.Off)
               .Permit(Trigger.TurnOn, MachineState.On);
               
        Machine.Configure(MachineState.On)
               .Permit(Trigger.TurnOff, MachineState.Off);
    }
}

public interface IStateMachine<TState, TTrigger>
{
    TState CurrentState { get; set; }
    bool IsInState(TState state);
    void Fire(TTrigger trigger);
    Task FireAsync(TTrigger trigger);
    void Start();
    void Register(TState state, Action callback);
    void RegisterAsync(TState state, Func<Task> callback);
}

public enum MachineState
{
    Off,
    On
}

public enum Trigger
{
    TurnOff,
    TurnOn
}
问题原因

空引用异常并非Autofac注入失败导致,而是ConcreteStateMachine内部的Machine字段未初始化就被调用。

当Autofac创建ConcreteStateMachine实例后,会立即调用Consumer的构造函数,构造函数里直接调用Machine.Register()方法,但此时ConcreteStateMachine的Start()方法还未执行,ConfigureMachine()没被调用,Machine字段仍是null,因此触发空引用异常。

直接调用Resolve<IStateMachine<...>>()时,你可能在后续手动调用了Start()方法,但注入到Consumer时,构造函数提前调用了依赖实例的方法,导致问题暴露。

解决方案

有两种可行的修复方式:

方式1:在ConcreteStateMachine构造函数中初始化状态机

修改ConcreteStateMachine,在构造函数里调用Start()完成状态机初始化:

public class ConcreteStateMachine : IStateMachine<MachineState, Trigger>
{
    protected StateMachine<MachineState, Trigger> Machine;

    public MachineState CurrentState { get; set; }

    // 添加构造函数,自动初始化状态机
    public ConcreteStateMachine()
    {
        Start();
    }

    // 其余代码保持不变...
}

方式2:延迟Consumer中的状态机注册逻辑

不在Consumer构造函数中调用Machine.Register(),而是新增初始化方法,在获取实例后手动调用:

public class Consumer
{
    protected IStateMachine<MachineState, Trigger> Machine;
    
    public Consumer(IStateMachine<MachineState, Trigger> machine)
    {
        Machine = machine;
        // 移除构造函数中的Register调用
    }

    // 添加初始化方法
    public void Initialize()
    {
        Machine.Register(MachineState.Off, () => Debug.WriteLine("State Off"));
        Machine.Register(MachineState.On, () => Debug.WriteLine("State On"));
        Machine.Start(); // 确保状态机启动
    }
    
    // 其余代码保持不变...
}

同时修改Main方法中的调用逻辑:

using (var scope = container.BeginLifetimeScope())
{
    var consumer = scope.Resolve<Consumer>();
    consumer.Initialize(); // 先执行初始化
    
    consumer.TurnOn();
    consumer.GetData();
    consumer.TurnOff();
}

内容的提问来源于stack exchange,提问作者Lempface

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 17:31:01