Jak firma Microsoft utworzyła zestawy, które mają odwołania cykliczne?

107

W .NET BCL istnieją cykliczne odwołania między:

  • System.dll i System.Xml.dll
  • System.dll i System.Configuration.dll
  • System.Xml.dll i System.Configuration.dll

Oto zrzut ekranu z .NET Reflector, który pokazuje, o co mi chodzi:

wprowadź opis obrazu tutaj

Sposób, w jaki Microsoft stworzył te zestawy, jest dla mnie tajemnicą. Czy wymagany jest specjalny proces kompilacji, aby to umożliwić? Wyobrażam sobie, że dzieje się tu coś interesującego.

Drew Noakes
źródło
2
Bardzo dobre pytanie. Właściwie nigdy nie poświęciłem czasu, żeby to sprawdzić, ale jestem ciekawy odpowiedzi. Rzeczywiście, wygląda na to, że Dykam zapewnił rozsądne rozwiązanie.
Noldorin
3
dlaczego te biblioteki dll nie są połączone w jeden, jeśli wszystkie wymagają siebie nawzajem? czy jest ku temu jakiś praktyczny powód?
Andreas Petersson
1
Ciekawe pytanie ... Chciałbym poznać odpowiedź Erica Lipperta na to! I jak powiedział Andreas, zastanawiam się, dlaczego nie umieścili wszystkiego w tym samym zestawie ...
Thomas Levesque
Cóż, jeśli jeden zespół wymaga aktualizacji, nie będą musieli dotykać drugiego. To jedyny powód, dla którego widzę. Ciekawe pytanie
Atmocreations
2
Spójrz na tę prezentację (pliki asmmeta): msakademik.net/academicdays2005/Serge_Lidin.ppt
Mehrdad Afshari

Odpowiedzi:

58

Mogę tylko powiedzieć, jak to robi Mono Project. Twierdzenie jest dość proste, chociaż powoduje bałagan w kodzie.

Najpierw kompilują System.Configuration.dll, a część nie wymaga odwołania do System.Xml.dll. Następnie kompilują System.Xml.dll w normalny sposób. Teraz jest magia. Ponownie kompilują System.configuration.dll, przy czym część wymaga odwołania do System.Xml.dll. Teraz jest pomyślna kompilacja z odwołaniem cyklicznym.

W skrócie:

  • A jest kompilowany bez kodu wymagającego B i odwołania do B.
  • B jest kompilowany.
  • A jest ponownie kompilowany.
Dykam
źródło
1
Jest blokowany przez program Visual Studio, ale można to zrobić bezpośrednio za pomocą kompilatora wiersza polecenia (csc.exe). Zobacz moją odpowiedź.
Alfred Myers
14
Wiem. Głównym systemem kompilacji Mono nie jest Visual Studio. Guess Microsofts też nie jest.
Dykam
35

RBarryYoung i Dykam są na czymś. Firma Microsoft używa wewnętrznego narzędzia, które używa ILDASM do dezasemblacji złożeń, usuwania wszystkich wewnętrznych / prywatnych elementów i treści metod oraz ponownej kompilacji IL (przy użyciu ILASM) do tak zwanego „zestawu odwodnionego” lub zestawu metadanych. Odbywa się to za każdym razem, gdy zmienia się publiczny interfejs asemblera.

Podczas kompilacji używane są zestawy metadanych zamiast rzeczywistych. W ten sposób cykl jest przerwany.

Srdjan Jovcic
źródło
1
Ciekawa odpowiedź, czy masz jakieś linki?
Henk Holterman
Próbuję znaleźć zewnętrzne odniesienie do narzędzia. Nie sądzę, aby został opublikowany poza firmą Microsoft, ale koncepcja jest prosta: zdemontować, rozłożyć elementy wewnętrzne, ponownie złożyć.
Srdjan Jovcic
Zgoda - ciekawa odpowiedź. Kilka linków do poparcia byłoby dobre.
Drew Noakes
Tak, rzeczywiście tak się to robi (z własnego doświadczenia).
Pavel Minaev
1
Nie są one silnie podpisane przed zakończeniem budowy (są podpisywane z opóźnieniem), więc odwodnione zespoły nie są podpisywane.
Srdjan Jovcic
26

Można to zrobić w sposób opisany przez Dykama, ale program Visual Studio uniemożliwia to.

Będziesz musiał bezpośrednio użyć kompilatora wiersza polecenia csc.exe.

  1. csc / target: biblioteka ClassA.cs

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

  3. csc / target: library 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
źródło
Możesz to zrobić również w programie Visual Studio, chociaż jest to dość trudne, podstawowym sposobem jest użycie # if's i usunięcie odwołania za pomocą eksploratora rozwiązań, odwracając to w trzecim kroku. Innym sposobem, o którym myślę, jest trzeci plik projektu zawierający te same pliki, ale różne odniesienia. To zadziała, ponieważ możesz określić kolejność kompilacji.
Dykam
O ile wiem, nie mogę tego tutaj przetestować.
Dykam
Naprawdę chciałbym to zobaczyć. Z tego, co tutaj eksperymentowałem, w momencie, gdy próbujesz dodać odniesienie, IDE cię zatrzymuje.
Alfred Myers
Wiem. Ale trzeci projekt nie ma tego odniesienia ORAZ symbolu #if i odwołuje się do niego drugi, do którego odwołuje się pierwszy. Bez cyklu. Ale trzeci używa kodu pierwszego i wyprowadza do pierwszej lokalizacji zestawu. zespół można łatwo wymienić na inny o tych samych specyfikacjach. Ale myślę, że strongnaming może powodować problem w tej metodzie.
Dykam
To trochę jak odpowiedź Srdjana, ale inna metoda.
Dykam
18

