控制反转(IOC)是一种设计原则,它将对象的创建和依赖关系的管理从对象本身转移到外部容器(IOC 容器)。在传统的编程方式中,一个对象如果需要使用另一个对象(即存在依赖关系),通常会在自身内部通过 new 关键字等方式直接创建被依赖的对象,这就导致了对象之间的紧密耦合。而在 IOC 模式下,由 IOC 容器负责创建对象,并在对象需要时将其依赖的对象注入进去。这样,对象只需要关注自身的业务逻辑,而不需要关心依赖对象的创建过程,从而实现了对象之间依赖关系的解耦。
在 WPF 应用中,这种解耦可以使视图、视图模型和其他组件之间的关系更加清晰和灵活,便于代码的维护和扩展。例如,视图模型依赖于数据访问服务来获取数据,通过 IOC,数据访问服务的具体实现可以由 IOC 容器注入到视图模型中,而不是视图模型自己去创建数据访问服务的实例。
示例 1:构造函数注入简单服务
csharp
using System;using System.Collections.Generic;using System.Windows;
// 定义IOC容器类class SimpleIocContainer{
// 使用字典存储接口和对应的创建实例的委托
private readonly Dictionary
// 注册接口和实现类的映射关系
public void Register
{
// 将创建TImplementation实例的委托存入字典,键为TInterface的Type对象
_registrations[typeof(TInterface)] = () => Activator.CreateInstance
}
// 根据接口解析出对应的实现类实例
public TInterface Resolve
{
if (_registrations.TryGetValue(typeof(TInterface), out var factory))
{
// 如果字典中存在对应的创建委托,则调用委托创建实例并返回
return (TInterface)factory();
}
throw new Exception($"Type {typeof(TInterface)} is not registered.");
}}
// 定义服务接口interface IMessageService{
string GetMessage();}
// 实现服务接口class ConsoleMessageService : IMessageService{
public string GetMessage()
{
return "Hello from ConsoleMessageService!";
}}
// 视图模型类,通过构造函数注入服务class MainViewModel{
// 保存注入的IMessageService实例
private readonly IMessageService _messageService;
// 构造函数接收IMessageService实例作为参数,实现依赖注入
public MainViewModel(IMessageService messageService)
{
_messageService = messageService;
}
// 视图模型的方法,调用注入的服务获取消息
public string GetViewModelMessage()
{
return _messageService.GetMessage();
}}
// 主窗口类public partial class MainWindow : Window{
public MainWindow()
{
InitializeComponent();
// 创建IOC容器实例
var container = new SimpleIocContainer();
// 注册IMessageService接口和ConsoleMessageService实现类的映射关系
container.Register
// 从IOC容器中解析出MainViewModel实例,此时会自动注入已注册的IMessageService实例
var viewModel = container.Resolve
// 设置窗口的数据上下文为视图模型实例
DataContext = viewModel;
}}
解析:此示例展示了最基本的构造函数注入方式。SimpleIocContainer 类实现了简单的 IOC 容器功能,通过 Register 方法注册接口和实现类的映射,Resolve 方法根据接口解析实例。IMessageService 定义了服务接口,ConsoleMessageService 是其实现类。MainViewModel 通过构造函数接收 IMessageService 实例,实现依赖注入。在主窗口中,创建 IOC 容器,注册映射关系,解析出视图模型并设置为数据上下文,从而实现了从容器中获取依赖并注入到视图模型的过程。
示例 2:属性注入服务
csharp
using System;using System.Collections.Generic;using System.Windows;
class SimpleIocContainer{
private readonly Dictionary
public void Register
{
_registrations[typeof(TInterface)] = () => Activator.CreateInstance
}
public TInterface Resolve
{
if (_registrations.TryGetValue(typeof(TInterface), out var factory))
{
return (TInterface)factory();
}
throw new Exception($"Type {typeof(TInterface)} is not registered.");
}}
interface ILogger{
void Log(string message);}
class FileLogger : ILogger{
public void Log(string message)
{
Console.WriteLine($"Logging to file: {message}");
}}
class OrderViewModel{
// 定义可读写的属性来接收注入的ILogger实例
public ILogger Logger { get; set; }
public void PlaceOrder()
{
// 在方法中使用注入的Logger实例记录日志
Logger.Log("Order placed successfully.");
}}
public partial class MainWindow : Window{
public MainWindow()
{
InitializeComponent();
var container = new SimpleIocContainer();
container.Register
var viewModel = container.Resolve
// 从IOC容器中解析出ILogger实例,并赋值给视图模型的Logger属性,实现属性注入
viewModel.Logger = container.Resolve
DataContext = viewModel;
}}
解析:该示例使用属性注入方式。OrderViewModel 定义了一个 Logger 属性用于接收 ILogger 实例。在主窗口中,创建 IOC 容器并注册 ILogger 接口和 FileLogger 实现类的映射关系。解析出 OrderViewModel 实例后,再从容器中解析出 ILogger 实例并赋值给视图模型的 Logger 属性,从而实现了属性注入。视图模型的 PlaceOrder 方法中使用注入的 Logger 实例记录日志。
示例 3:方法注入服务
csharp
using System;using System.Collections.Generic;using System.Windows;
class SimpleIocContainer{
private readonly Dictionary
public void Register
{
_registrations[typeof(TInterface)] = () => Activator.CreateInstance
}
public TInterface Resolve
{
if (_registrations.TryGetValue(typeof(TInterface), out var factory))
{
return (TInterface)factory();
}
throw new Exception($"Type {typeof(TInterface)} is not registered.");
}}
interface IEmailSender{
void SendEmail(string to, string subject, string body);}
class SmtpEmailSender : IEmailSender{
public void SendEmail(string to, string subject, string body)
{
Console.WriteLine($"Sending email to {to}: {subject} - {body}");
}}
class CustomerViewModel{
private IEmailSender _emailSender;
// 定义方法用于接收注入的IEmailSender实例
public void SetEmailSender(IEmailSender emailSender)
{
_emailSender = emailSender;
}
public void SendWelcomeEmail(string customerEmail)
{
// 在方法中使用注入的IEmailSender实例发送邮件
_emailSender.SendEmail(customerEmail, "Welcome", "Thank you for choosing us!");
}}
public partial class MainWindow : Window{
public MainWindow()
{
InitializeComponent();
var container = new SimpleIocContainer();
container.Register
var viewModel = container.Resolve
// 从IOC容器中解析出IEmailSender实例,并调用视图模型的SetEmailSender方法进行注入
viewModel.SetEmailSender(container.Resolve
DataContext = viewModel;
}}
解析:此示例采用方法注入。CustomerViewModel 定义了 SetEmailSender 方法用于接收 IEmailSender 实例。在主窗口中,创建 IOC 容器并注册 IEmailSender 接口和 SmtpEmailSender 实现类的映射关系。解析出 CustomerViewModel 实例后,从容器中解析出 IEmailSender 实例并调用视图模型的 SetEmailSender 方法,实现方法注入。视图模型的 SendWelcomeEmail 方法中使用注入的 IEmailSender 实例发送欢迎邮件。
案例4
csharp
using System;using System.Collections.Generic;using System.Windows;
class SimpleIocContainer{
private readonly Dictionary
public void Register
{
_registrations[typeof(TInterface)] = () => Activator.CreateInstance
}
public TInterface Resolve
{
if (_registrations.TryGetValue(typeof(TInterface), out var factory))
{
return (TInterface)factory();
}
throw new Exception($"Type {typeof(TInterface)} is not registered.");
}}
interface IDataAccess{
string GetData();}
class SqlDataAccess : IDataAccess{
public string GetData()
{
return "Data retrieved from SQL database.";
}}
interface IBusinessLogic{
string ProcessData();}
class BusinessLogic : IBusinessLogic{
// 保存注入的IDataAccess实例
private readonly IDataAccess _dataAccess;
// 构造函数接收IDataAccess实例作为参数,实现第一层依赖注入
public BusinessLogic(IDataAccess dataAccess)
{
_dataAccess = dataAccess;
}
// 业务逻辑方法,调用注入的IDataAccess实例获取数据并处理
public string ProcessData()
{
return $"Processed: {_dataAccess.GetData()}";
}}
class MainViewModel{
// 保存注入的IBusinessLogic实例
private readonly IBusinessLogic _businessLogic;
// 构造函数接收IBusinessLogic实例作为参数,实现第二层依赖注入
public MainViewModel(IBusinessLogic businessLogic)
{
_businessLogic = businessLogic;
}
// 视图模型的方法,调用注入的IBusinessLogic实例获取处理后的数据
public string GetViewModelData()
{
return _businessLogic.ProcessData();
}}
public partial class MainWindow : Window{
public MainWindow()
{
InitializeComponent();
var container = new SimpleIocContainer();
// 注册IDataAccess接口和SqlDataAccess实现类的映射关系
container.Register
// 注册IBusinessLogic接口和BusinessLogic实现类的映射关系
container.Register
// 从IOC容器中解析出MainViewModel实例,此时会自动按照依赖关系
// 先解析出BusinessLogic实例,而BusinessLogic实例的创建又会依赖于
// 已注册的SqlDataAccess实例,从而实现多层依赖注入
var viewModel = container.Resolve
DataContext = viewModel;
}}
解析:此示例展示了多层依赖注入的情况。BusinessLogic 类依赖于 IDataAccess 接口,通过构造函数注入 IDataAccess 实例来获取数据并进行处理。MainViewModel 类又依赖于 IBusinessLogic 接口,同样通过构造函数注入 IBusinessLogic 实例。在主窗口中,先在 IOC 容器中分别注册 IDataAccess 与 SqlDataAccess、IBusinessLogic 与 BusinessLogic 的映射关系。当解析 MainViewModel 实例时,IOC 容器会按照依赖关系,先创建 BusinessLogic 实例,而创建 BusinessLogic 实例时又会去解析并注入 SqlDataAccess 实例,从而实现了多层依赖的正确注入和使用。
示例 5:单例模式在 IOC 中的应用
csharp
using System;using System.Collections.Generic;using System.Windows;
class SimpleIocContainer{
// 存储接口和对应的创建实例的委托
private readonly Dictionary
// 存储单例对象,键为接口的Type对象,值为单例实例
private readonly Dictionary
// 注册普通的接口和实现类的映射关系
public void Register
{
_registrations[typeof(TInterface)] = () => Activator.CreateInstance
}
// 注册单例模式的接口和实现类的映射关系
public void RegisterSingleton
{
_registrations[typeof(TInterface)] = () =>
{
// 如果单例字典中不存在该接口对应的实例,则创建一个新的实例并存储
if (!_singletons.ContainsKey(typeof(TInterface)))
{
_singletons[typeof(TInterface)] = Activator.CreateInstance
}
// 返回已存储的单例实例
return _singletons[typeof(TInterface)];
};
}
// 根据接口解析出对应的实现类实例
public TInterface Resolve
{
if (_registrations.TryGetValue(typeof(TInterface), out var factory))
{
return (TInterface)factory();
}
throw new Exception($"Type {typeof(TInterface)} is not registered.");
}}
interface IConfigurationService{
string GetConfigurationValue(string key);}
class AppConfigurationService : IConfigurationService{
public string GetConfigurationValue(string key)
{
return $"Value for {key} from configuration.";
}}
class SettingsViewModel{
// 保存注入的IConfigurationService实例
private readonly IConfigurationService _configurationService;
// 构造函数接收IConfigurationService实例作为参数,实现依赖注入
public SettingsViewModel(IConfigurationService configurationService)
{
_configurationService = configurationService;
}
// 视图模型的方法,调用注入的IConfigurationService实例获取配置值
public string GetSettingValue(string key)
{
return _configurationService.GetConfigurationValue(key);
}}
public partial class MainWindow : Window{
public MainWindow()
{
InitializeComponent();
var container = new SimpleIocContainer();
// 注册IConfigurationService接口和AppConfigurationService实现类为单例模式
container.RegisterSingleton
// 从IOC容器中解析出SettingsViewModel实例,此时会注入已注册为单例的
// IConfigurationService实例,保证整个应用中该服务只有一个实例
var viewModel = container.Resolve
DataContext = viewModel;
}}
解析:该示例演示了在 IOC 容器中实现单例模式。SimpleIocContainer 类增加了一个 _singletons 字典用于存储单例对象。RegisterSingleton 方法实现了单例注册逻辑,当解析接口对应的实例时,如果单例字典中不存在该实例,则创建一个新的实例并存储,以后每次请求该接口的实例时都返回已存储的单例实例。IConfigurationService 定义了配置服务接口,AppConfigurationService 是其实现类。SettingsViewModel 通过构造函数注入 IConfigurationService 实例。在主窗口中,注册 IConfigurationService 与 AppConfigurationService 的单例映射关系,解析 SettingsViewModel 实例时会注入单例的配置服务实例,确保在整个应用中该配置服务只有一个实例,避免了重复创建和资源浪费,同时也方便在不同组件中共享相同的配置信息。