Как Microsoft создавала сборки с циклическими ссылками?

107

В .NET BCL существуют циклические ссылки между:

  • System.dll и System.Xml.dll
  • System.dll и System.Configuration.dll
  • System.Xml.dll и System.Configuration.dll

Вот скриншот из .NET Reflector, который показывает, что я имею в виду:

введите описание изображения здесь

Как Microsoft создавала эти сборки, для меня загадка. Требуется ли для этого специальный процесс компиляции? Я предполагаю, что здесь происходит что-то интересное.

Дрю Ноукс
источник
2
Очень хороший вопрос. На самом деле я никогда не удосужился проверить это, но мне любопытно узнать ответ. Действительно, похоже, что Dykam предоставил разумный вариант.
Нолдорин,
3
почему эти dll не объединены в одну, если все они нужны друг другу? есть ли для этого практическая причина?
Андреас Петерссон,
1
Интересный вопрос ... Я хотел бы знать ответ Эрика Липперта на этот! И как сказал Андреас, мне интересно, почему они не поместили все в одну и ту же сборку ...
Томас Левеск
Что ж, если одну сборку нужно обновить, им не нужно будет трогать другие. Это единственная причина, по которой я вижу. Интересный вопрос
Atmocreations
2
Взгляните на эту презентацию (файлы asmmeta): msakademik.net/academicdays2005/Serge_Lidin.ppt
Mehrdad Afshari

Ответы:

58

Я могу только сказать, как это делает Mono Project. Теорема довольно проста, хотя и вносит путаницу в код.

Сначала они компилируют System.Configuration.dll без той части, которая требует ссылки на System.Xml.dll. После этого они компилируют System.Xml.dll обычным способом. Теперь пришло волшебство. Они перекомпилируют System.configuration.dll, при этом часть требует ссылки на System.Xml.dll. Теперь есть успешная компиляция с круговой ссылкой.

Коротко:

  • A компилируется без кода, требующего B и ссылки на B.
  • B составлен.
  • A перекомпилирован.
Дыкам
источник
1
Это заблокировано Visual Studio, но может быть выполнено напрямую с помощью компилятора командной строки (csc.exe). Смотрите мой ответ.
Альфред Майерс,
14
Я знаю. Основная система сборки Mono - это не Visual Studio. Думаю, у Microsoft тоже нет.
Dykam
35

RBarryYoung и Dykam кое-что понимают. Microsoft использует внутренний инструмент, который использует ILDASM для дизассемблирования сборок, удаления всех внутренних / личных вещей и тел методов и повторной компиляции IL (с использованием ILASM) в так называемую «обезвоженную сборку» или сборку метаданных. Это происходит каждый раз при изменении публичного интерфейса сборки.

Во время сборки используются сборки метаданных вместо реальных. Таким образом, цикл нарушен.

Срджан Йовчич
источник
1
Интересный ответ, есть ли ссылки?
Хенк Холтерман,
Я пытаюсь найти внешнюю ссылку на инструмент. Я не думаю, что он опубликован за пределами Microsoft, но концепция проста: разобрать - убрать внутренности - собрать заново.
Срджан Йовчич,
Договорились - ответ интересный. Некоторые ссылки, подтверждающие это, были бы хороши.
Дрю Ноукс,
Да, это действительно так (из личного опыта).
Павел Минаев
1
Они не подписываются строго до завершения сборки (они подписываются с задержкой), поэтому обезвоженные сборки не подписываются.
Срджан Йовчич
26

Это можно сделать так, как описано в Dykam, но Visual Studio не дает вам этого сделать.

Вам придется напрямую использовать компилятор командной строки csc.exe.

  1. csc / target: библиотека ClassA.cs

  2. csc / target: библиотека ClassB.cs /reference:ClassA.dll

  3. csc / target: библиотека 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 {
    }
}
Альфред Майерс
источник
Вы также можете сделать это в Visual Studio, хотя это тоже довольно жестко, основной способ - использовать # if и удалить ссылку с помощью проводника решений, изменив это на третьем шаге. Другой способ, о котором я думаю, - это третий файл проекта, включающий те же файлы, но разные ссылки. Это будет работать, поскольку вы можете указать порядок сборки.
Dykam
Насколько я знаю, здесь не могу протестировать.
Dykam
Я бы очень хотел это увидеть. Судя по тому, что я здесь экспериментировал, в тот момент, когда вы пытаетесь добавить ссылку, IDE останавливает вас.
Альфред Майерс,
Я знаю. Но третий проект не имеет этой ссылки И символа #if и на него ссылается второй, на который ссылается первый. Без цикла. Но третий использует код первого и выводит в первое место сборки. одну сборку можно легко заменить другой с такими же характеристиками. Но я думаю, что сильное именование может вызвать проблемы в этом методе.
Dykam
Это немного похоже на ответ Срджана, хотя и другой метод.
Dykam
18

