Generic Vs依赖注入

时间:2012-07-30 04:13:24

标签: c# design-patterns generics dependency-injection

Generic Classes和Dependency注入之间有什么区别吗? 它们不是实现控制反转的方法

泛型类不是一种实现依赖注入的方法,还有编译时安全性的附加好处吗?

例如,如果我有一个节点类,那么我可以定义如下

class Node<T> where T : ISomeInterface
{
  ..
  ..
}

class Node
{
   ISomeInterface obj
   public Node(ISomeInterface inject)
   {
       obj = inject;
   }
}

更新2 随着新

class Node<T> where T : ISomeInterface, new()
{
  ISomeInterface obj
  public Node()
  {
     obj = new T();
  }
}

更新3 @akim:我举了一个例子,你要求使用Generics 使用泛型的存储库

Interface IRepository 
{   
      public DataTable GetAll();
}

public class ProductRep : IRepository
{ 
       public DataTable GetAll()
       {
            //implementation
       }
}

public class MockProductRep : IRepository
{ 
       public DataTable GetAll()
       {
            //mock implementation
       }
}

public class Product<T> where T : IRepository, new()
{
    IRepository repository = null
     public Product()
     {
       repository = new T();
     }
    public List<Product> GetProduct()
    {
      DataTable prodlst =  repository.GetAll();
      //convert to List of products now
    }
}


//so while using the Product class, client would Supply ProductRep class and in NUnit you //would supply MockProductRep class

Product<ProductRep> obj = new ProductRep<ProductRep>();
List<Product> lst = obj.GetProduct();

//in NUnit
Product<MockProductRep> obj = new ProductRep<MockProductRep>();
List<Product> lst = obj.GetProduct();

3 个答案:

答案 0 :(得分:3)

他们不一样。通用类型允许您定义可应用于各种其他类型的功能。但是,在实例化泛型类时,编译器会引用作为泛型参数传递的实际类型。因此声明是静态的,编译后无法更改。例如,我可以编写实例化Node类的代码:

Node<SomeImplementation> node1 = new Node<SomeImplementation>();
Node<SomeOtherImplementation> node2 = new Node<SomeOtherImplementation>();

我在不同的场景中重用你的Node类,但是一旦我编译了程序集,我就无法改变我的变量的泛型类型(node1和node2)。

另一方面,

依赖注入(和IoC容器)允许您在运行时更改应用程序的功能。您可以使用依赖注入将ISomeInterface的一个实现交换为在运行时完全不同的实现。例如,在第二个节点类中,我可以使用IoC容器来创建Node类......类似于:

Node n = Container.Create<Node>();
然后,IoC容器根据某些配置计算出如何实例化Node类。它确定构造函数需要ISomeInterface的实现,并且它知道如何在运行时构建实现。我可以更改IoC容器的配置并执行相同的程序集/代码,并创建ISomeInterface的不同实现并将其传递给Node的构造函数。

这在单元测试中非常有用,因为您可以模拟应用程序的某些部分,以便可以测试一个特定的类。例如,您可能希望测试一些通常访问数据库的业务逻辑。在单元测试中,您可以模拟数据访问逻辑并注入新功能,以返回测试每个特定业务案例所需的“静态”数据。这会破坏您对数据库的测试依赖性,并允许更准确/可维护的测试。

修改

关于更新,可能并不总是需要无参数构造函数限制。您可能有一个需要参数的类(由您或第三方编写)。要求类实现无参数构造函数可能会影响应用程序的完整性。 DI模式背后的想法是你的Node类不需要知道如何实际创建类。

假设您有多层类/依赖项。对于泛型类型,它可能如下所示:

class MyClass<T>
    where T : IUtilityClass
{
    ...
}

class UtilityClass<T> : IUtilityClass
    where T : IAnotherUtilityClass
{
    ...
}

class AnotherUtilityClass : IAnotherUtilityClass
{
    ...
}

在这种情况下,MyClass使用UtilityClass,而UtilityClass依赖于AnotherUtilityClass。因此,当您声明MyClass时,您必须知道所有依赖关系...不仅仅是MyClass的依赖关系,还包括UtilityClass的依赖关系。这个声明看起来像这样:

MyClass<UtilityClass<AnotherUtilityClass>> myTestClass =
    new MyClass<UtilityClass<AnotherUtilityClass>>();

随着您添加越来越多的依赖项,这会变得很麻烦。使用DI,您的调用者无需了解任何嵌套依赖项,因为IoC容器会自动将其计算出来。你只需要这样做:

MyClass myTestClass = Container.Create<MyClass>();

无需了解MyClass或其实用程序类的详细信息。

IoC容器通常还有其他好处,例如它们中的许多都提供了面向方面编程的形式。它们还允许您指定对象的生命周期,因此对象可以是单个对象(将只创建一个实例,并将相同的实例返回给所有调用者)。

答案 1 :(得分:2)

泛型引入了类型参数的概念,这使得可以设计推迟一个或多个类型的规范的类和方法,直到声明类或方法为止并由代码msdn实例化。并且使用静态分析在编译时应用所有限制和检查的泛型

另一方面,依赖注入是一种软件设计模式,允许在运行时选择组件而不是编译时 wiki 。并且对象耦合在运行时由汇编程序对象绑定,并且通常在编译时使用静态分析 wiki

回答您的问题:一个在编译时使用静态分析应用,另一个在运行时使用XML或代码内配置应用(它也应该对编译有效)。使用依赖注入关于绑定的决定将被推迟,直到从上下文中获得更多信息或配置。因此,泛型和依赖注入是不同的,并且用于不同的目的。

示例#3回答

让我们更进一步向Repository<Entity>提供Controller并考虑其使用情况。你打算如何实现controler的构造函数:

public ControlFreakController<Repository<Entity>>()
{
    this.repository = new Repository<Entity>(); // here is a logical problem
}

public ControllerWithInjection(IRepository repository)
{
   this.repository = repository;
}

如果它取决于ControlFreakController(字面上是硬编码的),你将如何用Repository<Entity>进行测试?如果Repository<Entity>没有默认构造函数,并且有自己的依赖关系和生命周期(例如,应该只有一个存储库代表HTTP请求),该怎么办?如果第二天需要使用Repository<Entity>审核工作怎么办?

答案 2 :(得分:0)

我会假设你的通用类看起来像这样:

class Node<T> where T : ISomeInterface {
    T obj;

    public Node(T inject) {
        obj = inject;
    }
}

..在这种情况下,你只是打开一个通用类型的依赖注入(带约束)。您还没有发现依赖注入的不同“方法” - 它仍然是依赖注入。

这在“真实世界”场景中不会非常有用。你已经假设类型参数如何纯粹基于注入和限制它来使用。此外,你只能将1种单一类型的对象注入其中,这是一个非常糟糕的假设。

使用new()更新后,您会遇到更多问题。注入的类型必须允许无参数构造。这进一步限制了你。