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();
答案 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()更新后,您会遇到更多问题。注入的类型必须允许无参数构造。这进一步限制了你。