В реализациях языка программирования Scheme (стандарт R6RS) я могу импортировать модуль следующим образом:
(import (abc def xyz))
Система попытается найти файл, в $DIR/abc/def/xyz.sls
котором $DIR
находится какой-то каталог, в котором вы храните свои модули Scheme. xyz.sls
является исходным кодом для модуля и при необходимости компилируется на лету.
В этом отношении системы модулей Ruby, Python и Perl похожи.
C #, с другой стороны, немного сложнее.
Во-первых, у вас есть dll-файлы, на которые вы должны ссылаться отдельно для каждого проекта. Вы должны ссылаться на каждый явно. Это более сложный процесс, чем, скажем, удаление DLL-файлов в каталоге и получение C # их по имени.
Во-вторых, не существует однозначного соответствия именования между именем файла dll и пространством имен, предлагаемым dll. Я могу оценить эту гибкость, но она также может выйти из-под контроля (и имеет).
Чтобы конкретизировать, было бы неплохо, если бы, когда я это сказал using abc.def.xyz;
, C # попытался бы найти файл abc/def/xyz.dll
в каком-то каталоге, в котором C # знает, что искать (настраивается для каждого проекта).
Я считаю способ обработки модулей Ruby, Python, Perl, Scheme более элегантным. Кажется, что появляющиеся языки имеют тенденцию идти с более простым дизайном.
Почему мир .NET / C # работает таким образом, с дополнительным уровнем косвенности?
источник
Ответы:
Следующая аннотация в разделе « Руководство по разработке структуры» 3.3. Имена сборок и библиотек дают представление о том, почему пространства имен и сборки разделены.
источник
Это добавляет гибкости и позволяет загружать библиотеки (что вы называете модулями в вашем вопросе) по запросу.
Одно пространство имен, несколько библиотек:
Одним из преимуществ является то, что я могу легко заменить одну библиотеку на другую. Допустим, у меня есть пространство имен
MyCompany.MyApplication.DAL
и библиотекаDAL.MicrosoftSQL.dll
, которая содержит все запросы SQL и другие вещи, которые могут быть специфическими для базы данных. Если я хочу, чтобы приложение было совместимо с Oracle, я просто добавляюDAL.Oracle.dll
то же самое пространство имен. Теперь я могу поставить приложение с одной библиотекой для клиентов, которым требуется совместимость с Microsoft SQL Server, и с другой библиотекой для клиентов, использующих Oracle.Изменение пространства имен на этом уровне приведет либо к дублированию кода, либо к необходимости перейти и изменить все
using
элементы внутри исходного кода для каждой базы данных.Одна библиотека, несколько пространств имен:
Наличие нескольких пространств имен в одной библиотеке также выгодно с точки зрения читабельности. Если в классе я использую только одно из пространств имен, я помещаю только это в верхнюю часть файла.
Наличие всех пространств имен большой библиотеки будет довольно запутанным как для человека, который читает исходный код, так и для самого автора, поскольку у Intellisense есть слишком много вещей, которые можно предложить в данном контексте.
При наличии меньших библиотек одна библиотека на файл будет влиять на производительность: каждая библиотека должна загружаться по требованию в память и обрабатываться виртуальной машиной при запуске приложения; меньше файлов для загрузки означает немного лучшую производительность.
источник
Похоже, вы решили перегружать терминологию «пространства имен» и «модуля». Не должно быть сюрпризом, что вы видите вещи как «косвенные», когда они не соответствуют вашим определениям.
В большинстве языков, которые поддерживают пространства имен, включая C #, пространство имен не является модулем. Пространство имен - это способ определения имен. Модули - это способ определения поведения.
В целом, хотя среда выполнения .Net поддерживает идею модуля (с определением, немного отличающимся от того, который вы неявно используете), он используется довольно редко; Я видел его только в проектах, созданных в SharpDevelop, главным образом для того, чтобы вы могли собрать одну DLL из модулей, построенных на разных языках. Вместо этого мы создаем библиотеки с использованием динамически связанной библиотеки.
В C # пространства имен разрешаются без какого-либо «уровня косвенности», если они все находятся в одном двоичном файле; Ответственность за любое косвенное обращение лежит на компиляторе и компоновщике, о котором вам не нужно много думать. Как только вы начинаете создавать проект с несколькими зависимостями, вы затем ссылаетесь на внешние библиотеки. Как только ваш проект сделал ссылку на внешнюю библиотеку (DLL), компилятор найдет ее для вас.
В Схеме, если вам нужно загрузить внешнюю библиотеку, вы должны
(#%require (lib "mylib.ss"))
сначала сделать что-то подобное или использовать интерфейс сторонней функции напрямую, насколько я помню. Если вы используете внешние двоичные файлы, у вас есть такой же объем работы для разрешения внешних двоичных файлов. Скорее всего, вы в основном использовали библиотеки, которые так часто используются, что есть схема, основанная на Scheme, которая абстрагирует вас от этого, но если вам когда-нибудь понадобится написать собственную интеграцию со сторонней библиотекой, вам, по сути, придется поработать над «загрузкой». " библиотека.В Ruby Модули, Пространства имен и Имена файлов на самом деле гораздо менее связаны, чем кажется; LOAD_PATH делает вещи немного сложнее, и объявления модулей могут быть где угодно. Python, вероятно, ближе к тому, чтобы делать вещи так, как вы думаете в Scheme, за исключением того, что сторонние библиотеки в C по-прежнему добавляют (маленькую) складку.
Кроме того, языки с динамической типизацией, такие как Ruby, Python и Lisp, обычно не имеют такой же подход к «контрактам», как языки со статической типизацией. В динамически типизированных языках вы обычно устанавливаете своего рода «соглашение Джентльмена», что код будет реагировать на определенные методы, и, если кажется, что ваши классы говорят на одном языке, все хорошо. Языки со статической типизацией имеют дополнительные механизмы для обеспечения соблюдения этих правил во время компиляции. В C # использование такого контракта позволяет вам предоставлять как минимум умеренно полезные гарантии соблюдения этих интерфейсов, что позволяет связывать плагины и замены с некоторой степенью гарантии общности, поскольку все вы компилируете по одному и тому же контракту. В Ruby или Scheme вы проверяете эти соглашения путем написания тестов, которые работают во время выполнения.
Эти гарантии времени компиляции дают ощутимый выигрыш в производительности, так как вызов метода не требует двойной отправки. Для того, чтобы получить эти преимущества в чем-то вроде Lisp, Ruby, JavaScript или где-то еще, требуется то, что сейчас все еще является немного экзотическими механизмами статического компиляции классов точно в срок в специализированных виртуальных машинах.
Одна вещь, которую экосистема C # все еще имеет относительно незрелую поддержку, - это управление этими бинарными зависимостями; В течение нескольких лет в Java Maven работал над тем, чтобы убедиться, что у вас есть все необходимые зависимости, в то время как в C # все еще существует довольно примитивный MAKE-подобный подход, который предполагает стратегическое размещение файлов в нужном месте заранее.
источник