Perché i moduli .NET separano i nomi dei file dei moduli dai namespace?

9

Nelle implementazioni del linguaggio di programmazione Scheme (standard R6RS) posso importare un modulo come segue:

(import (abc def xyz))

Il sistema proverà a cercare un file $DIR/abc/def/xyz.sls dove $DIR è una directory in cui si conservano i moduli Scheme. xyz.sls è il codice sorgente per il modulo ed è compilato al volo se necessario.

I sistemi di moduli Ruby, Python e Perl sono simili in questo senso.

C # d'altra parte è un po 'più coinvolto.

Per prima cosa, hai i file dll a cui devi fare riferimento per ogni progetto. È necessario fare riferimento a ciascuno in modo esplicito. Questo è più complicato che dire, lasciando cadere i file dll in una directory e facendoli raccogliendo per nome.

In secondo luogo, non esiste una corrispondenza di denominazione uno a uno tra il nome file della DLL e gli spazi dei nomi offerti dalla dll. Posso apprezzare questa flessibilità, ma può anche sfuggire di mano (e ha).

Per renderlo concreto, sarebbe bello se, quando dico questo using abc.def.xyz; , C # provasse a trovare un file abc/def/xyz.dll , in una directory in cui C # sa di cercare (configurabile per ogni progetto) .

Trovo più elegante il modo in cui Ruby, Python, Perl, Scheme gestiscono i moduli. Sembra che le lingue emergenti tendano ad andare con il design più semplice.

Perché il mondo .NET / C # fa le cose in questo modo, con un livello aggiuntivo di riferimento indiretto?

    
posta dharmatech 17.05.2012 - 18:35
fonte

3 risposte

21

La seguente annotazione nella Linee guida per la progettazione di framework Sezione 3.3. Nomi di Assemblee e DLL fornisce informazioni sul motivo per cui spazi dei nomi e assiemi sono separati.

BRAD ABRAMS Early in the design of the CLR we decided to seperate the developer view of the platform (namespaces) from the packaging and deployment view of the platform (assemblies). This separation allows each to be optimized independently based on its own criteria. For example, we are free to factor namespaces to group types that are functionally related (e.g., all the I/O stuff in System.IO) while the assemblies can be factored for performance (load time), deployment, servicing, or versioning reasons.

    
risposta data 17.05.2012 - 22:28
fonte
5

Aggiunge flessibilità e consente di caricare su richiesta le librerie (quelle che chiamate moduli nella domanda).

Uno spazio dei nomi, più librerie:

Uno dei vantaggi è che posso facilmente sostituire una libreria con un'altra. Diciamo che ho uno spazio dei nomi MyCompany.MyApplication.DAL e una libreria DAL.MicrosoftSQL.dll , che contiene tutte le query SQL e altre cose che potrebbero essere specifiche per il database. Se voglio che l'applicazione sia compatibile con Oracle, aggiungo solo DAL.Oracle.dll , mantenendo lo stesso spazio dei nomi. Da ora, posso consegnare l'applicazione con una libreria per i clienti che necessitano della compatibilità con Microsoft SQL Server e con l'altra libreria per i clienti che usano Oracle.

La modifica dello spazio dei nomi a questo livello porterebbe a un codice duplicato o alla necessità di andare a modificare tutti i using s all'interno del codice sorgente per ciascun database.

Una libreria, più spazi dei nomi:

Avere diversi spazi dei nomi in una libreria è anche vantaggioso in termini di leggibilità. Se, in una classe, utilizzo solo uno degli spazi dei nomi, inserisco solo questo nella parte superiore del file.

  • Avere tutti gli spazi dei nomi di una grande libreria sarebbe piuttosto confuso sia per la persona che legge il codice sorgente sia per la sceneggiatrice stessa, con Intellisense che ha troppe cose da suggerire in un dato contesto.

  • Avendo librerie più piccole, una libreria per file avrebbe un impatto sulle prestazioni: ogni libreria deve essere caricata su richiesta in memoria ed elaborata dalla macchina virtuale durante l'esecuzione dell'applicazione; meno file da caricare significa prestazioni leggermente migliori.

risposta data 17.05.2012 - 20:15
fonte
2

Sembra che tu stia scegliendo di sovraccaricare la terminologia sia del "namespace" che del "modulo". Non dovrebbe sorprendere il fatto che tu veda le cose come "indirette" quando non sono adatte 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 definire i nomi. I moduli sono un modo di osservare gli ambiti.

In generale, mentre il runtime .Net supporta l'idea di un modulo (con una definizione leggermente diversa da quella che si sta utilizzando implicitamente), è usato piuttosto raramente; L'ho visto solo in progetti sviluppati in SharpDevelop, principalmente per creare 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 indirezione richiesta è una responsabilità del compilatore e del linker a cui non devi pensare molto. Una volta che si inizia a costruire un progetto con più dipendenze, si fa riferimento alle librerie esterne. Una volta che il tuo progetto ha fatto riferimento a una libreria esterna (DLL), il compilatore lo trova per te.

In Scheme, se hai bisogno di caricare una libreria esterna, devi prima fare qualcosa come (#%require (lib "mylib.ss")) , o usare direttamente l'interfaccia della funzione straniera, come ricordo. Se si utilizzano i binari esterni, si ha 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 Scheme che ti astrae da te, ma se dovessi mai scrivere la tua integrazione con una libreria di terze parti, in pratica avrai un po 'di lavoro da "caricare" "la biblioteca.

In Ruby, Modules, Namespace e Nomi file sono in realtà molto meno connessi di quanto sembri supporre; il 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 che le librerie di terze parti in C aggiungono ancora una (piccola) ruga.

Inoltre, linguaggi tipizzati dinamicamente come Ruby, Python e Lisp in genere non hanno lo stesso approccio ai "contratti" come lingue tipizzate staticamente. Nelle lingue digitate dinamicamente, di solito si stabilisce solo una sorta di "Gentleman's agreement" che il codice risponderà a determinati metodi, e se le tue classi sembrano parlare la stessa lingua, tutto va bene. Le lingue tipizzate staticamente dispongono di meccanismi aggiuntivi per applicare queste regole in fase di compilazione. In C #, l'utilizzo di tale contratto consente di fornire almeno moderatamente utili garanzie di aderenza a queste interfacce, che consente di raggruppare plug-in e sostituzioni con un certo grado di garanzia di comunanza perché tutti si compilano con lo stesso contratto. In Ruby o Scheme, si verificano questi accordi scrivendo test che funzionano in fase di runtime. Ci sono costi e benefici per entrambe le soluzioni, ma non è d'aiuto l'idea di uno spazio dei nomi, un modulo e una libreria.

C'è un vantaggio misurabile in termini di prestazioni da queste garanzie di tempo di compilazione, poiché l'invocazione di un metodo non richiede una doppia spedizione. Per ottenere questi benefici in qualcosa come Lisp, Ruby, JavaScript o altrove, sono ora necessari meccanismi leggermente esotici di classi di compilazione statica just-in-time in macchine virtuali specializzate.

Una cosa su cui l'ecosistema C # ha ancora un supporto relativamente immaturo è la gestione di queste dipendenze binarie; Java ha avuto Maven per diversi anni per far sì che tu abbia tutte le tue dipendenze necessarie, mentre C # ha ancora un approccio MAKE abbastanza primitivo che implica posizionare strategicamente i file nel posto giusto prima del tempo.

    
risposta data 17.05.2012 - 20:46
fonte

Leggi altre domande sui tag