Edw*_*uay 10 c# delegates dependency-injection
在大多数依赖注入的例子中,我看到了注入的简单对象,例如在下面的示例中,SecurityManager被注入到MainApplication中.
但是,注入委托似乎也很自然,如下面的示例中LogHandler注入到MainApplication中.
代表一般不用于依赖注入吗?他们使用的原因是什么?
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");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
回到面向对象的原则,对象的关键特征之一是它具有行为和状态。我可以想象这样一个场景:日志处理程序可能需要维护某种状态(日志文件名、数据库连接等),但也可能存在日志处理程序不需要关心状态的争论。
如果您的依赖项需要管理自己的状态,请使用适当的对象(而不是接口)。
如果您的依赖项只有行为而没有状态,那么委托可能是合适的,尽管有些人可能更愿意使用正确的对象(接口),因为稍后如果需要的话向其添加状态管理可能会更容易。
委托的一个好处是用 lambda 表达式来模拟它们非常简单:)(尽管接口也很容易模拟)
当然,现在任何委托仍然可以只是某个普通对象上的一些普通方法,并且该方法完全可以具有影响对象状态的行为,并且肯定有充分的理由这样做,但是您正在接近这样的地步:仅依赖整个对象而不是仅依赖其方法之一可能更有意义。
沿着这条路走下去,注入委托也可以成为应用接口隔离原则的一种方法,这样您就可以确保您的系统不依赖于它不使用的东西。
几乎没有充分的理由来定义自己的委托类型。大多数用例都适合Func<>和Action<>C# 类型(和事件,但这是另一个问题)。在您的情况下,您的MainApplication构造函数不应采用 aWindow1.LogHandler作为参数,而应仅采用Action<string>. 然后你只需这样调用它:
MainApplication app = new MainApplication(ConsoleLogHandler, new ToughSecurityManager());
Run Code Online (Sandbox Code Playgroud)
或类似的,因为该ConsoleLogHandler方法已经符合Action<string>签名。
在您的测试中,您只需将其实例化:
MainApplication app = new MainApplication(x => { /*Do nothing*/ }, new MySecurityManagerStub());
Run Code Online (Sandbox Code Playgroud)
甚至更好:
int timesCalled;
MainApplication app = new MainApplication(x => { timesCalled++ }, new MySecurityManagerStub());
Run Code Online (Sandbox Code Playgroud)
然后,您可以验证 MainApplication 调用该方法的次数是否与您预期的次数完全相同。
例如,我知道MEF允许注入委托。但是,您也可以创建一个 ILog 接口,该接口具有与您的委托具有相同签名的 Log 方法。我认为这样会更清楚地理解,其目的是注入能够记录日志的对象的实现,而不是单个日志函数。
| 归档时间: |
|
| 查看次数: |
4451 次 |
| 最近记录: |