Comment Microsoft a-t-il créé des assemblys avec des références circulaires?

107

Dans le .NET BCL, il existe des références circulaires entre:

  • System.dll et System.Xml.dll
  • System.dll et System.Configuration.dll
  • System.Xml.dll et System.Configuration.dll

Voici une capture d'écran de .NET Reflector qui montre ce que je veux dire:

entrez la description de l'image ici

La façon dont Microsoft a créé ces assemblages est un mystère pour moi. Un processus de compilation spécial est-il nécessaire pour permettre cela? J'imagine que quelque chose d'intéressant se passe ici.

Drew Noakes
la source
2
Très bonne question. Je n'ai jamais pris le temps d'inspecter cela, mais je suis curieux de connaître la réponse. En effet, il semble que Dykam en ait fourni un sensé.
Noldorin
3
pourquoi ces DLL ne sont-elles pas fusionnées en une seule, si elles se requièrent toutes? y a-t-il une raison pratique à cela?
Andreas Petersson
1
Question intéressante ... J'aimerais connaître la réponse d'Eric Lippert à celle-ci! Et comme Andreas l'a dit, je me demande pourquoi ils n'ont pas tout mis dans le même assemblage ...
Thomas Levesque
Eh bien, si un assemblage doit être mis à jour, il n'aura pas besoin de toucher les autres. C'est la seule raison pour laquelle je vois. Question intéressante cependant
Atmocreations
2
Jetez un œil à cette présentation (fichiers asmmeta): msakademik.net/academicdays2005/Serge_Lidin.ppt
Mehrdad Afshari

Réponses:

58

Je peux seulement dire comment le projet Mono fait cela. Le théorème est assez simple, bien qu'il donne un désordre de code.

Ils compilent d'abord System.Configuration.dll, sans que la partie ait besoin de la référence à System.Xml.dll. Après cela, ils compilent System.Xml.dll de la manière normale. Maintenant vient la magie. Ils recompilent System.configuration.dll, la partie nécessitant la référence à System.Xml.dll. Maintenant, il y a une compilation réussie avec la référence circulaire.

En bref:

  • A est compilé sans que le code ait besoin de B et de la référence à B.
  • B est compilé.
  • A est recompilé.
Dykam
la source
1
Il est bloqué par Visual Studio, mais peut être effectué à l'aide du compilateur de ligne de commande (csc.exe) directement. Voyez ma réponse.
Alfred Myers
14
Je connais. Le système de construction principal de Mono n'est pas Visual Studio. Je suppose que Microsofts ne l'est pas non plus.
Dykam
35

RBarryYoung et Dykam sont sur quelque chose. Microsoft utilise un outil interne qui utilise ILDASM pour désassembler les assemblys, dépouiller tous les éléments internes / privés et les corps de méthode et recompiler IL à nouveau (en utilisant ILASM) dans ce que l'on appelle un `` assemblage déshydraté '' ou un assemblage de métadonnées. Ceci est fait à chaque fois que l'interface publique de l'assembly est modifiée.

Pendant la génération, les assemblys de métadonnées sont utilisés au lieu des vrais. De cette façon, le cycle est rompu.

Srdjan Jovcic
la source
1
Réponse intéressante, avez-vous des liens?
Henk Holterman
J'essaie de trouver une référence externe à l'outil. Je ne pense pas qu'il soit publié en dehors de Microsoft, mais le concept est simple: démonter-démonter les internes-réassembler.
Srdjan Jovcic
D'accord - réponse intéressante. Quelques liens pour sauvegarder cela seraient bons.
Drew Noakes
Oui, c'est bien ainsi que cela se fait (par expérience personnelle).
Pavel Minaev
1
Ils ne sont fortement signés qu'après la construction (ils sont signés avec retard), de sorte que les assemblages déshydratés ne sont pas signés.
Srdjan Jovcic
26

Cela peut être fait de la manière décrite par Dykam, mais Visual Studio vous empêche de le faire.

Vous devrez utiliser directement le compilateur de ligne de commande csc.exe.

  1. csc / target: bibliothèque ClassA.cs

  2. csc / target: bibliothèque ClassB.cs /reference:ClassA.dll

  3. csc / target: bibliothèque ClassA.cs ClassC.cs /reference:ClassB.dll


//ClassA.cs
namespace CircularA {
    public class ClassA {
    }
}


//ClassB.cs
using CircularA;
namespace CircularB {
    public class ClassB : ClassA  {
    }
}


