一、依赖注入的核心原理
控制反转(Inversion of Control, IoC)
- 核心思想:将对象的创建和依赖管理从代码内部转移到外部容器,控制权由容器接管。
- 示例:在 WPF 中,
MainViewModel
不直接创建IDataService
,而是通过容器注入。 - 实现方式:通过构造函数、属性或方法注入依赖项,容器负责解析依赖关系并实例化对象。
依赖管理机制
- 依赖图:容器根据对象间的依赖关系构建有向图,确保正确的创建顺序。
- 生命周期管理:支持单例(Singleton)、瞬态(Transient)、作用域(Scoped)等模式。
- 单例:全局唯一实例(如日志服务)。
- 瞬态:每次请求创建新实例(如临时数据处理服务)。
- 作用域:在特定上下文(如 HTTP 请求)内共享实例。
松耦合设计
- 通过接口定义服务,实现类与调用者解耦。例如,
MockDataService
实现IDataService
,当需要切换数据源时,只需替换实现类而不影响调用方。
- 通过接口定义服务,实现类与调用者解耦。例如,
二、依赖注入的核心原则
依赖倒置原则(Dependency Inversion Principle, DIP)
- 高层模块不依赖底层模块,两者均依赖抽象。
- 示例:
MainViewModel
依赖IDataService
接口,而非具体的MockDataService
类。 - 作用:提高代码灵活性,支持运行时动态替换服务实现。
单一职责原则(Single Responsibility Principle, SRP)
- 每个类仅负责单一功能,依赖由外部注入。
- 示例:
ConsoleLoggerService
专注日志记录,MockDataService
专注数据获取,通过构造函数注入协作。
构造函数注入优先
- 通过构造函数明确声明依赖,确保对象初始化时依赖已准备好。
- 反模式:避免使用属性注入(Setter Injection)或字段注入(Field Injection),因为可能导致未初始化的依赖。
显式依赖原则
- 依赖关系必须显式声明,而非隐式查找(如通过全局服务定位器)。
- 优势:提高代码可读性,便于静态分析和测试。
案例一:
MainWindow.xaml
<Window x:Class="UserApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="User App" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical" Margin="20">
<TextBox Text="{Binding NewName, UpdateSourceTrigger=PropertyChanged}" PlaceholderText="Enter user name" />
<TextBox Text="{Binding NewAge, UpdateSourceTrigger=PropertyChanged}" PlaceholderText="Enter user age" />
<Button Content="Add User" Command="{Binding AddUserCommand}" />
<ListBox ItemsSource="{Binding Users}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="5" />
<TextBlock Text=" - " Margin="5" />
<TextBlock Text="{Binding Age}" Margin="5" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.Extensions.DependencyInjection;
using System.Windows;
namespace UserApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<IUserService, UserService>();
serviceCollection.AddSingleton<UserViewModel>();
var serviceProvider = serviceCollection.BuildServiceProvider();
DataContext = serviceProvider.GetRequiredService<UserViewModel>();
}
}
}
UserService.cs
using System.Collections.Generic;
// 用户服务接口
public interface IUserService
{
List<User> GetUsers();
void AddUser(User user);
}
// 用户类
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
// 用户服务实现类
public class UserService : IUserService
{
private readonly List<User> _users = new List<User>();
public List<User> GetUsers()
{
return _users;
}
public void AddUser(User user)
{
_users.Add(user);
}
}
UserViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
// 用户视图模型
public class UserViewModel : INotifyPropertyChanged
{
private readonly IUserService _userService;
public ObservableCollection<User> Users { get; set; }
private string _newName;
private int _newAge;
public string NewName
{
get { return _newName; }
set
{
_newName = value;
OnPropertyChanged();
}
}
public int NewAge
{
get { return _newAge; }
set
{
_newAge = value;
OnPropertyChanged();
}
}
public UserViewModel(IUserService userService)
{
_userService = userService;
Users = new ObservableCollection<User>(_userService.GetUsers());
}
public void AddNewUser()
{
if (!string.IsNullOrEmpty(NewName) && NewAge > 0)
{
var newUser = new User { Name = NewName, Age = NewAge };
_userService.AddUser(newUser);
Users.Add(newUser);
NewName = string.Empty;
NewAge = 0;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
案例二
Book.cs
namespace WpfMvvmDiExample.Models
{
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
}
BookService.cs
using System.Collections.Generic;
using WpfMvvmDiExample.Models;
namespace WpfMvvmDiExample.Services
{
public class BookService : IBookService
{
private readonly List<Book> _books = new List<Book>();
public List<Book> GetBooks()
{
return _books;
}
public void AddBook(Book book)
{
_books.Add(book);
}
}
}
IBookService.cs
using System.Collections.Generic;
namespace WpfMvvmDiExample.Services
{
public interface IBookService
{
List<Book> GetBooks();
void AddBook(Book book);
}
}
BookViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using WpfMvvmDiExample.Models;
using WpfMvvmDiExample.Services;
namespace WpfMvvmDiExample.ViewModels
{
public class BookViewModel : INotifyPropertyChanged
{
private readonly IBookService _bookService;
public ObservableCollection<Book> Books { get; set; }
private string _newTitle;
private string _newAuthor;
public string NewTitle
{
get { return _newTitle; }
set
{
_newTitle = value;
OnPropertyChanged();
}
}
public string NewAuthor
{
get { return _newAuthor; }
set
{
_newAuthor = value;
OnPropertyChanged();
}
}
public BookViewModel(IBookService bookService)
{
_bookService = bookService;
Books = new ObservableCollection<Book>(_bookService.GetBooks());
}
public void AddNewBook()
{
if (!string.IsNullOrEmpty(NewTitle) && !string.IsNullOrEmpty(NewAuthor))
{
var newBook = new Book { Title = NewTitle, Author = NewAuthor };
_bookService.AddBook(newBook);
Books.Add(newBook);
NewTitle = string.Empty;
NewAuthor = string.Empty;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
App.xaml.cs
using Microsoft.Extensions.DependencyInjection;
using System.Windows;
using WpfMvvmDiExample.Services;
using WpfMvvmDiExample.ViewModels;
namespace WpfMvvmDiExample
{
public partial class App : Application
{
private readonly ServiceProvider _serviceProvider;
public App()
{
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
_serviceProvider = serviceCollection.BuildServiceProvider();
}
private void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<IBookService, BookService>();
services.AddSingleton<BookViewModel>();
services.AddTransient<MainWindow>();
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();
}
}
}
MainWindow.xaml
<Window x:Class="WpfMvvmDiExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Book Management App" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Vertical" Margin="20">
<TextBox Text="{Binding NewTitle, UpdateSourceTrigger=PropertyChanged}" PlaceholderText="Enter book title" />
<TextBox Text="{Binding NewAuthor, UpdateSourceTrigger=PropertyChanged}" PlaceholderText="Enter book author" />
<Button Content="Add Book" Click="AddBookButton_Click" />
<ListBox ItemsSource="{Binding Books}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" Margin="5" />
<TextBlock Text=" - " Margin="5" />
<TextBlock Text="{Binding Author}" Margin="5" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.Extensions.DependencyInjection;
using System.Windows;
using WpfMvvmDiExample.ViewModels;
namespace WpfMvvmDiExample
{
public partial class MainWindow : Window
{
private readonly BookViewModel _viewModel;
public MainWindow(BookViewModel viewModel)
{
InitializeComponent();
_viewModel = viewModel;
DataContext = _viewModel;
}
private void AddBookButton_Click(object sender, RoutedEventArgs e)
{
_viewModel.AddNewBook();
}
}
}