2

J'ai travaillé sur la création de mon propre conteneur IoC à des fins d'apprentissage. Après avoir posé quelques questions à leur sujet, on m'a montré que créer une usine pour "résoudre" les objets était la meilleure solution (see third solution here). L'utilisateur Krzysztof Koźmic a montré que Castle Windsor peut réellement mettre en œuvre ceci pour vous. J'ai lu la source de CW toute la matinée. Je sais que lorsque Resolve est appelé, il "renvoie l'interface". Comment cette interface intercepte-t-elle les appels (puisqu'il n'y a pas d'implémentation derrière) et appelle-t-elle ses propres méthodes?Implémenter l'interface sans créer d'implémentation (proxies dynamiques?)

Je sais qu'il y a de la tromperie ici et c'est assez incroyable. Je ne suis pas du tout l'utilisateur comment l'interception est faite. J'ai essayé de m'aventurer dans le trou du lapin moi-même, mais je me suis perdu. Si quelqu'un pouvait me pointer dans la bonne direction, ce serait très apprécié.

Egalement - La création d'une fabrique dactylographiée ne dépendrait-elle pas du conteneur dans le code appelant? En termes ASP.NET MVC, c'est ce qu'il me semble.

EDIT: Trouvé Reflection.Emit ... cela pourrait-il être utile?

EDIT2: Plus je m'intéresse à cela, plus il semble compliqué de créer automatiquement des usines. Je pourrais finir par coller avec le code répétitif.

Répondre

7

Il y a deux concepts distincts ici:

  1. injection de dépendance instancie simplement une classe existante qui implémente l'interface. Par exemple, vous pourriez avoir une classe MyServices qui implémente IMyServices. Les frameworks IoC vous permettent de spécifier que lorsque vous demandez un IMyServices, il se résout en une instance de MyServices. Il peut y avoir une certaine magie IL Emit pour configurer les méthodes d'usine ou d'assistance, mais les instances réelles sont simplement des classes que vous avez définies.

  2. MockingMocking permet d'instancier une classe qui implémente une interface, sans devoir coder cette classe. Cela fait généralement usage de Reflection et IL Emit, comme vous le pensiez. Typiquement, le code IL émis est assez simple, en déléguant la plus grande partie du travail aux méthodes écrites en C#. La plus grande partie de la complexité du moqueur est liée à la spécification du comportement de la méthode elle-même, car les frameworks vous permettent souvent de spécifier un comportement avec une syntaxe fluide. Certains, comme Moles, vous permettent simplement de spécifier un délégué pour implémenter la méthode, bien que les Moles puissent faire d'autres choses plus folles, comme rediriger les appels vers des méthodes statiques.


Pour élaborer un peu plus loin, vous n'avez pas réellement besoin d'utiliser IL pour implémenter des fonctionnalités IoC, mais cela est souvent précieux pour éviter la surcharge des appels de réflexion répétées, car la réflexion est relativement cher. Here est quelques informations sur ce que Castle Windsor est en train de faire.


Pour répondre à votre question, l'endroit le plus utile de commencer trouvé était la classe OpCodes. Ceci est un bon résumé des fonctionnalités disponibles dans IL et comment fonctionnent les OpCodes. Il s'agit essentiellement d'un langage d'assemblage basé sur une pile (pas de registres à se soucier), mais fortement typé et avec un accès de première classe aux symboles et concepts d'objets, tels que les types, les champs et les méthodes. Here est un bon article de Code Project présentant les bases de l'IL.Si cela vous intéresse, je peux également vous envoyer des cours d'aide que j'ai créés au cours des dernières années pour mon propre code Emit.

+0

Merci pour le commentaire. J'essaie d'utiliser * "moqueur" pour mon conteneur IoC que j'essaie de faire. Castle Windsor utilise une sorte de technique pour créer une «usine typée» qui implémente l'interface spécifiée. Si vous lisez le lien que j'ai posté de Krzysztof, vous verrez ce que je veux dire. Je ne veux pas utiliser une bibliothèque tierce car c'est tout pour mon propre apprentissage. Auriez-vous des ressources sur la génération de classe? EDIT: Je sais que je n'ai pas besoin d'utiliser IL, je suis juste curieux de savoir comment c'est fait. Merci! :) – TheCloudlessSky

+0

Merci pour votre aide. Je vais commencer à lire. Je ne pourrais pas implémenter cette fonctionnalité parce qu'elle semble si extrême. Mais, je vais y jeter un coup d'œil parce que c'est très intéressant. Merci! – TheCloudlessSky

+0

@TheCloudlessSky, IL est très amusant, car vous pouvez faire des choses très puissantes avec. Cela prend beaucoup de travail, et les méthodes Emit sont assez persnickety (ils ne fournissent aucune vérification que vous avez inclus les arguments corrects, donc il est très facile de créer par erreur le code IL qui ne parvient pas à vérifier ou exploser à l'exécution. Si vous jouez avec, n'oubliez pas que peverify.exe est votre ami. –

5

La fabrique typée est implémentée à l'aide de la bibliothèque Castle DynamicProxy. Il génère un type à la volée qui implémente l'interface et transmet tous les appels à ce type, que vous faites via l'interface, à l'intercepteur.

Il n'impose aucune dépendance dans votre code. L'interface est créée dans votre assemblage, que vous contrôlez, où vous ne faites pas référence à Windsor. Dans une autre assemblée (point d'entrée de l'application), vous informez Windsor de cette interface et dites-lui de faire une usine et Windsor apprend votre interface et fait des choses avec elle. C'est l'inversion du contrôle dans sa gloire.

Il est en fait pas si compliqué que ça :)

+0

Merci pour la réponse. Donc, je suppose que la bibliothèque Castle DynamicProxy utilise Reflection.Emit? – TheCloudlessSky

+0

Peu importe. Je suis allé creuser à travers la source de DynamicProxy et trouvé toutes sortes de choses soignées (et oui c'est en utilisant Reflection.Emit). Merci! :) – TheCloudlessSky

1

ImpromptuInterface crée proxies dynamiques DLR basés sur des interfaces. Cela vous permet d'avoir une implémentation dynamique avec une interface statique. En fait, il a même un baseclass ImpromptuFactory qui fournit le point de départ pour créer des usines avec une implémentation dynamique basée sur l'interface.