//ClassC.cs
namespace CircularA {
    class ClassC : ClassB {
    }
}
Alfred Myers
la source
Vous pouvez également le faire dans Visual Studio, bien que cela soit assez difficile, la méthode de base consiste à utiliser les # if et à supprimer la référence à l'aide de l'explorateur de solutions, en inversant cela dans la troisième étape. Je pense également à un troisième fichier de projet comprenant les mêmes fichiers mais des références différentes. Cela fonctionnerait car vous pouvez spécifier l'ordre de construction.
Dykam
Autant que je sache, je ne peux pas le tester ici.
Dykam
J'aimerais vraiment voir ça. D'après ce que j'ai expérimenté ici, au moment où vous essayez d'ajouter une référence, l'EDI vous arrête.
Alfred Myers
Je connais. Mais un troisième projet n'ayant pas cette référence ET le symbole #if, et être référencé par le second, qui est référencé par le premier. Pas de cycle. Mais le troisième utilise le code du premier et renvoie vers le premier emplacement d'assemblage. un assemblage peut facilement être remplacé par un autre avec les mêmes spécifications. Mais je pense que le nom fort peut poser un problème dans cette méthode.
Dykam
C'est un peu la réponse de Srdjan, mais une méthode différente.
Dykam
18

C'est assez facile à faire dans Visual Studio tant que vous n'utilisez pas de références de projet ... Essayez ceci:

  1. Ouvrir un studio visuel
  2. Créez 2 projets de bibliothèque de classes "ClassLibrary1" et "ClassLibrary2".
  3. Construire
  4. À partir de ClassLibrary1, ajoutez une référence à ClassLibrary2 en accédant à la DLL créée à l'étape 3.
  5. À partir de ClassLibrary2, ajoutez une référence à ClassLibrary1 en accédant à la DLL créée à l'étape 3.
  6. Reconstruire (Remarque: si vous apportez des modifications dans les deux projets, vous devrez créer deux fois pour rendre les deux références "fraîches")

Voilà donc comment vous procédez. Mais sérieusement ... Ne le faites JAMAIS dans un vrai projet! Si vous le faites, le Père Noël ne vous apportera aucun cadeau cette année.

JohannesH
la source
1
La seule exception étant si c'est entre le 26 et le 31 décembre et que les cadeaux ont déjà été obtenus
Jesse Hufstetler
6

Je suppose que cela pourrait être fait en commençant par un ensemble acyclique d'assemblages et en utilisant ILMerge pour ensuite fusionner les petits assemblages en groupes logiquement liés.

Steve Gilham
la source
4

Eh bien, je ne l'ai jamais fait sur Windows, mais je l'ai fait sur de nombreux environnements compile-link-rtl qui ont servi de progéniteurs pratiques pour cela. Ce que vous faites est d'abord de créer des "cibles" de stub sans les références croisées, puis de les lier, puis d'ajouter les références circulaires, puis de les relier. Les linkers ne se soucient généralement pas des références circulaires ou des chaînes de références suivantes, ils se soucient uniquement de pouvoir résoudre chaque référence par elle-même.

Donc, si vous avez deux bibliothèques, A et B qui doivent se référencer, essayez quelque chose comme ceci:

  1. Reliez A sans aucune référence à B.
  2. Lien B avec les références à A.
  3. Lien A, en ajoutant les références à B.

Dykam fait un bon point, c'est compiler, pas lier en .Net, mais le principe reste le même: faites vos sources croisées, avec leurs points d'entrée exportés, mais avec tous sauf un ayant leurs propres références aux autres en dehors. Construisez-les comme ça. Ensuite, supprimez les références externes et reconstruisez-les. Cela devrait fonctionner même sans outils spéciaux, en fait, cette approche a fonctionné sur tous les systèmes d'exploitation sur lesquels je l'ai essayé (environ 6 d'entre eux). Bien que de toute évidence, quelque chose qui automatise ce serait d'une grande aide.

RBarryYoung
la source
le théorème a raison. Cependant, dans le monde .Net, la liaison se fait de manière dynamique et ne pose pas de problème. C'est l'étape de compilation où cette solution est nécessaire.
Dykam
Désolé de vous réparer à nouveau: P. Mais le référencement (liaison) au moment de la compilation se produit dans le monde .Net, qui est tout ce qui est dérivé de cette spécification ECMA spécifique. Ainsi Mono, dotGnu et .Net. Pas Windows lui-même.
Dykam
1

Une approche possible consiste à utiliser la compilation conditionnelle (#if) pour d'abord compiler un System.dll qui ne dépend pas de ces autres assemblys, puis compiler les autres assemblys, et enfin recompiler System.dll pour inclure les parties dépendant de Xml et Configuration.

Daniel
la source
1
Malheureusement, cela ne vous permet pas de référencer conditionnellement un assemblage (j'aurais aimé que ce soit possible, cela aiderait vraiment dans l'un de mes projets ...)
Thomas Levesque
1
Les références conditionnelles peuvent être facilement effectuées en éditant le fichier .csproj. Ajoutez simplement un attribut Condition à l'élément <Reference>.
Daniel
0

Techniquement, il est possible que ceux-ci n'aient pas du tout été compilés et assemblés à la main. Ce sont des bibliothèques de bas niveau, après tout.

tylermac
la source
Pas vraiment. Il n'y a pas beaucoup de choses de bas niveau, seulement de base. Qu'est-ce qui vous a fait penser que ce serait de bas niveau? Le runtime et corlib sont de bas niveau. Relativement. Toujours en C ou C ++, je pensais que le JIT contenait des éléments de bas niveau.
Dykam