Это довольно просто сделать в Visual Studio, если вы не используете ссылки на проекты ... Попробуйте следующее:

  1. Открытая визуальная студия
  2. Создайте 2 проекта библиотеки классов «ClassLibrary1» и «ClassLibrary2».
  3. Построить
  4. Из ClassLibrary1 добавьте ссылку на ClassLibrary2, перейдя к dll, созданной на шаге 3.
  5. Из ClassLibrary2 добавьте ссылку на ClassLibrary1, перейдя к DLL, созданной на шаге 3.
  6. Снова выполните сборку (Примечание: если вы внесете изменения в оба проекта, вам придется выполнить сборку дважды, чтобы обе ссылки были «свежими»)

Вот как вы это делаете. А если серьезно ... НИКОГДА не делайте этого в реальном проекте! Если вы это сделаете, Санта в этом году не принесет вам подарков.

JohannesH
источник
1
Единственное исключение, если это будет между 26-31 декабря и подарки уже получены
Джесси Хафстетлер
6

Я предполагаю, что это можно было бы сделать, начав с ациклического набора сборок и используя ILMerge, чтобы затем объединить более мелкие сборки в логически связанные группы.

Стив Гилхэм
источник
4

Что ж, я никогда не делал этого в Windows, но я делал это во многих средах compile-link-rtl, которые послужили его практическими прародителями. Что вы делаете, так это сначала создаете «цели» заглушки без перекрестных ссылок, затем связываете, затем добавляете циклические ссылки, а затем повторно связываете. Компоновщики обычно не заботятся о циклических ссылках или следовании цепочкам ссылок, они заботятся только о том, чтобы иметь возможность разрешить каждую ссылку самостоятельно.

Итак, если у вас есть две библиотеки, A и B, которые должны ссылаться друг на друга, попробуйте что-то вроде этого:

  1. Ссылка A без ссылок на B.
  2. Ссылка B с на A.
  3. Ссылка A, добавление ссылок на B.

Dykam делает хорошее замечание: он компилируется, а не ссылается на .Net, но принцип остается тем же: сделайте ваши источники перекрестными ссылками с их экспортированными точками входа, но со всеми, кроме одного, со своими собственными ссылками на другие заглушки вне. Постройте их вот так. Затем отключите внешние ссылки и перестройте их. Это должно работать даже без каких-либо специальных инструментов. Фактически, этот подход работал во всех операционных системах, в которых я когда-либо пробовал (около 6 из них). Хотя, очевидно, что-то, что автоматизирует, было бы большим подспорьем.

RBarryYoung
источник
Теорема верна. Однако в мире .Net связывание выполняется динамически, и это не проблема. Это решение необходимо на этапе компиляции.
Dykam
Извините за исправление снова: P. Но ссылка (компоновка) во время компиляции происходит в мире .Net, который является всем, что получено из этой конкретной спецификации ECMA. Таким образом, Mono, dotGnu и .Net. Не сама винда.
Dykam
1

Один из возможных подходов - использовать условную компиляцию (#if), чтобы сначала скомпилировать System.dll, которая не зависит от этих других сборок, затем скомпилировать другие сборки и, наконец, перекомпилировать System.dll, чтобы включить части, зависящие от Xml и Конфигурация.

Даниэль
источник
1
К сожалению, это не позволяет вам условно ссылаться на сборку (я бы хотел, чтобы это было возможно, это действительно помогло бы в одном из моих проектов ...)
Томас Левеск,
1
Условные ссылки можно легко сделать, отредактировав файл .csproj. Просто добавьте атрибут Condition к элементу <Reference>.
Даниэль
0

Технически возможно, что они вообще не были скомпилированы, а собраны вручную. В конце концов, это библиотеки низкого уровня.

Tylermac
источник
На самом деле, нет. В нем не так много вещей низкого уровня, только базовые. Что заставило вас подумать, что это будет низкий уровень? Среда выполнения и corlib находятся на низком уровне. Относительно. По-прежнему простой C или C ++, хотя JIT содержит вещи низкого уровня.
Dykam