Interaction with Vs Generation

Is there a difference between generic classes and dependency injection? Isn't that a way to implement inversion of control

Is a generic class not a means of implementing dependency injection with additional compile-time security benefits?

For example, if I have a class node, then I can define as the following

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

UPDATE 2 Using New

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

Update 3 @akim: I made an example that you asked to use Generics Repository using 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(); 
+6
source share
3 answers

They do not match. Generic types allow you to define functionality that can be applied to a wide range of other types. However, when you create a typical class, the compiler makes a reference to the actual types that were passed as general parameters. Thus, the declaration is static and cannot change after compilation. For example, I can write code that creates an instance of the Node class:

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

I am reusing your Node class in different scenarios, but as soon as I compile my assembly, I cannot change the general type of my variables (node1 and node2).

Dependency injection (and IoC containers), on the other hand, allows you to change the functionality of your application at runtime . You can use Dependency Injection to replace one implementation of ISomeInterface with a completely different implementation at runtime. For example, in the second Node class, I can use the IoC container to create the Node class ... something like:

 Node n = Container.Create<Node>(); 

The IoC container then determines how to instantiate the Node class based on some configuration. He determines that the designer needs an implementation of ISomeInterface , and he knows how to build an implementation at runtime . I can change my configuration for the IoC container and execute the same build / code, and another implementation of ISomeInterface will be created and passed to the Node constructor.

This is useful in unit tests because you can mock some parts of your application so that you can test one specific class. For example, you can test some business logic that usually refers to a database. In unit test, you can make fun of the data access logic and introduce new functionality that returns the β€œstatic” data needed to validate each particular business case. This breaks the dependence of your tests on the database and allows for more accurate / supported tests.

Edit

As for your update, restricting a constructor without a parameter may not always be desirable. You may have a class (written by you or a third party) that requires parameters. Requiring a class to implement a constructor without parameters can affect application integrity. The idea behind the DI template is that your Node class does not need to know how the class was created.

Suppose you had many levels of classes / dependencies. With typical types, this might look like this:

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

In this case, MyClass uses UtilityClass, and UtilityClass depends on AnotherUtilityClass. Therefore, when you declare MyClass , you should know each dependency in a row ... not only the MyClass dependency, but also the UtilityClass dependency. This expression looks something like this:

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

This can become cumbersome as you add more and more dependencies. With DI, your caller does not need to know about any nested dependencies, because the IoC container automatically detects them. You just do something like this:

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

You do not need to know anything about the details of MyClass or its utility classes.

There are usually other benefits to IoC containers, for example, many of them provide aspect-oriented programming forms. They also allow you to specify the lifetime of the object, so the object can be single (only one instance will be created, and the same instance will be returned to all callers).

+3
source

Generics introduce the concept of type parameters, which allows you to create classes and methods that defer the specification of one or more types until a class or method is declared and created using msdn code. And generic tools with all their limitations and validation are applied at compile time using static analysis.

Otherwise, Dependency Injection is a software development pattern that allows you to select a component at runtime rather than compile the wiki time. And the association of the object is bound at runtime by the assembler object and is usually not known at compile time using static wiki analysis.

Answer your question : one is applied at compile time using static analysis, the other is applied at runtime using XML or embedded code (it must also be valid for compilation). Using the solution Enabling binding dependencies will be delayed until more information or configuration is available from the context. Thus, generics and dependency injections are different and are used for different purposes.

Answer # 3 of the answer

Go one step further and point the Repository<Entity> to Controller and think about it. How are you going to implement the controller constructor:

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

or

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

And how will you ControlFreakController with tests if it depends on the Repository<Entity> (literally hard-coded)? What if Repository<Entity> does not have a default constructor and has its own dependencies and lifetime (for example, there should be one and only one HTTP request from the repository repository)? What if the next day you need an audit of working with Repository<Entity> ?

+2
source

I'm going to suggest that you mean your general class to look like this:

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

.. In this case, you simply open the generic type for dependency injection (with restriction). You have not found another β€œmethod” for dependency injection β€” it is still a dependency injection.

This would not be very useful in a "real world" scenario. You made assumptions about how the type parameter will be used solely on the basis of its injection and containment. In addition, you can ever enter only one type of object into it, which is a very bad assumption.

After updating with new (), you have even more problems. Your input type must allow for a stepless design. This limits you even more.

0
source

Source: https://habr.com/ru/post/921636/


All Articles