Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ruby-on-rails-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 代理在依赖项注入中扮演什么角色?_C#_Dependency Injection_Delegates - Fatal编程技术网

C# 代理在依赖项注入中扮演什么角色?

C# 代理在依赖项注入中扮演什么角色?,c#,dependency-injection,delegates,C#,Dependency Injection,Delegates,在大多数依赖项注入示例中,我看到简单的对象被注入,例如下面的示例中SecurityManager被注入MainApplication 然而,注入委托似乎也很自然,就像下面的示例中LogHandler被注入到main应用程序中一样 委托通常不用于依赖项注入吗?使用和反对它们的理由是什么? using System; using System.Windows; using System.Windows.Controls; namespace TestSimpleDelegate82343 {

在大多数依赖项注入示例中,我看到简单的对象被注入,例如下面的示例中SecurityManager被注入MainApplication

然而,注入委托似乎也很自然,就像下面的示例中LogHandler被注入到main应用程序中一样

委托通常不用于依赖项注入吗?使用和反对它们的理由是什么?

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
的其他用途)。