在面向对象编程领域,依赖关系反转 (DI) 和依赖关系注入 (DI) 原则在实现可维护、可扩展和松散耦合的代码方面起着关键作用。在 C# 中,这些原则是编写可靠且可测试的应用程序的基础。让我们深入研究这些概念,了解它们的重要性,并探索 C# 中的实际示例。
依赖反转是 **Robert C. Martin(Uncle Bob)**提出的一个原则,作为 SOLID 原则的一部分。它表明高级模块不应依赖于低级模块;两者都应该依赖于抽象。抽象不应依赖于细节;细节应该取决于抽象。
简单来说,这个原则强调:
考虑一个类直接依赖于类的场景:BusinessLogicDataAccess
public class BusinessLogic
{
private readonly DataAccess _dataAccess;
public BusinessLogic()
{
_dataAccess = new DataAccess();
}
// Methods using \_dataAccess
}
这种紧密耦合使类依赖于类,因此很难替换或测试 .BusinessLogicDataAccessDataAccess
通过遵循依赖反转原则,我们可以重构以下代码:
public interface IDataAccess
{
// Method signatures
}
public class DataAccess : IDataAccess
{
// Implement methods
}
public class BusinessLogic
{
private readonly IDataAccess _dataAccess;
public BusinessLogic(IDataAccess dataAccess)
{
_dataAccess = dataAccess;
}
// Methods using \_dataAccess
}
现在,依赖于接口而不是特定的实现()。这种抽象提供了灵活性,支持注入不同的实现,便于测试,并在数据访问策略之间切换。BusinessLogicIDataAccessDataAccessIDataAccess
依赖注入是一种实现依赖反转原则的设计模式。这是一种技术,其中对象从外部源接收其依赖项,而不是在内部创建它们。可以通过构造函数、属性或方法提供此外部源。
在 C# 中,可以通过多种方式实现依赖注入:
让我们检查每种类型的简单示例。
让我们看一下构造函数注入的简单代码示例:
public class BusinessLogic
{
private readonly IDataAccess _dataAccess;
public BusinessLogic(IDataAccess dataAccess)
{
_dataAccess = dataAccess;
}
// Methods using _dataAccess
}
在这里,依赖项 () 是通过构造函数注入的。IDataAccess
让我们看一下属性注入的简单代码示例:
public class BusinessLogic
{
public IDataAccess DataAccess { get; set; }
// Other methods using DataAccess
}
在这种情况下,依赖项是通过公共属性注入的,这可能会导致可选依赖项。
让我们看一下方法注入的简单代码示例:
public class BusinessLogic
{
public void PerformOperation(IDataAccess dataAccess)
{
// Use dataAccess for operation
}
}
依赖项作为方法的参数提供,适用于短期依赖项或一次性操作。
像 ASP.NET Core 这样的框架提供了对依赖注入的内置支持,允许您通过构造函数注入自动注册服务并注入依赖关系。
您可以像下面的代码一样在 program.cs 中注册您的服务**。** 大多数情况下,我们在 program.cs 中注册 DI 服务。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IDataAccess, DataAccess>();
// Other service registrations
在这里,我们使用 DI 的瞬态生存期注册了服务。之后,您可以通过构造函数注入来注入您的服务。
public class BusinessLogic
{
private readonly IDataAccess _dataAccess;
public BusinessLogic(IDataAccess dataAccess)
{
_dataAccess = dataAccess;
}
// Methods using _dataAccess
}
framework(ASP.NET Core) 在创建类实例时解析并注入指定的依赖项。
在 C# 中的依赖项注入 (DI) 的上下文中,DI 容器管理的依赖项的生存期或范围通常是指特定依赖项的实例保持活动状态的时间。通常使用三种常见的生存期或作用域:
builder.Services.AddTransient<IDataAccess, DataAccess>();
2. 单例:单例生存期是指容器在应用程序的整个生命周期内维护依赖项的单个实例。每当请求依赖项时,都会重用此实例,从而确保应用程序的所有部分共享同一实例。
builder.Services.AddSingleton<IDataAccess, DataAccess>();
3. 作用域:作用域生存期意味着每个作用域或每个请求创建一次依赖项的单个实例。对于 Web 应用程序,此范围通常与 HTTP 请求保持一致。在同一个 HTTP 请求中,会重用相同的实例,但后续请求会接收自己的实例。
builder.Services.AddScoped<IDataAccess, DataAccess>();
这些不同的生存期迎合了应用程序开发中的各种方案。开发人员应根据其应用程序中组件所需的特定要求和行为来选择适当的生存期。了解这些生存期有助于有效地管理内存,并确保依赖项在整个生命周期中的预期行为。
依赖关系反转和依赖关系注入是现代软件开发中的关键概念,尤其是在 C# 和其他面向对象的语言中。这些原则通过解耦依赖关系和实现组件实现的灵活性来促进代码的可维护、可测试和可伸缩。
通过采用这些原则,开发人员可以创建模块化、可重用且易于维护的软件系统。Mastering Dependency Injection 使开发人员能够编写更简洁、更易于维护的代码库,这些代码库更易于扩展和测试,最终导致更强大、更灵活的应用程序。