In che modo Microsoft ha creato assembly con riferimenti circolari?


107

In .NET BCL ci sono riferimenti circolari tra:

  • System.dll e System.Xml.dll
  • System.dll e System.Configuration.dll
  • System.Xml.dll e System.Configuration.dll

Ecco uno screenshot di .NET Reflector che mostra cosa intendo:

inserisci qui la descrizione dell'immagine

Il modo in cui Microsoft ha creato questi assembly è un mistero per me. È necessario un processo di compilazione speciale per consentirlo? Immagino che qui stia succedendo qualcosa di interessante.


2
Domanda molto buona. In realtà non mi sono mai preso il tempo di ispezionarlo, ma sono curioso di conoscere la risposta. In effetti, sembra che Dykam ne abbia fornito uno sensato.
Noldorin

3
perché quelle dll non sono unite in una, se si richiedono tutte a vicenda? c'è qualche ragione pratica per questo?
Andreas Petersson

1
Domanda interessante ... mi piacerebbe conoscere la risposta di Eric Lippert a questa! E come ha detto Andreas, mi chiedo perché non abbiano messo tutto nella stessa assemblea ...
Thomas Levesque

Ebbene, se un assieme deve essere aggiornato, non sarà necessario toccare gli altri. Questo è l'unico motivo per cui vedo. Domanda interessante però
Atmocreations

2
Dai un'occhiata a questa presentazione (file asmmeta): msakademik.net/academicdays2005/Serge_Lidin.ppt
Mehrdad Afshari

Risposte:


58

Posso solo dire come fa il Mono Project. Il teorema è abbastanza semplice, sebbene dia un pasticcio di codice.

Per prima cosa compilano System.Configuration.dll, senza che la parte richieda il riferimento a System.Xml.dll. Dopo questo, compilano System.Xml.dll nel modo normale. Adesso arriva la magia. Ricompilano System.configuration.dll, con la parte che richiede il riferimento a System.Xml.dll. Ora c'è una compilazione di successo con il riferimento circolare.

In breve:

  • A viene compilato senza che il codice richieda B e il riferimento a B.
  • B è compilato.
  • A viene ricompilato.

1
È bloccato da Visual Studio, ma può essere eseguito utilizzando direttamente il compilatore della riga di comando (csc.exe). Vedi la mia risposta.
Alfred Myers,

14
Lo so. Il sistema di compilazione principale di Mono non è Visual Studio. Immagino che nemmeno Microsofts lo sia.
Dykam

35

RBarryYoung e Dykam stanno pensando a qualcosa. Microsoft utilizza uno strumento interno che utilizza ILDASM per disassemblare gli assembly, rimuovere tutti gli elementi interni / privati ​​e i corpi dei metodi e ricompilare nuovamente IL (utilizzando ILASM) in quello che viene chiamato "assembly disidratato" o assembly di metadati. Questa operazione viene eseguita ogni volta che viene modificata l'interfaccia pubblica dell'assembly.

Durante la compilazione, vengono usati gli assembly di metadati anziché quelli reali. In questo modo il ciclo si interrompe.


1
Risposta interessante, hai dei link?
Henk Holterman

Sto cercando di trovare un riferimento esterno allo strumento. Non credo che sia pubblicato al di fuori di Microsoft, ma il concetto è semplice: smontare-rimuovere gli interni-rimontare.
Srdjan Jovcic

D'accordo - risposta interessante. Alcuni link per sostenerlo sarebbero utili.
Drew Noakes il

Sì, è davvero così che viene fatto (per esperienza personale).
Pavel Minaev

1
Non sono fortemente firmati fino a dopo la compilazione (sono firmati in ritardo), quindi gli assembly disidratati non vengono firmati.
Srdjan Jovcic

26

Può essere fatto nel modo descritto da Dykam, ma Visual Studio ti impedisce di farlo.

Dovrai utilizzare direttamente il compilatore della riga di comando csc.exe.

  1. csc / target: libreria ClassA.cs

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

  3. csc / target: libreria 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 {
    }
}

Puoi farlo anche in Visual Studio anche se è abbastanza difficile, il modo di base è usare # if e rimuovere il riferimento usando Esplora soluzioni, invertendolo nel terzo passaggio. Un altro modo a cui sto pensando è un terzo file di progetto che include gli stessi file ma riferimenti diversi. Ciò funzionerebbe poiché è possibile specificare l'ordine di compilazione.
Dykam

Per quanto ne so, non posso provarlo qui.
Dykam

Mi piacerebbe davvero vederlo. Da quello che ho sperimentato qui, nel momento in cui provi ad aggiungere riferimento, l'IDE ti ferma.
Alfred Myers

