¿Cómo creó Microsoft ensamblajes que tienen referencias circulares?

En .NET BCL hay referencias circulares entre:

  • System.dll y System.Xml.dll
  • System.dll y System.Configuration.dll
  • System.Xml.dll y System.Configuration.dll

Aquí hay una captura de pantalla de .NET Reflector que muestra lo que quiero decir:

enter image description here

Cómo creó Microsoft estos conjuntos es un misterio para mí. ¿Se requiere un proceso de comstackción especial para permitir esto? Imagino que algo interesante está sucediendo aquí.

Solo puedo decir cómo el Proyecto Mono hace esto. El teorema es bastante simple, aunque da un desorden de código.

Primero comstackn System.Configuration.dll, sin que la parte necesite la referencia a System.Xml.dll. Después de esto, comstackn System.Xml.dll de la manera normal. Ahora viene la magia. Recomstackn System.configuration.dll, con la parte que necesita la referencia a System.Xml.dll. Ahora hay una comstackción exitosa con la referencia circular.

En breve:

  • A se comstack sin el código que necesita B y la referencia a B.
  • B está comstackdo.
  • A se recomstack.

RBarryYoung y Dykam están en algo. Microsoft usa una herramienta interna que usa ILDASM para desmontar ensamblajes, eliminar todos los elementos internos y privados y los cuerpos de métodos, y recomstackr IL nuevamente (usando ILASM) en lo que se denomina ‘ensamblaje deshidratado’ o ensamblaje de metadatos. Esto se hace cada vez que se cambia la interfaz pública de ensamblaje.

Durante la comstackción, se utilizan conjuntos de metadatos en lugar de los reales. De esa manera el ciclo se rompe.

Se puede hacer como Dykam lo describió, pero Visual Studio te impide hacerlo.

Tendrá que usar directamente el comstackdor de línea de comandos csc.exe.

  1. csc / target: biblioteca ClassA.cs

  2. csc / target: library ClassB.cs /reference:ClassA.dll

  3. csc / destino: biblioteca 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 { } } 

Es bastante fácil de hacer en Visual Studio siempre que no uses referencias de proyectos … Prueba esto:

  1. Abrir estudio visual
  2. Cree 2 proyectos de Class Library “ClassLibrary1” y “ClassLibrary2”.
  3. Construir
  4. Desde ClassLibrary1 agregue una referencia a ClassLibrary2 navegando al dll creado en el paso 3.
  5. Desde ClassLibrary2 agregue una referencia a ClassLibrary1 navegando al dll creado en el paso 3.
  6. Comstack de nuevo (Nota: si realizas cambios en ambos proyectos necesitarías comstackr dos veces para que ambas referencias sean “nuevas”)

Así que así es como lo haces. Pero en serio … ¡Nunca lo hagas en un proyecto real! Si lo haces, Santa no te traerá ningún regalo este año.

Supongo que se podría hacer comenzando con un conjunto acíclico de ensamblajes y utilizando ILMerge para luego fusionar los ensamblajes más pequeños en grupos lógicamente relacionados.

Bueno, nunca lo hice en Windows, pero lo he hecho en muchos de los entornos compile-link-rtl que servían como los progenitores prácticos para ello. Lo que debe hacer primero es crear “objectives” sin las referencias cruzadas, luego vincular, luego agregar las referencias circulares, luego volver a vincular. En general, los vinculadores no se preocupan por los ref circulares ni por las cadenas de ref, solo se preocupan por poder resolver cada referencia por sí mismos.

Entonces, si tiene dos bibliotecas, A y B que necesitan referenciarse, intente algo como esto:

  1. Enlace A sin referencias a B.
  2. Enlace B con referencias a A.
  3. Enlace A, agregando los refs a B.

Dykam hace un buen punto, es comstackr, no enlazar en .Net, pero el principio sigue siendo el mismo: hacer sus referencias cruzadas, con sus puntos de entrada exportados, pero con todos menos uno de ellos teniendo sus propias referencias a los otros apalabrados fuera. Constrúyalos así. Luego, quita las referencias externas y reconstruyelas. Esto debería funcionar incluso sin herramientas especiales, de hecho, este enfoque ha funcionado en todos los sistemas operativos en los que lo haya probado alguna vez (alrededor de 6 de ellos). Aunque obviamente algo que lo automatiza sería una gran ayuda.

Un posible enfoque es utilizar la comstackción condicional (#if) para comstackr primero un System.dll que no dependa de esos otros ensamblajes, luego comstackr los otros ensamblajes y finalmente recomstackr System.dll para incluir las partes que dependen de Xml y Configuración.

Técnicamente, es posible que estos no se hayan comstackdo en absoluto y se hayan ensamblado a mano. Estas son bibliotecas de bajo nivel, después de todo.