Jest to całkiem łatwe do zrobienia w programie Visual Studio, o ile nie używasz odwołań do projektu ... Spróbuj tego:

  1. Otwórz Visual Studio
  2. Utwórz 2 projekty biblioteki klas „ClassLibrary1” i „ClassLibrary2”.
  3. Budować
  4. Z ClassLibrary1 Dodaj odwołanie do ClassLibrary2, przechodząc do biblioteki dll utworzonej w kroku 3.
  5. Z ClassLibrary2 Dodaj odwołanie do ClassLibrary1, przechodząc do biblioteki dll utworzonej w kroku 3.
  6. Utwórz ponownie (Uwaga: jeśli wprowadzisz zmiany w obu projektach, musisz zbudować dwukrotnie, aby oba odniesienia były „świeże”)

Więc tak to się robi. Ale poważnie ... Nie rób tego NIGDY w prawdziwym projekcie! Jeśli to zrobisz, Mikołaj nie przyniesie Ci w tym roku żadnych prezentów.

JohannesH
źródło
1
Jedynym wyjątkiem jest to, że jest to między 26 a 31 grudnia, a prezenty zostały już zabezpieczone
Jesse Hufstetler,
6

Myślę, że można to zrobić zaczynając od acyklicznego zestawu złożeń i używając ILMerge, aby następnie połączyć mniejsze zespoły w logicznie powiązane grupy.

Steve Gilham
źródło
4

Cóż, nigdy nie robiłem tego w systemie Windows, ale zrobiłem to na wielu środowiskach kompilacji-link-rtl, które służyły jako praktyczni przodkowie. Najpierw tworzysz pośredniczące „cele” bez odsyłaczy, a następnie łączysz, dodajesz odwołania cykliczne i ponownie łączysz. Linkery generalnie nie przejmują się cyklicznymi referencjami lub podążaniem za łańcuchami referencji, dbają tylko o możliwość samodzielnego rozwiązania każdego odwołania.

Więc jeśli masz dwie biblioteki, A i B, które muszą się odwoływać, spróbuj czegoś takiego:

  1. Link A bez odwołań do B.
  2. Link B z odwołaniami do A.
  3. Link A, dodając odwołania do B.

Dykam ma rację, jest kompilowany, a nie linkowany w .Net, ale zasada pozostaje ta sama: twórz źródła z odsyłaczami z ich wyeksportowanymi punktami wejścia, ale wszystkie z nich oprócz jednego mają własne odniesienia do innych. na zewnątrz. Zbuduj je w ten sposób. Następnie usuń odnośniki zewnętrzne i odbuduj je. To powinno działać nawet bez żadnych specjalnych narzędzi, w rzeczywistości to podejście działało na każdym systemie operacyjnym, na którym kiedykolwiek próbowałem (około 6 z nich). Chociaż oczywiście coś, co automatyzuje, byłoby bardzo pomocne.

RBarryYoung
źródło
twierdzenie jest słuszne. Jednak w świecie .Net linkowanie jest dynamiczne i nie stanowi problemu. To krok kompilacji, w którym potrzebne jest to rozwiązanie.
Dykam
Przepraszam, że znowu cię naprawiam: P. Ale odwoływanie się (linkowanie) w czasie kompilacji ma miejsce w świecie .Net, który jest wszystkim, co pochodzi z tej konkretnej specyfikacji ECMA. Tak więc Mono, dotGnu i .Net. Nie sam system Windows.
Dykam
1

Jedną z możliwych metod jest użycie kompilacji warunkowej (#if), aby najpierw skompilować System.dll, który nie zależy od tych innych zestawów, następnie skompilować inne zestawy, a na końcu ponownie skompilować System.dll, aby uwzględnić części zależne od Xml i Konfiguracja.

Daniel
źródło
1
Niestety nie pozwala to na warunkowe odwołanie się do zespołu (chciałbym, żeby było to możliwe, to naprawdę pomogłoby w jednym z moich projektów ...)
Thomas Levesque
1
Odwołania warunkowe można łatwo wykonać, edytując plik .csproj. Wystarczy dodać atrybut Condition do elementu <Reference>.
Daniel
0

Z technicznego punktu widzenia możliwe jest, że nie zostały one w ogóle skompilowane i złożone ręcznie. W końcu są to biblioteki niskiego poziomu.

tylermac
źródło
Nie całkiem. Nie ma w nim wielu rzeczy niskiego poziomu, tylko podstawowe. Co sprawiło, że pomyślałeś, że to niski poziom? Runtime i corlib są na niskim poziomie. Stosunkowo. Wciąż zwykły C lub C ++, myślę, że JIT zawiera elementy niskiego poziomu.
Dykam