待办事项小程序(3) Autofac改造
Autofac是一个第三方的di框架(依赖注入框架),这次用它来改造这个小程序。
在这次改造时,用到Autofac下面的知识点:
依赖注入的核心实现:通过 Autofac 以「构造函数注入」的方式,
自动为 ViewModel 注入依赖服务,替代手动
new,降低代码耦合。Autofac 核心概念区分:「注册模块(Module)」负责拆分注册逻辑,
「服务组件(Service)」负责实现业务逻辑,二者职责分离。
Autofac 生命周期控制:掌握瞬时(每次解析新建实例、状态独立)和
单例(全局唯一实例、状态共享)的特性与适用场景。
Autofac 核心工作流程:注册 → 构建容器 → 解析(自动递归注入依赖),
完成对象的创建与依赖装配。

TodoServiceModule.cs
using Autofac;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp4.Common.Services;
namespace WpfApp4.Common.AutofacModules
{
/// <summary>
/// 待办服务注册模块(配合:模块化注册、生命周期配置(瞬时/单例))
/// </summary>
public class TodoServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
// 注册瞬时服务:配合验证「瞬时生命周期」,每次解析创建新实例
builder.RegisterType<TodoDataService>()
.As<ITodoDataService>()
.InstancePerDependency(); // 可省略,默认即为瞬时
// 注册单例服务:配合验证「单例生命周期」,全局只创建一个实例
builder.RegisterType<UserPreferenceService>()
.As<IUserPreferenceService>()
.SingleInstance();
}
}
}TodoViewModelModule.cs
using Autofac;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp4.ViewModel;
namespace WpfApp4.Common.AutofacModules
{
/// <summary>
/// 待办 ViewModel 注册模块(配合:模块化注册、构造函数注入自动解析)
/// </summary>
public class TodoViewModelModule : Module
{
protected override void Load(ContainerBuilder builder)
{
// 注册 ViewModel:Autofac 自动解析其构造函数中的服务依赖,配合验证构造函数注入
builder.RegisterType<TodoViewModel>()
.InstancePerDependency();
}
}
}ITodoDataService.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp4.Model;
namespace WpfApp4.Common.Services
{
/// <summary>
/// 待办事项数据操作服务接口(配合:构造函数注入、瞬时生命周期验证)
/// </summary>
public interface ITodoDataService
{
ObservableCollection<TodoItem> GetTodoItems();
void AddTodoItem(string content);
void DeleteTodoItem(TodoItem? todo);
void ClearCompletedTodoItems();
int GetUnfinishedCount();
}
}IUserPreferenceService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApp4.Common.Services
{
/// <summary>
/// 用户偏好配置服务接口(配合:构造函数注入、单例生命周期验证)
/// </summary>
public interface IUserPreferenceService
{
string GetCurrentTheme(); // 配合单例:共享主题状态
void SetCurrentTheme(string theme); // 配合单例:修改共享状态
string GetInstanceGuid(); // 配合单例:验证实例唯一性
}
}TodoDataService.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp4.Model;
namespace WpfApp4.Common.Services
{
/// <summary>
/// 待办事项数据操作服务实现(配合:瞬时生命周期验证,每个实例维护独立待办集合)
/// </summary>
public class TodoDataService : ITodoDataService
{
// 私有独立集合:每个瞬时实例都有自己的集合,配合验证瞬时生命周期的「状态独立」
private readonly ObservableCollection<TodoItem> _todoItems = new();
// 所有方法都操作当前实例的私有集合,保证实例独立性
public ObservableCollection<TodoItem> GetTodoItems() => _todoItems;
public void AddTodoItem(string content) => _todoItems.Add(new TodoItem { Content = content.Trim(), IsCompleted = false });
public void DeleteTodoItem(TodoItem? todo) { if (todo != null && _todoItems.Contains(todo)) _todoItems.Remove(todo); }
public void ClearCompletedTodoItems()
{
var completedItems = _todoItems.Where(t => t.IsCompleted).ToList();
foreach (var item in completedItems) _todoItems.Remove(item);
}
public int GetUnfinishedCount() => _todoItems.Count(t => !t.IsCompleted);
}
}UserPreferenceService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApp4.Common.Services
{
/// <summary>
/// 用户偏好配置服务实现(配合:单例生命周期验证,全局共享一个实例和状态)
/// </summary>
public class UserPreferenceService : IUserPreferenceService
{
private readonly string _instanceGuid; // 实例唯一标识:配合验证单例的「实例唯一性」
private string _currentTheme = "Light"; // 共享主题状态:配合验证单例的「状态共享性」
public UserPreferenceService()
{
// 构造时生成唯一 Guid,后续界面展示,验证所有解析请求都获取同一个实例
_instanceGuid = Guid.NewGuid().ToString("N").Substring(0, 8);
}
public string GetCurrentTheme() => _currentTheme;
public void SetCurrentTheme(string theme) { if (!string.IsNullOrWhiteSpace(theme)) _currentTheme = theme; }
public string GetInstanceGuid() => _instanceGuid;
}
}TodoItem.cs
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace WpfApp4.Model
{
// 改写后:使用 ObservableObject(框架内置)+ [ObservableProperty],消除所有手搓的 INotifyPropertyChanged 样板代码
public partial class TodoItem : ObservableObject
{
// 框架自动生成:私有字段 _content + 公共属性 Content + PropertyChanged 通知
[ObservableProperty]
private string _content = string.Empty;
// 框架自动生成:私有字段 _isCompleted + 公共属性 IsCompleted + PropertyChanged 通知
[ObservableProperty]
private bool _isCompleted;
}
//// 旧的手搓代码
//// 待办项实体,实现INotifyPropertyChanged(状态变更通知界面)
//public class TodoItem : INotifyPropertyChanged
//{
// private string _content;
// private bool _isCompleted;
// public string Content
// {
// get => _content;
// set { _content = value; OnPropertyChanged(); }
// }
// public bool IsCompleted
// {
// get => _isCompleted;
// set { _isCompleted = value; OnPropertyChanged(); }
// }
// public event PropertyChangedEventHandler? PropertyChanged;
// protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
// {
// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// }
//}
}MainWindow.xaml
<Window x:Class="WpfApp4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp4"
xmlns:vm="clr-namespace:WpfApp4.ViewModel"
mc:Ignorable="d"
Title="待办事项列表" Height="450" Width="800">
<!-- 新增xmlns:vm这一行,指向 ViewModel 命名空间 -->
<!--移除硬编码 DataContext,配合 App.xaml.cs 中的手动注入
<Window.DataContext>
<vm:TodoViewModel />
</Window.DataContext>-->
<!-- 布局代码(Grid) -->
<Grid Margin="20">
<!-- 输入区:第0行 -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<!-- 新增:配合单例生命周期验证,展示用户偏好信息 -->
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 原有输入区:配合验证构造函数注入后的基础功能 -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,10,0">
<TextBox x:Name="txtNewTodo" Width="300" Text="{Binding NewTodoText, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="添加" Command="{Binding AddTodoCommand}"/>
</StackPanel>
<!-- 原有待办列表区:配合验证构造函数注入后的基础功能 -->
<ListView Grid.Row="1" ItemsSource="{Binding TodoItems}" Margin="0,10,0,10">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!-- CheckBox只负责勾选状态,文字用独立TextBlock控制样式 -->
<CheckBox IsChecked="{Binding IsCompleted}" Width="20" VerticalAlignment="Center"/>
<!-- 待办文字:通过DataTrigger控制删除线 -->
<TextBlock Text="{Binding Content}"
Width="250"
VerticalAlignment="Center"
Margin="5,0,5,0"> <!-- 左右留5px间距 -->
<TextBlock.Style>
<Style TargetType="TextBlock">
<!-- 默认样式:无文字装饰 -->
<Setter Property="TextDecorations" Value="None"/>
<!-- 数据触发器:IsCompleted=true时显示删除线 -->
<Style.Triggers>
<DataTrigger Binding="{Binding IsCompleted}" Value="True">
<Setter Property="TextDecorations" Value="Strikethrough"/>
<!-- 可选:完成后文字变灰色,视觉更明显 -->
<Setter Property="Foreground" Value="Gray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- 删除按钮 -->
<Button Content="×"
Command="{Binding DataContext.DeleteTodoCommand, RelativeSource={RelativeSource AncestorType=ListView}}"
CommandParameter="{Binding}"
VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- 原有操作区:配合验证构造函数注入后的基础功能 -->
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,10,0,10">
<TextBlock Text="{Binding UnfinishedCount, StringFormat='待办数量:{0}'}"/>
<Button Content="清空已完成" Command="{Binding ClearCompletedCommand}"/>
</StackPanel>
<!-- 新增:用户偏好展示区(配合单例生命周期验证) -->
<StackPanel Grid.Row="3" Orientation="Horizontal" Background="#F5F5F5" Margin="0,10,0,10">
<TextBlock Text="当前主题:" VerticalAlignment="Center"/>
<TextBlock Text="{Binding CurrentTheme}" FontWeight="Bold" Margin="5,0,20,0" VerticalAlignment="Center"/>
<Button Content="切换主题" Command="{Binding ToggleThemeCommand}" Margin="0,0,20,0"/>
<TextBlock Text="偏好服务实例ID:" VerticalAlignment="Center"/>
<TextBlock Text="{Binding PreferenceServiceGuid}" Foreground="Blue" Margin="5,0,0,0" VerticalAlignment="Center"/>
</StackPanel>
</Grid>
</Window>TodoViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using WpfApp4.Common.Services;
using WpfApp4.Model;
namespace WpfApp4.ViewModel
{
public partial class TodoViewModel : ObservableObject
{
#region 配合构造函数注入:新增私有服务字段(接收 Autofac 注入的实例)
private readonly ITodoDataService _todoDataService;
private readonly IUserPreferenceService _userPreferenceService;
#endregion
#region 改造原有属性:配合构造函数注入,依赖服务获取数据
[ObservableProperty]
private string _newTodoText = string.Empty;
// 待办列表:从注入的瞬时服务中获取,配合验证瞬时生命周期的独立状态
public ObservableCollection<TodoItem> TodoItems => _todoDataService.GetTodoItems();
// 未完成数量:调用注入的瞬时服务方法,配合验证构造函数注入成功
public int UnfinishedCount => _todoDataService.GetUnfinishedCount();
#endregion
#region 新增属性:配合单例生命周期验证,展示单例服务的状态和实例ID
public string CurrentTheme => _userPreferenceService.GetCurrentTheme(); // 共享主题状态
public string PreferenceServiceGuid => _userPreferenceService.GetInstanceGuid(); // 唯一实例ID
#endregion
#region 配合构造函数注入:新增带参构造函数(Autofac 注入入口)
public TodoViewModel(ITodoDataService todoDataService, IUserPreferenceService userPreferenceService)
{
_todoDataService = todoDataService;
_userPreferenceService = userPreferenceService;
// 保留原有集合变更监听,确保绑定正常
TodoItems.CollectionChanged += (s, e) => OnPropertyChanged(nameof(UnfinishedCount));
TodoItems.CollectionChanged += (s, e) => OnPropertyChanged(nameof(CurrentTheme));
}
#endregion
#region 改造原有命令:配合构造函数注入,调用服务方法替代硬编码
partial void OnNewTodoTextChanged(string value) => AddTodoCommand.NotifyCanExecuteChanged();
[RelayCommand(CanExecute = nameof(CanAddTodo))]
private void AddTodo()
{
_todoDataService.AddTodoItem(NewTodoText.Trim()); // 调用注入服务,配合验证注入成功
NewTodoText = string.Empty;
}
private bool CanAddTodo() => !string.IsNullOrWhiteSpace(NewTodoText);
[RelayCommand]
private void DeleteTodo(TodoItem? todo) => _todoDataService.DeleteTodoItem(todo); // 调用注入服务
[RelayCommand]
private void ClearCompleted() => _todoDataService.ClearCompletedTodoItems(); // 调用注入服务
#endregion
#region 新增命令:配合单例生命周期验证,修改单例服务的共享状态
[RelayCommand]
private void ToggleTheme()
{
var currentTheme = _userPreferenceService.GetCurrentTheme();
_userPreferenceService.SetCurrentTheme(currentTheme == "Light" ? "Dark" : "Light");
// 通知界面更新,展示单例状态变更结果
OnPropertyChanged(nameof(CurrentTheme));
OnPropertyChanged(nameof(PreferenceServiceGuid));
}
#endregion
}
/*
*
// 改写后:继承 ObservableObject(框架内置,替代手搓的 ViewModelBase)
public partial class TodoViewModel : ObservableObject
{
// 改写后:使用 [ObservableProperty] 特性,编译时自动生成以下内容:
// 1. 私有字段 _newTodoText
// 2. 公共属性 NewTodoText(包含 get/set,且 set 时自动触发 PropertyChanged 通知)
// 3. 无需手动调用 OnPropertyChanged()
[ObservableProperty]
private string _newTodoText = string.Empty;
// 当 NewTodoText 变化时,通知 AddTodoCommand 刷新 CanExecute
partial void OnNewTodoTextChanged(string value)
{
AddTodoCommand.NotifyCanExecuteChanged();
}
// 待办列表(核心:ObservableCollection,保持不变,框架不替代集合本身)
public ObservableCollection<TodoItem> TodoItems { get; } = new();
// 未完成待办数量(计算属性,保持逻辑不变,仅触发通知的方式简化)
public int UnfinishedCount => TodoItems.Count(t => !t.IsCompleted);
// 改写后:使用 [RelayCommand] 特性,编译时自动生成以下内容:
// 1. 公共 ICommand 属性 AddTodoCommand
// 2. 无需手动初始化 RelayCommand,框架自动绑定对应的执行方法
// 3. 支持配套的 CanAddTodo 方法(命名规范:命令方法名 + CanExecute),自动作为 CanExecute 逻辑
[RelayCommand(CanExecute = nameof(CanAddTodo))]
private void AddTodo()
{
var newTodo = new TodoItem { Content = NewTodoText.Trim(), IsCompleted = false };
TodoItems.Add(newTodo);
NewTodoText = string.Empty; // 清空输入框(赋值时,框架自动触发 PropertyChanged)
}
// AddTodo 命令的 CanExecute 逻辑(命名规范:命令方法名 + CanExecute)
private bool CanAddTodo()
{
return !string.IsNullOrWhiteSpace(NewTodoText);
}
// 改写后:泛型命令,[RelayCommand] 自动支持参数类型,无需手动写 RelayCommand<T>
[RelayCommand]
private void DeleteTodo(TodoItem? todo)
{
if (todo != null && TodoItems.Contains(todo))
{
TodoItems.Remove(todo);
}
}
// 改写后:无参数命令,框架自动生成 ClearCompletedCommand
[RelayCommand]
private void ClearCompleted()
{
var completedItems = TodoItems.Where(t => t.IsCompleted).ToList();
foreach (var item in completedItems)
{
TodoItems.Remove(item);
}
}
public TodoViewModel()
{
// 监听 TodoItems 集合变更,更新 UnfinishedCount
// 仅需手动触发计算属性的通知,框架无额外简化(集合变更本身是 ObservableCollection 的职责)
TodoItems.CollectionChanged += (s, e) => OnPropertyChanged(nameof(UnfinishedCount));
}
}
*/
//下面是手搓代码
//public class TodoViewModel : ViewModelBase // 复用之前的ViewModelBase
//{
// // 绑定输入框的待办内容
// private string _newTodoText;
// public string NewTodoText
// {
// get => _newTodoText;
// set { _newTodoText = value; OnPropertyChanged(); }
// }
// // 待办列表(核心:ObservableCollection)
// public ObservableCollection<TodoItem> TodoItems { get; } = new();
// // 未完成待办数量(计算属性,实时更新)
// public int UnfinishedCount
// {
// get => TodoItems.Count(t => !t.IsCompleted);
// }
// // 命令:添加待办
// public ICommand AddTodoCommand { get; }
// // 命令:删除待办
// public ICommand DeleteTodoCommand { get; }
// // 命令:清空已完成
// public ICommand ClearCompletedCommand { get; }
// public TodoViewModel()
// {
// // 初始化命令
// AddTodoCommand = new RelayCommand(AddTodo, CanAddTodo);
// DeleteTodoCommand = new RelayCommand<TodoItem>(DeleteTodo);
// ClearCompletedCommand = new RelayCommand(ClearCompleted);
// // 监听TodoItems集合变更,更新UnfinishedCount
// TodoItems.CollectionChanged += (s, e) => OnPropertyChanged(nameof(UnfinishedCount));
// // 监听每个TodoItem的IsCompleted变更,更新UnfinishedCount
// // (需额外处理:新增TodoItem时订阅其PropertyChanged事件)
// }
// // AddTodoCommand的执行逻辑
// private void AddTodo(object? parameter)
// {
// var newTodo = new TodoItem { Content = NewTodoText.Trim(), IsCompleted = false };
// TodoItems.Add(newTodo);
// NewTodoText = string.Empty; // 清空输入框
// }
// // AddTodoCommand的CanExecute逻辑(输入为空时禁用)
// private bool CanAddTodo(object? parameter)
// {
// return !string.IsNullOrWhiteSpace(NewTodoText);
// }
// // DeleteTodoCommand的执行逻辑
// private void DeleteTodo(TodoItem? todo)
// {
// if (todo != null && TodoItems.Contains(todo))
// {
// TodoItems.Remove(todo);
// }
// }
// // ClearCompletedCommand的执行逻辑
// private void ClearCompleted(object? parameter)
// {
// var completedItems = TodoItems.Where(t => t.IsCompleted).ToList();
// foreach (var item in completedItems)
// {
// TodoItems.Remove(item);
// }
// }
//}
}App.xaml
<Application x:Class="WpfApp4.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApp4"> <!--StartupUri="View/MainWindow.xaml">--> <Application.Resources> </Application.Resources> </Application>
App.xaml.cs
using Autofac;
using System.Configuration;
using System.Data;
using System.Windows;
using WpfApp4.Common.AutofacModules;
using WpfApp4.ViewModel;
namespace WpfApp4
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
// 配合容器资源管理:保存全局根容器,退出时释放
private static IContainer? _rootContainer;
/// <summary>
/// 改造:重写启动方法,配合验证 Autofac 容器构建、依赖解析特性
/// </summary>
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 步骤 1:构建容器构建器,配合容器构建
var containerBuilder = new ContainerBuilder();
// 步骤 2:加载所有模块,配合验证模块化注册特性
containerBuilder.RegisterModule<TodoServiceModule>();
containerBuilder.RegisterModule<TodoViewModelModule>();
// 步骤 3:构建根容器,配合验证容器构建特性
_rootContainer = containerBuilder.Build();
// 步骤 4:开启作用域,解析 ViewModel,配合验证依赖解析和构造函数注入
using (var scope = _rootContainer.BeginLifetimeScope())
{
var todoViewModel = scope.Resolve<TodoViewModel>(); // Autofac 自动注入依赖
// 步骤 5:手动创建窗口,赋值 DataContext,替换原硬编码
var mainWindow = new MainWindow { DataContext = todoViewModel };
mainWindow.Show();
}
}
/// <summary>
/// 改造:重写退出方法,配合验证容器资源管理特性
/// </summary>
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
_rootContainer?.Dispose(); // 释放容器,避免内存泄漏
}
}
}(一)autofac依赖注入的过程解析
先明确两个核心概念(先分清「注册」和「解析」)
在 Autofac 中,注入的前提是「所有相关组件都已注册」,
注入的过程是「容器解析目标组件时,自动查找并注入其依赖」,二者是先后关系。
注册(Register):相当于给 Autofac 容器「提交产品说明书」,告诉容器:
有一个组件 TodoViewModel(产品);
有一个组件 TodoDataService,它对应 ITodoDataService 接口(产品 + 产品编号);
有一个组件 UserPreferenceService,它对应 IUserPreferenceService 接口(另一个产品 + 编号);
每个组件的生命周期是什么(生产规则)。
这里 TodoServiceModule(注册两个服务)和 TodoViewModelModule(注册 TodoViewModel),
就是在做这件事。
解析 / 注入(Resolve/Inject):相当于给 Autofac 容器「下订单」,
告诉容器:「我需要一个 TodoViewModel 实例」,此时容器会根据「产品说明书」,
自动完成所有依赖的查找和实例化,最终给你一个完整的、依赖齐全的 TodoViewModel 实例,
这个过程中,构造函数的参数会被自动填充,这就是注入。
完整注入过程拆解(对应这里的代码,一步一步来)
我们以App.xaml.cs 中的这行代码为起点,这是「下订单」的开始:
var todoViewModel = scope.Resolve<TodoViewModel>();
从这行代码开始,Autofac 容器开始工作,整个注入过程分为 5 步,
全程自动完成,无需你手动写任何赋值代码。
步骤 1:容器接收解析请求,查找 TodoViewModel 的注册信息
容器收到「需要一个 TodoViewModel 实例」的请求;
容器去自己的「注册列表」中查找,发现 TodoViewModel 已经在 TodoViewModelModule 中注册了:
// TodoViewModelModule 中的注册代码(给容器的「产品说明书」) builder.RegisterType<TodoViewModel>().InstancePerDependency();
容器获取到 TodoViewModel 的注册信息:「实例类型是 TodoViewModel,
生命周期是瞬时(每次解析新建实例)」。
步骤 2:容器反射检查 TodoViewModel 的构造函数,提取依赖项
容器通过「反射」(C# 自带的能力,可读取类的结构信息),
找到 TodoViewModel 的公开构造函数:
public TodoViewModel(ITodoDataService todoDataService, IUserPreferenceService userPreferenceService)
容器分析构造函数的参数,提取出两个「依赖项」(也就是需要注入的接口):
第一个参数:ITodoDataService(接口类型);
第二个参数:IUserPreferenceService(接口类型);
容器明白:「要创建 TodoViewModel 实例,必须先拿到这两个接口对应的具体实例,否则无法调用构造函数」。
步骤 3:容器递归解析第一个依赖项 —— ITodoDataService
容器先处理第一个依赖:「我需要一个 ITodoDataService 实例」;
容器再次去「注册列表」中查找,发现 ITodoDataService 已经在 TodoServiceModule 中注册了,
对应的实现类是 TodoDataService:
// TodoServiceModule 中的注册代码(产品说明书) builder.RegisterType<TodoDataService>().As<ITodoDataService>().InstancePerDependency();
容器根据注册信息,创建 TodoDataService 实例(因为是瞬时生命周期,新建一个实例,记为「实例 A」);
容器检查 TodoDataService 的构造函数,发现它是「无参构造函数」
(没有依赖项),无需继续递归,「实例 A」创建完成,备用。
步骤 4:容器递归解析第二个依赖项 —— IUserPreferenceService
容器处理第二个依赖:「我需要一个 IUserPreferenceService 实例」;
容器去「注册列表」中查找,发现 IUserPreferenceService 已经在 TodoServiceModule 中注册了,
对应的实现类是 UserPreferenceService:
// TodoServiceModule 中的注册代码(产品说明书) builder.RegisterType<UserPreferenceService>().As<IUserPreferenceService>().SingleInstance();
容器根据注册信息,创建 UserPreferenceService 实例(因为是单例生命周期,
这是第一次解析,新建一个实例,记为「实例 B」;后续再解析,直接复用这个实例);
容器检查 UserPreferenceService 的构造函数,发现它是「无参构造函数」,
无需继续递归,「实例 B」创建完成,备用。
步骤 5:容器调用 TodoViewModel 构造函数,完成注入,返回最终实例
容器现在有了两个备用实例:「实例 A(TodoDataService)」和「实例 B(UserPreferenceService)」;
容器通过「反射」,调用 TodoViewModel 的带参构造函数,把「实例 A」和「实例 B」作为参数传入:
// 这是 Autofac 内部自动完成的,你看不到这段代码,但它确实发生了 new TodoViewModel(实例 A, 实例 B);
TodoViewModel 实例创建成功,它的私有字段 _todoDataService 和 _userPreferenceService 被成功赋值(注入完成);
容器把这个完整的 TodoViewModel 实例返回给你,也就是 todoViewModel 变量;
你后续把 todoViewModel 赋值给 MainWindow 的 DataContext,整个流程结束。