Injection de constructeur en C # / Unity?

J’utilise C # avec le framework Unity de Microsoft. Je ne sais pas trop comment résoudre ce problème. Cela a probablement quelque chose à voir avec mon manque de compréhension de DI avec Unity.

Mon problème peut être résumé en utilisant l’exemple de code suivant:

class Train(Person p) { ... } class Bus(Person p) { ... } class Person(ssortingng name) { ... } Person dad = new Person("joe"); Person son = new Person("timmy"); 

Lorsque j’appelle la méthode de résolution sur le bus, comment puis-je être sûr que la personne ‘fils’ avec le nom ‘timmy’ est injectée et lors de la résolution de Train, comment puis-je être sûr que la personne ‘papa’ portant le nom ‘joe’ est résolue?

Je pense peut-être utiliser des instances nommées? Mais je suis perdu. Toute aide serait appréciée.

En passant, je préférerais ne pas créer d’interface IPerson.

Un moyen de résoudre ce problème consiste à utiliser un constructeur d’injection avec un enregistrement nommé.

 // Register timmy this way Person son = new Person("Timmy"); container.RegisterInstance("son", son); // OR register timmy this way container.RegisterType("son", new InjectionConstructor("Timmy")); // Either way, register bus this way. container.RegisterType(new InjectionConstructor(container.Resolve("son"))); // Repeat for Joe / Train 

Sauf si vous enregistrez respectivement “joe” et “timmy” en tant que dépendances nommées, vous ne pouvez pas être sûr que “timmy” est injecté dans Schoolbus. En fait, si vous essayez d’enregistrer deux instances de la même classe en tant que dépendances sans nom, vous aurez une configuration ambiguë et vous ne pourrez pas résoudre du tout Person .

En général, si vous devez enregistrer un grand nombre d’instances nommées, vous vous engagez probablement dans le problème de DI. L’idée principale de DI est de résoudre les services de domaine plus que les objects de domaine .

L’idée principale de DI est de fournir un mécanisme permettant de résoudre des types abstraits (interfaces ou classes abstraites) en types concrets . Votre exemple n’a pas de types abstraits, donc cela n’a pas vraiment de sens.

Mark Seeman a eu raison. Et je sympathise avec votre confusion. J’y suis allé moi-même quand j’ai appris à utiliser des conteneurs d’dependency injection automatiques. Le problème est qu’il existe de nombreux moyens valables et raisonnables de concevoir et d’utiliser des objects. Pourtant, seules certaines de ces approches fonctionnent avec des conteneurs à dependency injection automatiques.

Mon histoire personnelle: J’ai appris les principes OO de construction d’object et d’Inversion Of Control bien avant d’apprendre à utiliser les conteneurs Inversion of Control tels que les conteneurs Unity ou Castle Windsor. J’ai pris l’habitude d’écrire du code comme ceci:

 public class Foo { IService _service; int _accountNumber; public Foo(IService service, int accountNumber) { _service = service; _accountNumber = accountNumber; } public void SaveAccount() { _service.Save(_accountNumber); } } public class Program { public static void Main() { Foo foo = new Foo(new Service(),1234); foo.Save(); } } 

Dans cette conception, ma classe Foo est responsable de la sauvegarde des comptes dans la firebase database. Il faut un numéro de compte pour le faire et un service pour faire le sale boulot. Cela ressemble un peu aux classes concrètes que vous avez fournies ci-dessus, où chaque object prend des valeurs uniques dans le constructeur. Cela fonctionne bien lorsque vous instanciez les objects avec votre propre code. Vous pouvez transmettre les valeurs appropriées au bon moment.

Cependant, lorsque j’ai appris à propos des conteneurs d’dependency injection automatiques, j’ai constaté que je ne faisais plus l’instanciation manuelle de Foo. Le conteneur instancierait pour moi les arguments du constructeur. C’était très pratique pour des services comme IService. Mais cela ne fonctionne évidemment pas aussi bien pour les entiers, les chaînes et autres. Dans ces cas, il fournirait une valeur par défaut (comme zéro pour un entier). Au lieu de cela, j’avais l’habitude de transmettre des valeurs spécifiques au contexte, telles que le numéro de compte, le nom, etc … Il a donc fallu adapter mon style de codage et de conception de la manière suivante:

 public class Foo { IService _service; public Foo(IService service) { _service = service; } public void SaveAccount(int accountNumber) { _service.Save(accountNumber); } } public class Program { public static void Main() { Foo foo = new Foo(new Service()); foo.Save(1234); } } 

Il semble que les deux classes Foo sont des conceptions valides. Mais le second est utilisable avec l’injection automatique de dépendance, et le premier ne l’est pas.