Lo so. Ma un terzo progetto non ha quel riferimento E il simbolo #if, ed è referenziato dal secondo, a cui fa riferimento il primo. Nessun ciclo. Ma il terzo utilizza il codice del primo e restituisce il primo percorso di assemblaggio. un assieme può essere facilmente sostituito da un altro con le stesse specifiche. Ma penso che lo strongnaming possa causare un problema con questo metodo.
Dykam

È un po 'come la risposta di Srdjan, anche se un metodo diverso.
Dykam

18

È abbastanza facile da fare in Visual Studio fintanto che non usi riferimenti al progetto ... Prova questo:

  1. Apri Visual Studio
  2. Creare 2 progetti di libreria di classi "ClassLibrary1" e "ClassLibrary2".
  3. Costruire
  4. Da ClassLibrary1 aggiungere un riferimento a ClassLibrary2 sfogliando la dll creata nel passaggio 3.
  5. Da ClassLibrary2 aggiungere un riferimento a ClassLibrary1 sfogliando la dll creata nel passaggio 3.
  6. Crea di nuovo (Nota: se apporti modifiche in entrambi i progetti, dovrai compilare due volte per rendere entrambi i riferimenti "freschi")

Quindi è così che lo fai. Ma seriamente ... non farlo MAI in un progetto reale! Se lo fai, Babbo Natale non ti porterà nessun regalo quest'anno.


1
L'unica eccezione è se è tra il 26 e il 31 dicembre ei regali sono già stati assicurati
Jesse Hufstetler

6

Immagino che potrebbe essere fatto iniziando con un insieme aciclico di assembly e utilizzando ILMerge per poi unire gli assembly più piccoli in gruppi logicamente correlati.


4

Beh, non l'ho mai fatto su Windows, ma l'ho fatto su molti ambienti compile-link-rtl che sono serviti come pratici progenitori per questo. Quello che devi fare è prima creare "target" stub senza i riferimenti incrociati, quindi collegare, quindi aggiungere i riferimenti circolari, quindi ricollegare. I linker generalmente non si preoccupano dei riferimenti circolari o delle seguenti catene di riferimenti, si preoccupano solo di essere in grado di risolvere ogni riferimento da soli.

Quindi, se hai due librerie, A e B che devono fare riferimento l'una all'altra, prova qualcosa del genere:

  1. Collega A senza riferimenti a B.
  2. Collega B con riferimenti ad A.
  3. Link A, aggiungendo i riferimenti a B.

Dykam è un buon punto, è compilare, non collegare in .Net, ma il principio rimane lo stesso: crea le tue fonti con riferimenti incrociati, con i loro punti di ingresso esportati, ma con tutti tranne uno che hanno i propri riferimenti agli altri stubbed su. Costruiscili così. Quindi, elimina i riferimenti esterni e ricostruiscili. Questo dovrebbe funzionare anche senza strumenti speciali, infatti, questo approccio ha funzionato su tutti i sistemi operativi su cui l'ho provato (circa 6 di loro). Anche se ovviamente qualcosa che lo automatizza sarebbe di grande aiuto.


il teorema è giusto. Tuttavia, nel mondo .Net, il collegamento è dinamico e non è un problema. È la fase di compilazione in cui è necessaria questa soluzione.
Dykam

Mi dispiace risolverti di nuovo: P. Ma la referenziazione (collegamento) in fase di compilazione avviene nel mondo .Net, che è tutto ciò che deriva da quella specifica specifica ECMA. Quindi Mono, dotGnu e .Net. Non Windows stesso.
Dykam

1

Un possibile approccio è utilizzare la compilazione condizionale (#if) per compilare prima un System.dll che non dipende da quegli altri assembly, quindi compilare gli altri assembly e infine ricompilare System.dll per includere le parti che dipendono da Xml e Configurazione.


1
Sfortunatamente questo non ti consente di fare riferimento in modo condizionale a un assembly (vorrei che fosse possibile, sarebbe davvero d'aiuto in uno dei miei progetti ...)
Thomas Levesque

1
I riferimenti condizionali possono essere eseguiti facilmente modificando il file .csproj. Basta aggiungere un attributo Condizione all'elemento <Reference>.
Daniel

0

Tecnicamente, è possibile che questi non siano stati compilati affatto e assemblati a mano. Queste sono biblioteche di basso livello, dopotutto.


Non proprio. Non ci sono molte cose di basso livello, solo di base. Cosa ti ha fatto pensare che sarebbe stato di basso livello? Il runtime e il corlib sono di basso livello. Relativamente. Ancora semplice C o C ++, pensavo che JIT contenesse roba di basso livello.
Dykam
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.