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.


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.

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.


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 {
    }
}

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.


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.


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.


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.


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.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.