Sembra che tu stia scegliendo di sovraccaricare la terminologia di "spazio dei nomi" e "modulo". Non dovrebbe sorprendere vedere le cose come "indirette" quando non si adattano alle tue definizioni.
Nella maggior parte delle lingue che supportano gli spazi dei nomi, incluso C #, uno spazio dei nomi non è un modulo. Uno spazio dei nomi è un modo di individuare i nomi. I moduli sono un modo di comportamento di scoping.
In generale, mentre il runtime .Net supporta l'idea di un modulo (con una definizione leggermente diversa da quella che si sta usando implicitamente), è piuttosto raramente usato; L'ho visto usato solo in progetti creati in SharpDevelop, principalmente per poter costruire una singola DLL da moduli costruiti in lingue diverse. Invece, costruiamo librerie usando una libreria collegata dinamicamente.
In C #, gli spazi dei nomi si risolvono senza alcun "livello di riferimento indiretto" purché siano tutti nello stesso binario; qualsiasi indiretta richiesta è una responsabilità del compilatore e del linker a cui non devi pensare molto. Una volta che inizi a creare un progetto con più dipendenze, fai riferimento a librerie esterne. Una volta che il progetto ha fatto riferimento a una libreria esterna (DLL), il compilatore lo trova per te.
In Scheme, se devi caricare una libreria esterna, devi fare qualcosa come (#%require (lib "mylib.ss"))
prima, oppure utilizzare direttamente l'interfaccia di funzione esterna, come ricordo. Se stai usando binari esterni, hai la stessa quantità di lavoro per risolvere i binari esterni. È probabile che tu abbia usato principalmente librerie così comunemente usate che esiste uno shim basato su Schema che lo sottrae da te, ma se mai dovessi scrivere la tua integrazione con una libreria di terze parti, dovrai essenzialmente fare del lavoro per "caricare " la Biblioteca.
In Ruby, Modules, Namespace e File Names sono in realtà molto meno connessi di quanto sembri; LOAD_PATH rende le cose un po 'complicate e le dichiarazioni del modulo possono essere ovunque. Python è probabilmente più vicino a fare le cose come pensi di vedere in Scheme, tranne per il fatto che le librerie di terze parti in C aggiungono ancora una (piccola) ruga.
Inoltre, i linguaggi tipizzati dinamicamente come Ruby, Python e Lisp in genere non hanno lo stesso approccio ai "contratti" dei linguaggi tipizzati staticamente. Nelle lingue tipizzate dinamicamente, di solito stabilisci solo una sorta di "accordo di gentiluomo" che il codice risponderà a determinati metodi, e se le tue classi sembrano parlare la stessa lingua, tutto va bene. I linguaggi tipizzati staticamente hanno meccanismi aggiuntivi per applicare queste regole in fase di compilazione. In C #, l'utilizzo di un contratto del genere consente di fornire garanzie di aderenza almeno moderatamente utili a queste interfacce, che consente di raggruppare plug-in e sostituzioni con un certo grado di garanzia di comunanza perché tutti compilano lo stesso contratto. In Ruby o Scheme, si verificano questi accordi scrivendo test che funzionano in fase di esecuzione.
Le prestazioni in termini di tempo di compilazione offrono un vantaggio misurabile in termini di prestazioni, in quanto una chiamata al metodo non richiede una doppia spedizione. Al fine di ottenere questi benefici in qualcosa come Lisp, Ruby, JavaScript o altrove, sono necessari meccanismi che ora sono ancora leggermente esotici di classi di compilazione statica just-in-time in VM specializzate.
Una cosa per cui l'ecosistema C # ha ancora un supporto relativamente immaturo è la gestione di queste dipendenze binarie; Java ha avuto Maven per diversi anni per assicurarsi di avere tutte le dipendenze necessarie, mentre C # ha ancora un approccio abbastanza primitivo simile a MAKE che prevede di posizionare strategicamente i file nel posto giusto prima del tempo.