Por que os módulos .NET separam os nomes dos arquivos dos espaços para nome?


9

Nas implementações da linguagem de programação Scheme (padrão R6RS), posso importar um módulo da seguinte maneira:

(import (abc def xyz))

O sistema tentará procurar um arquivo $DIR/abc/def/xyz.slsem $DIRalgum diretório em que você mantenha seus módulos do esquema. xyz.slsé o código fonte do módulo e é compilado em tempo real, se necessário.

Os sistemas de módulos Ruby, Python e Perl são semelhantes nesse aspecto.

C #, por outro lado, é um pouco mais envolvido.

Primeiro, você tem arquivos dll que devem ser referenciados por projeto. Você deve fazer referência a cada um explicitamente. Isso está mais envolvido do que, digamos, soltar arquivos DLL em um diretório e fazer com que o C # os escolha pelo nome.

Segundo, não há uma correspondência de nomes individual entre o nome do arquivo da DLL e os espaços de nomes oferecidos pela DLL. Eu posso apreciar essa flexibilidade, mas também pode ficar fora de controle (e tem).

Para tornar isso concreto, seria bom se, quando digo isso using abc.def.xyz;, o C # tentasse encontrar um arquivo abc/def/xyz.dll, em algum diretório que o C # saiba procurar (configurável por projeto).

Acho a maneira Ruby, Python, Perl, Scheme de lidar com módulos mais elegante. Parece que as línguas emergentes tendem a seguir o design mais simples.

Por que o mundo .NET / C # faz as coisas dessa maneira, com um nível extra de indireção?


10
O mecanismo de montagem e resolução de classes do .NET funciona bem há mais de 10 anos. Acho que está faltando um mal-entendido fundamental (ou de pesquisa não é suficiente) sobre por que ele é projetado dessa forma - por exemplo, para apoiar a montagem redirecionando etc em muitos outros mecanismos de resolução úteis tempo de ligação e
Kev

Tenho certeza de que a resolução DLL de uma instrução using interromperia a execução lado a lado. Além disso, se havia um 1 a 1 você precisa de 50 dlls para todos os namespaces em mscorlib, ou eles teriam que abandonar a idéia de namespaces
Conrad Frix

Um nível extra de indireção? Hmmmmm .... dmst.aueb.gr/dds/pubs/inbook/beautiful_code/html/Spi07g.html
user541686

@gilles Obrigado pela edição e melhoria da pergunta!
Dharmatech 20/05

Alguns de seus pontos são peculiares ao Visual Studio, e compará-los a um idioma não é muito justo ( por exemplo , referências de DLL em Projetos). Uma comparação melhor para o ambiente .NET completo seria Java + Eclipse.
Ross Patterson

Respostas:


22

A seguinte anotação na Seção 3.3 das Diretrizes de Projeto da Estrutura . O Names of Assemblies and Dlls oferece informações sobre por que namespaces e assemblies são separados.

BRAD ABRAMS No início do design do CLR, decidimos separar a visão do desenvolvedor da plataforma (namespaces) da visão de empacotamento e implantação da plataforma (assemblies). Essa separação permite que cada um seja otimizado independentemente, com base em seus próprios critérios. Por exemplo, somos livres para fatorar espaços de nome para agrupar tipos que são funcionalmente relacionados (por exemplo, todo o material de E / S no System.IO), enquanto os assemblies podem ser fatorados por motivos de desempenho (tempo de carregamento), implantação, manutenção ou versão .


Parece a fonte mais autorizada até agora. Graças à Conrad!
Dharmatech 17/05/12

+1 para obter a resposta autorizada. Mas também, toda pergunta " por que C # faz <X> " também precisa ser vista através da lente " Java faz <X> da seguinte maneira: ... ", porque o C # foi (explicitamente ou não) uma reação às ameaças da Sun e As diferentes agendas da Microsoft para Java no Windows.
Ross Patterson

6

Ele adiciona flexibilidade e permite carregar as bibliotecas (o que você chama de módulos em sua pergunta) sob demanda.

Um espaço para nome, várias bibliotecas:

Uma das vantagens é que posso substituir facilmente uma biblioteca por outra. Digamos que eu tenho um espaço para nome MyCompany.MyApplication.DALe uma biblioteca DAL.MicrosoftSQL.dll, que contém todas as consultas SQL e outras coisas que podem ser específicas ao banco de dados. Se eu quiser que o aplicativo seja compatível com o Oracle, basta adicionar DAL.Oracle.dll, mantendo o mesmo espaço para nome. A partir de agora, posso entregar o aplicativo com uma biblioteca para clientes que precisam da compatibilidade com o Microsoft SQL Server e com a outra biblioteca para clientes que usam Oracle.

Alterar o espaço para nome nesse nível levaria à duplicação do código ou à necessidade de alterar todos os usings dentro do código-fonte de cada banco de dados.

Uma biblioteca, vários espaços para nome:

