Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/334.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_Inversion Of Control - Fatal编程技术网

C# 控制反转对我有什么帮助?

C# 控制反转对我有什么帮助?,c#,dependency-injection,inversion-of-control,C#,Dependency Injection,Inversion Of Control,我试图理解控制反转,以及它如何帮助我进行单元测试。我在网上读过几篇关于国际奥委会及其作用的解释,但我只是不太理解 我开发了一个示例项目,其中包括使用StructureMap进行单元测试。StructureMap设置代码如下所示: private readonly IAccountRepository _accountRepository public Logon() { _accountRepository = ObjectFactory.GetInstance<IAccount

我试图理解控制反转,以及它如何帮助我进行单元测试。我在网上读过几篇关于国际奥委会及其作用的解释,但我只是不太理解

我开发了一个示例项目,其中包括使用StructureMap进行单元测试。StructureMap设置代码如下所示:

private readonly IAccountRepository _accountRepository

public Logon()
{
    _accountRepository = ObjectFactory.GetInstance<IAccountRepository>();
}
AccountRepository _accountRepository = new AccountRepository();
public class MyClass
{
   public MyEntity GetEntityBy(long id)
   {
      AccountRepository _accountRepository = new AccountRepository();

      return _accountRepository.GetEntityFromDatabaseBy(id);
   }
}
public interface IAccountRepository
{
   AccountEntity GetAccountFromDatabase(long id);
}

public class AccountRepository : IAccountRepository
{
   public AccountEntity GetAccountFromDatabase(long id)
   {
      //... some DB implementation here
   }
}

public class MyClass
{
   private readonly IAccountRepository _accountRepository;

   public MyClass(IAccountRepository accountRepository)
   {
      _accountRepository = accountRepository;
   }

   public AccountEntity GetAccountEntityBy(long id)
   {
      return _accountRepository.GetAccountFromDatabase(id)
   }
}
它将执行与先前代码相同的操作。所以,我只是想知道是否有人能以一种简单的方式向我解释一下,使用IOC的好处是什么(特别是在处理单元测试时)


谢谢

这背后的想法是使您能够将默认帐户存储库实现替换为更易于单元测试的版本。在单元测试中,您现在可以实例化一个不进行数据库调用,而是返回固定数据的版本。通过这种方式,您可以专注于测试方法中的逻辑,并摆脱对数据库的依赖

这在很多方面都更好: 1) 您的测试更加稳定,因为您不再需要担心由于数据库中的数据更改而导致测试失败 2) 您的测试将运行得更快,因为您不需要调用外部数据源 3) 您可以更轻松地模拟所有测试条件,因为模拟存储库可以返回测试任何条件所需的任何类型的数据

是让框架回调到用户代码中的概念。这是一个非常抽象的概念,它简单地描述了库和框架之间的区别,或者可以将其描述为“框架的定义特征”。我们的代码调用库,而框架控制我们的代码。任何框架都提供挂钩,允许我们插入代码

控制反转是一种模式,只有当您在构建框架时是框架开发人员,或者当您是与框架代码交互的应用程序开发人员时,才能应用这种模式。但当专门处理应用程序代码时,IoC不适用

依赖抽象而不是实现的行为称为依赖性反转,应用程序和框架开发人员都可以进行依赖性反转。所以你们所说的IoC实际上是依赖倒置,正如Krzysztof所评论的:你们所做的不是IoC。从现在开始,我将讨论依赖项反转

依赖倒置基本上有两种形式/实现:服务定位器和依赖注入

使用服务定位器模式,可以从需要依赖项的类中调用静态工厂。一般来说,它看起来是这样的:

public class Service
{
    public void SomeOperation() {
        IDependency dependency = 
            ServiceLocator.GetInstance<IDependency>();
        dependency.Execute();
    }
}
{ 依赖性=依赖性; }

    public void SomeOperation()
{ 此参数为.dependency.Execute(); } }

    public void SomeOperation()
这两种模式都是依赖项倒置,因为在这两种情况下,
服务
类不负责创建依赖项,也不知道它使用的是哪个实现。它只是与接口对话。这两种模式都为您提供了相对于类正在使用的实现的灵活性,从而允许您编写更灵活的软件

