控制反转和依赖性注入都使您能够打破应用程序中组件之间的依赖性,并使应用程序更易于测试和维护。 但是,控制反转和依赖注入并不相同-两者之间存在细微差异。
在本文中,我们将检查控制模式的反转,并通过C#中的相关代码示例了解其与依赖注入的区别。
若要使用本文提供的代码示例,您应该在系统中安装Visual Studio 2019。 如果您还没有副本,则可以在此处下载Visual Studio 2019 。
在Visual Studio中创建控制台应用程序项目
首先,让我们在Visual Studio中创建一个.NET Core控制台应用程序项目。 假设系统中已安装Visual Studio 2019,请按照以下概述的步骤在Visual Studio中创建新的.NET Core控制台应用程序项目。
- 启动Visual Studio IDE。
- 点击“创建新项目”。
- 在“创建新项目”窗口中,从显示的模板列表中选择“控制台应用程序(.NET Core)”。
- 点击下一步。
- 在接下来显示的“配置新项目”窗口中,指定新项目的名称和位置。
- 单击创建。
这将在Visual Studio 2019中创建一个新的.NET Core控制台应用程序项目。在本文的后续部分中,我们将使用该项目来探索控件的反转。
什么是控制反转?
控制反转(IoC)是一种程序模式的控制流程反转的设计模式。 您可以利用控制模式的反转来解耦应用程序的组件,交换依赖关系实现,模拟依赖关系,并使应用程序模块化和可测试。
依赖注入是控制原理反转的子集。 换句话说,依赖注入只是实现控制反转的一种方式。 例如,您还可以使用事件,委托,模板模式,工厂方法或服务定位器来实现控制反转。
控件设计模式的反转表明,对象不应创建其依赖于执行某些活动的对象。 相反,他们应该从外部服务或容器中获取那些对象。 这个想法类似于好莱坞的原则,即“不要打电话给我们,我们会打电话给您。” 作为示例,代替应用程序调用框架中的方法,框架将调用应用程序已提供的实现。
C#中的控制示例反转
假设您正在构建订单处理应用程序,并且想要实现日志记录。 为了简单起见,让我们假设日志目标是一个文本文件。 在“解决方案资源管理器”窗口中选择刚创建的控制台应用程序项目,然后创建两个文件,分别名为ProductService.cs和FileLogger.cs。
public class ProductService
{
private readonly FileLogger _fileLogger = new FileLogger();
public void Log(string message)
{
_fileLogger.Log(message);
}
}
public class FileLogger
{
public void Log(string message)
{
Console.WriteLine("Inside Log method of FileLogger.");
LogToFile(message);
}
private void LogToFile(string message)
{
Console.WriteLine("Method: LogToFile, Text: {0}", message);
}
}
前面的代码片段中所示的实现是正确的,但是有一个限制。 您只能将数据记录到文本文件中。 您不能以任何方式将数据记录到其他数据源或不同的日志目标。
不灵活的日志记录实现
如果您想将数据记录到数据库表中怎么办? 现有的实现不支持此功能,您将不得不更改实现。 您可以更改FileLogger类的实现,也可以创建一个新类,例如DatabaseLogger。
public class DatabaseLogger
{
public void Log(string message)
{
Console.WriteLine("Inside Log method of DatabaseLogger.");
LogToDatabase(message);
}
private void LogToDatabase(string message)
{
Console.WriteLine("Method: LogToDatabase, Text: {0}", message);
}
}
您甚至可以在ProductService类内创建DatabaseLogger类的实例,如下面的代码片段所示。
public class ProductService
{
private readonly FileLogger _fileLogger = new FileLogger();
private readonly DatabaseLogger _databaseLogger =
new DatabaseLogger();
public void LogToFile(string message)
{
_fileLogger.Log(message);
}
public void LogToDatabase(string message)
{
_fileLogger.Log(message);
}
}
但是,尽管这可行,但是如果您需要将应用程序的数据记录到EventLog上怎么办? 您的设计不够灵活,每次需要登录到新的日志目标时,都将不得不更改ProductService类。 这不仅麻烦,而且随着时间的推移,使您很难管理ProductService类。
通过界面增加灵活性
解决此问题的方法是使用具体记录器类将实现的接口。 以下代码片段显示了一个称为ILogger的接口。 该接口将由两个具体的类FileLogger和DatabaseLogger实现。
public interface ILogger
{
void Log(string message);
}
下面给出了FileLogger和DatabaseLogger类的更新版本。
public class FileLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine("Inside Log method of FileLogger.");
LogToFile(message);
}
private void LogToFile(string message)
{
Console.WriteLine("Method: LogToFile, Text: {0}", message);
}
}
public class DatabaseLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine("Inside Log method of DatabaseLogger.");
LogToDatabase(message);
}
private void LogToDatabase(string message)
{
Console.WriteLine("Method: LogToDatabase, Text: {0}", message);
}
}
现在,您可以在必要时使用或更改ILogger接口的具体实现。 以下代码段显示了带有Log方法实现的ProductService类。
public class ProductService
{
public void Log(string message)
{
ILogger logger = new FileLogger();
logger.Log(message);
}
}
到目前为止,一切都很好。 但是,如果您想在ProductService类的Log方法中使用DatabaseLogger代替FileLogger,该怎么办? 您可以更改ProductService类中Log方法的实现来满足要求,但这不能使设计灵活。 现在,通过使用控件反转和依赖注入,使设计更加灵活。
使用依赖注入反转控件
以下代码段说明了如何利用依赖项注入来使用构造函数注入传递具体记录器类的实例。
public class ProductService
{
private readonly ILogger _logger;
public ProductService(ILogger logger)
{
_logger = logger;
}
public void Log(string message)
{
_logger.Log(message);
}
}
最后,让我们看看如何将ILogger接口的实现传递给ProductService类。 以下代码段显示了如何创建FileLogger类的实例以及如何使用构造函数注入传递依赖关系。
static void Main(string[] args)
{
ILogger logger = new FileLogger();
ProductService productService = new ProductService(logger);
productService.Log("Hello World!");
}
这样做,我们已经反转了控件。 ProductService类不再负责创建ILogger接口的实现的实例,甚至不再决定应使用ILogger接口的哪种实现。
控制反转和依赖项注入可帮助您实现对象的自动实例化和生命周期管理。 ASP.NET Core包含一个简单的内置控件容器反转,具有有限的功能集。 如果您的需求很简单,则可以使用此内置IoC容器;如果要利用其他功能,则可以使用第三方容器。
您可以在我之前的文章中阅读有关如何在ASP.NET Core中进行控制反转和依赖注入的更多信息 。
From: https://www.infoworld.com/article/3527190/how-to-use-inversion-of-control-in-c.html