Ter vários namespaces em uma biblioteca também é vantajoso em termos de legibilidade. Se, em uma classe, eu usar apenas um dos namespaces, coloquei apenas este na parte superior do arquivo.

  • Ter todos os espaços para nome de uma grande biblioteca seria bastante confuso tanto para a pessoa que leu o código-fonte quanto para o próprio escritor, com o Intellisense tendo muitas coisas para sugerir em um determinado contexto.

  • Com bibliotecas menores, uma biblioteca por arquivo teria um impacto no desempenho: cada biblioteca deve ser carregada sob demanda na memória e processada pela máquina virtual ao executar o aplicativo; menos arquivos para carregar significa um desempenho um pouco melhor.


O segundo caso não exige que os nomes de arquivos sejam separados dos espaços para nome (embora permitir essa separação facilite significativamente a separação), pois um determinado assembly pode facilmente ter muitas subpastas e arquivos. Além disso, também é possível incorporar vários assemblies na mesma DLL (por exemplo, usando o ILMerge ). Trabalhos em Java seguem essa abordagem.
18712 Brian

2

Parece que você está optando por sobrecarregar a terminologia do "namespace" e do "módulo". Não deve surpreender que você veja as coisas como "indiretas" quando elas não se encaixam nas suas definições.

Na maioria dos idiomas que oferecem suporte a espaços para nome, incluindo C #, um espaço para nome não é um módulo. Um espaço para nome é uma maneira de definir nomes de escopo. Módulos são uma forma de escopo de comportamento.

Em geral, embora o tempo de execução .Net suporte a idéia de um módulo (com uma definição ligeiramente diferente daquela que você está usando implicitamente), é raramente usado; Eu só vi isso usado em projetos criados no SharpDevelop, principalmente para que você pudesse criar uma única DLL a partir de módulos criados em diferentes idiomas. Em vez disso, construímos bibliotecas usando uma biblioteca vinculada dinamicamente.

No C #, os namespaces são resolvidos sem nenhuma "camada de indireção", desde que todos estejam no mesmo binário; qualquer indireção necessária é de responsabilidade do compilador e vinculador que você não precisa pensar muito. Depois de começar a criar um projeto com várias dependências, você faz referência a bibliotecas externas. Depois que o seu projeto faz uma referência a uma biblioteca externa (DLL), o compilador a encontra para você.

No esquema, se você precisar carregar uma biblioteca externa, precisará fazer algo como (#%require (lib "mylib.ss"))primeiro ou usar a interface de função externa diretamente, pelo que me lembro. Se você estiver usando binários externos, terá a mesma quantidade de trabalho para resolver binários externos. É provável que você tenha usado bibliotecas na maioria das vezes tão usadas que existe um calço baseado em esquema que abstrai isso de você, mas se você precisar escrever sua própria integração com uma biblioteca de terceiros, terá que trabalhar para "carregar" " a biblioteca.

No Ruby, Módulos, Espaços de Nomes e Nomes de Arquivos são realmente muito menos conectados do que você imagina; o LOAD_PATH torna as coisas um pouco complicadas e as declarações do módulo podem estar em qualquer lugar. O Python provavelmente está mais perto de fazer as coisas da maneira que você pensa que está vendo no Scheme, exceto que as bibliotecas de terceiros em C ainda adicionam uma (pequena) ruga.

Além disso, linguagens dinamicamente tipadas como Ruby, Python e Lisp normalmente não têm a mesma abordagem de "contratos" que linguagens estaticamente tipificadas. Em idiomas tipificados dinamicamente, você geralmente estabelece apenas uma espécie de "acordo dos cavalheiros" de que o código responderá a certos métodos e, se suas classes parecerem estar falando o mesmo idioma, tudo estará bem. Linguagens de tipo estático possuem mecanismos adicionais para impor essas regras em tempo de compilação. No C #, o uso de um contrato desse tipo permite fornecer pelo menos garantias de adesão moderadamente úteis a essas interfaces, o que permite agrupar plugins e substituições com algum grau de garantia de semelhança, pois todos compilam com o mesmo contrato. Em Ruby ou Scheme, você verifica esses contratos escrevendo testes que funcionam em tempo de execução.

Há um benefício de desempenho mensurável com essas garantias de tempo de compilação, pois uma chamada de método não requer expedição dupla. Para obter esses benefícios em algo como Lisp, Ruby, JavaScript ou qualquer outro lugar, o que agora são mecanismos ainda exóticos de classes compiladas estaticamente just-in-time em VMs especializadas é necessário.

Uma coisa para a qual o ecossistema C # ainda tem suporte relativamente imaturo é o gerenciamento dessas dependências binárias; O Java possui o Maven há vários anos para garantir que você tenha todas as suas dependências necessárias, enquanto o C # ainda possui uma abordagem bastante primitiva do tipo MAKE, que envolve a colocação estratégica de arquivos no lugar certo antes do tempo.


11
Em relação ao gerenciamento de dependências, você pode dar uma olhada no NuGet . Aqui está um bom artigo sobre isso de Phil Haack
Conrad Frix

Nas implementações do esquema R6RS que usei (Ikarus, Chez e Ypsilon, por exemplo), as dependências são tratadas automaticamente, com base nas importações da biblioteca. As dependências são encontradas e, se necessário, compiladas e armazenadas em cache para futuras importações.
Dharmatech

Familiarizado com Nuget, e, portanto, meu comentário de que é "relativamente imaturo" #
JasonTrue
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.