C# 中的构造函数依赖注入模式实现






4.69/5 (26投票s)
本技巧是关于 C# 中的构造函数依赖注入模式实现的。
引言
依赖注入 (DI) 是一种模式,其中对象不负责创建自己的依赖项。依赖注入是一种消除对象之间硬编码依赖关系的方法,从而更容易替换对象的依赖项,无论是用于测试(在单元测试中使用模拟对象)还是更改运行时行为。
在理解依赖注入之前,您应该熟悉面向对象编程的两个概念,一个是紧耦合,另一个是松耦合,所以我们逐一来看。
紧耦合:当一个类依赖于一个具体的依赖项时,它就被认为是与该类紧耦合的。紧耦合的对象依赖于另一个对象;这意味着在紧耦合的应用程序中更改一个对象通常需要更改许多其他对象。当应用程序很小时,这并不困难,但在企业级应用程序中,进行更改非常困难。
松耦合:这意味着两个对象是独立的,一个对象可以在不依赖于另一个对象的情况下使用它。这是一种设计目标,旨在减少系统中组件之间的相互依赖性,目标是降低一个组件中的更改要求另一个组件发生更改的风险。
现在简而言之,依赖注入是一种使对象松耦合而不是紧耦合的模式。在本技巧中,您将首先介绍紧耦合,然后使用构造函数依赖注入模式介绍松耦合。所以让我们来看看。
Using the Code
为了理解依赖注入的概念,请创建一个示例,即错误日志管理。错误日志管理示例描述了如何为应用程序创建错误日志。有两种日志管理方式,一种是使用文本文件,另一种是使用事件查看器。
首先,创建一个名为 IErrorLogger
的接口,该接口有一个用于写入错误日志的方法。其他日志类将继承此接口。
using System;
namespace DependencyInjection
{
public interface IErrorLogger
{
void LogMessage(Exception ex);
}
}
现在创建一个名为 FileLogger
的类,该类继承 IErrorLogger
接口。此类将错误日志消息写入文本文件。
using System;
using System.Configuration;
using System.IO;
namespace DependencyInjection
{
public class FileLogger : IErrorLogger
{
public void LogMessage(Exception ex)
{
string folderPath = ConfigurationManager.AppSettings["ErrorFolder"];
if (!(Directory.Exists(folderPath)))
{
Directory.CreateDirectory(folderPath);
}
FileStream objFileStrome = new FileStream(folderPath + "errlog.txt", FileMode.Append, FileAccess.Write);
StreamWriter objStreamWriter = new StreamWriter(objFileStrome);
objStreamWriter.Write("Message: " + ex.Message);
objStreamWriter.Write("StackTrace: " + ex.StackTrace);
objStreamWriter.Write("Date/Time: " + DateTime.Now.ToString());
objStreamWriter.Write("============================================");
objStreamWriter.Close();
objFileStrome.Close();
}
}
现在创建一个名为 EventViewerLogger
的类,该类继承 IErrorLogger
接口。此类将错误日志消息写入事件查看器。
using System;
using System.Configuration;
using System.Diagnostics;
namespace DependencyInjection
{
public class EventViewerLogger : IErrorLogger
{
public void LogMessage(Exception ex)
{
EventLog objEventLog = new EventLog();
string sourceName = ConfigurationManager.AppSettings["App"];
string logName = ConfigurationManager.AppSettings["LogName"];
if (!(EventLog.SourceExists(sourceName)))
{
EventLog.CreateEventSource(sourceName, logName);
}
objEventLog.Source = sourceName;
string message = String.Format("Message: {0} \n StackTrace:
{1} \n Date/Time: {2} ", ex.Message, ex.StackTrace, DateTime.Now.ToString());
objEventLog.WriteEntry(message, EventLogEntryType.Error);
}
}
}
现在我们创建另一个类来理解紧耦合的概念。Operation
类具有一个接口,一个由 FileLogger
类创建的 IErrorLogger
实例。换句话说,Operation
类的对象与 FileLogger
类紧耦合。
using System;
namespace DependencyInjection
{
public class Operation
{
IErrorLogger logger = new FileLogger();
public void Division()
{
try
{
int firstNumber = 15, secondNumber = 0, result;
result = firstNumber / secondNumber;
Console.WriteLine("Result is :{0}", result);
}
catch (DivideByZeroException ex)
{
logger.LogMessage(ex);
}
}
}
}
上面的代码有效,但它不是最佳设计,因为它与 FileLogger
紧耦合。当您想使用另一个 IErrorLogger
实现类时,您需要更改它。这不符合面向对象编程的开闭原则。
现在在应用程序的启动类中调用您的类方法,您将在文本文件中获得日志,因此您的启动类代码如下所示:
using System;
namespace DependencyInjection
{
class Program
{
static void Main(string[] args)
{
Operation objOperation = new Operation();
objOperation.Division();
Console.Read();
}
}
}
让我们运行应用程序。您将在日志文件中收到一个异常,如图 1.1 所示。
为了解决这个问题,您应该使用构造函数依赖注入,将 IErrorLogger
注入到对象中。
构造函数依赖注入模式
这是面向对象编程中最常用的依赖模式。构造函数注入使用参数来注入依赖项,因此通常只有一个带参数的构造函数。所以在这个构造函数依赖中,对象没有默认构造函数,您需要在创建时传递指定值来初始化对象。
您可以通过使用构造函数依赖注入来说明您的设计是松耦合的。
现在创建一个名为 OperationEvent
的类。该类有一个参数化构造函数。该构造函数将用于将依赖项注入对象。让我们看下面的代码。
using System;
namespace DependencyInjection
{
public class OperationEvent
{
IErrorLogger logger;
public OperationEvent(IErrorLogger logger)
{
this.logger = logger;
}
public void Division()
{
try
{
int firstNumber = 15, secondNumber = 0, result;
result = firstNumber / secondNumber;
Console.WriteLine("Result is :{0}", result);
}
catch (DivideByZeroException ex)
{
logger.LogMessage(ex);
}
}
}
}
通过上面的内容,您会注意到 OperationEvent
对象既不依赖于 FileLogger
对象,也不依赖于 EventViewerLogger
对象,因此您可以在运行时注入 FileLogger
依赖项或 EventViewerLogger
依赖项。让我们看看启动类,我们在其中使用构造函数依赖注入将 EventViewerLogger
依赖项注入到 OperationEvent
对象中。
using System;
namespace DependencyInjection
{
class Program
{
static void Main(string[] args)
{
OperationEvent objOperationEvent = new OperationEvent(new EventViewerLogger());
objOperationEvent.Division();
Console.Read();
}
}
}
让我们运行应用程序,您将获得如图 1.2 所示的结果。
结论
本文介绍了面向对象的基本概念,如紧耦合和松耦合。我希望您也能了解如何管理应用程序的错误日志,并最终了解了构造函数依赖注入模式。我希望它对您有所帮助,如果您有任何疑问或反馈,请在此处发表评论,或者您可以直接通过 https://twitter.com/ss_shekhawat 与我联系。