然而,服务定位器模式有很多问题,这就是为什么它被认为是反模式。您已经遇到了这些问题,因为您想知道依赖倒置(您的案例中的服务定位器)如何帮助您进行单元测试

答案是服务定位器模式对单元测试没有帮助。相反,这使得单元测试非常困难。通过让类调用
ObjectFactory
,可以在两者之间创建硬依赖关系。替换用于测试的
IAccountRepository
,还意味着您的单元测试必须使用
ObjectFactory
。这使得单元测试更难阅读。但更重要的是,由于
ObjectFactory
是一个静态实例,所有单元测试都使用同一个实例,因此很难单独运行测试,也很难在每个测试的基础上交换实现

我过去使用过服务定位器模式,我处理这个问题的方法是在我的服务定位器中注册依赖项,我可以逐个线程地更改这些依赖项(在封面下使用[ThreadStatic]字段)。这使我能够并行运行测试(MSTest在默认情况下所做的),同时保持测试的隔离。然而,问题是,这个问题变得非常复杂,测试中充斥着各种技术性的东西,这让我花了很多时间解决这些技术问题,而我本可以编写更多的测试

这些问题的真正解决方案是依赖注入。一旦您通过类的构造函数注入类所需的依赖项,所有这些问题就会消失。这不仅使类需要什么依赖项(没有隐藏的依赖项)变得非常清楚,而且每个单元测试本身都负责注入它需要的依赖项。这使得编写测试变得更加容易,并且避免了在单元测试中配置DI容器


进一步阅读:。

回答您的问题的关键是可测试性,以及您是否希望管理注入对象的生存期,或者您是否打算让IoC容器为您做这件事

例如,假设您正在编写一个使用存储库的类,并希望对其进行测试

如果您执行以下操作:

private readonly IAccountRepository _accountRepository

public Logon()
{
    _accountRepository = ObjectFactory.GetInstance<IAccountRepository>();
}
AccountRepository _accountRepository = new AccountRepository();
public class MyClass
{
   public MyEntity GetEntityBy(long id)
   {
      AccountRepository _accountRepository = new AccountRepository();

      return _accountRepository.GetEntityFromDatabaseBy(id);
   }
}
public interface IAccountRepository
{
   AccountEntity GetAccountFromDatabase(long id);
}

public class AccountRepository : IAccountRepository
{
   public AccountEntity GetAccountFromDatabase(long id)
   {
      //... some DB implementation here
   }
}

public class MyClass
{
   private readonly IAccountRepository _accountRepository;

   public MyClass(IAccountRepository accountRepository)
   {
      _accountRepository = accountRepository;
   }

   public AccountEntity GetAccountEntityBy(long id)
   {
      return _accountRepository.GetAccountFromDatabase(id)
   }
}
当您尝试测试此方法时,您会发现有很多复杂因素: 1.必须已经设置了数据库。 2.您的数据库需要具有包含您要查找的实体的表。 3.您用于测试的id必须存在,如果出于任何原因删除它,那么您的自动测试现在将被破坏

如果您有以下内容:

private readonly IAccountRepository _accountRepository

public Logon()
{
    _accountRepository = ObjectFactory.GetInstance<IAccountRepository>();
}
AccountRepository _accountRepository = new AccountRepository();
public class MyClass
{
   public MyEntity GetEntityBy(long id)
   {
      AccountRepository _accountRepository = new AccountRepository();

      return _accountRepository.GetEntityFromDatabaseBy(id);
   }
}
public interface IAccountRepository
{
   AccountEntity GetAccountFromDatabase(long id);
}

public class AccountRepository : IAccountRepository
{
   public AccountEntity GetAccountFromDatabase(long id)
   {
      //... some DB implementation here
   }
}

public class MyClass
{
   private readonly IAccountRepository _accountRepository;

   public MyClass(IAccountRepository accountRepository)
   {
      _accountRepository = accountRepository;
   }

   public AccountEntity GetAccountEntityBy(long id)
   {
      return _accountRepository.GetAccountFromDatabase(id)
   }
}
现在,您已经知道了,您可以单独测试MyClass类,而不需要数据库