C# 代理在依赖项注入中扮演什么角色?
在大多数依赖项注入示例中,我看到简单的对象被注入,例如下面的示例中SecurityManager被注入MainApplication 然而,注入委托似乎也很自然,就像下面的示例中LogHandler被注入到main应用程序中一样 委托通常不用于依赖项注入吗?使用和反对它们的理由是什么?C# 代理在依赖项注入中扮演什么角色?,c#,dependency-injection,delegates,C#,Dependency Injection,Delegates,在大多数依赖项注入示例中,我看到简单的对象被注入,例如下面的示例中SecurityManager被注入MainApplication 然而,注入委托似乎也很自然,就像下面的示例中LogHandler被注入到main应用程序中一样 委托通常不用于依赖项注入吗?使用和反对它们的理由是什么? using System; using System.Windows; using System.Windows.Controls; namespace TestSimpleDelegate82343 {
using System;
using System.Windows;
using System.Windows.Controls;
namespace TestSimpleDelegate82343
{
public partial class Window1 : Window
{
public delegate void LogHandler(string message);
public Window1()
{
InitializeComponent();
}
private void Button_Gui_Lax_Click(object sender, RoutedEventArgs e)
{
MainApplication app = new MainApplication(new LogHandler(GuiLogHandler), new LaxSecurityManager());
}
private void Button_Console_Lax_Click(object sender, RoutedEventArgs e)
{
MainApplication app = new MainApplication(new LogHandler(ConsoleLogHandler), new LaxSecurityManager());
}
private void Button_Gui_Tough_Click(object sender, RoutedEventArgs e)
{
MainApplication app = new MainApplication(new LogHandler(GuiLogHandler), new ToughSecurityManager());
}
private void Button_Console_Tough_Click(object sender, RoutedEventArgs e)
{
MainApplication app = new MainApplication(new LogHandler(ConsoleLogHandler), new ToughSecurityManager());
}
public void GuiLogHandler(string message)
{
TextBlock tb = new TextBlock();
tb.Text = "logging: " + message;
TheContent.Children.Add(tb);
}
public void ConsoleLogHandler(string message)
{
Console.WriteLine("logging: " + message);
}
}
public interface ISecurityManager
{
bool UserIsEntitled();
}
public class LaxSecurityManager : ISecurityManager
{
public bool UserIsEntitled()
{
return true;
}
}
public class ToughSecurityManager : ISecurityManager
{
public bool UserIsEntitled()
{
return false;
}
}
public class MainApplication
{
public MainApplication(Window1.LogHandler logHandler, ISecurityManager securityManager)
{
logHandler("test1");
logHandler("test2");
logHandler("test3");
if (securityManager.UserIsEntitled())
{
logHandler("secret");
}
}
}
}
我知道,比如说,允许注射代表。但是,您也可以创建一个ILog接口,该接口具有与您的委托具有相同签名的Log方法。我想更清楚的是,我的意图是注入一个能够记录日志的对象的实现,而不是一个单独的日志函数。我偶尔会将委托用作DI
然而,这种方法的一个问题是,单元测试是否在类中注入并使用了正确的依赖项变得有点困难,因为委托实例不是类型,有时,您只需要验证类是否使用了正确的策略/依赖类型。回到面向对象原则,对象的一个关键特性是它具有行为和状态。我可以设想这样一个场景:日志处理程序可能需要维护某种状态(logfilename、db连接等),但也可能有一个参数,说明日志处理程序不需要关心状态 如果依赖项需要管理自己的状态,请使用适当的对象(而不是接口) 如果您的依赖项只有行为而没有状态,那么委托可能是合适的,尽管有些人可能更愿意使用适当的对象(接口),因为如果需要的话,以后可以更容易地向其添加状态管理 委托的一个好处是,用lambda表达式模拟它们非常简单:)(尽管接口也很容易模拟) 当然,任何委托仍然可以是某个普通对象上的某个普通方法,该方法完全可以具有影响对象状态的行为,当然有充分的理由这样做,但您正在接近这样一个点,即仅依赖于整个对象可能更有意义,而不仅仅是它的一种方法 沿着这条路走下去,注入代理也是一种应用的方式,因此您可以确保您的系统不依赖于它不使用的东西 关于代表的另一个说明。。。 几乎没有一个好的理由来定义自己的委托类型。大多数用例都适合
Func
和Action
C#类型(和事件,但这是另一个问题)。
在您的情况下,main应用程序
构造函数不应将Window1.LogHandler
作为参数,而应仅将操作
作为参数。那么你就可以用以下方式来称呼它:
MainApplication app = new MainApplication(ConsoleLogHandler, new ToughSecurityManager());
或者类似,因为ConsoleLogHandler
方法已经适合操作
签名
在您的测试中,您只需将其实例化为:
MainApplication app = new MainApplication(x => { /*Do nothing*/ }, new MySecurityManagerStub());
或者更好:
int timesCalled;
MainApplication app = new MainApplication(x => { timesCalled++ }, new MySecurityManagerStub());
然后,您可以验证MainApplication调用该方法的次数是否与您预期的次数完全相同。据我所知,依赖注入是由主要使用Java的人创建的,他们没有委托/回调,在开始时,它只是一种治疗这种残疾的工具。+1回答不错,但是我想我不同意你最后一条意见,即在将委托传递给构造函数的情况下,更喜欢
操作
而不是命名委托<代码>公共MainApplication(LogHandler LogHandler)比公共MainApplication(Action LogHandler)
更清晰。它还允许您(理论上,取决于DI容器实现)将LogHandler
显式注册为依赖项,因为您不能只在DI容器中注册Action
,因为这将是不明确的(您可能有Action
的其他用途)。