Il semble que vous choisissiez de surcharger la terminologie de "namespace" et de "module". Il ne devrait pas être surprenant que vous voyiez les choses comme "indirectes" quand elles ne correspondent pas à vos définitions.
Dans la plupart des langages qui prennent en charge les espaces de noms, y compris C #, un espace de noms n'est pas un module. Un espace de noms est un moyen de délimiter les noms. Les modules sont un moyen de délimiter le comportement.
En général, alors que le runtime .Net prend en charge l'idée d'un module (avec une définition légèrement différente de celle que vous utilisez implicitement), il est plutôt rarement utilisé; Je ne l'ai vu qu'utilisé dans des projets construits dans SharpDevelop, principalement pour que vous puissiez créer une seule DLL à partir de modules construits dans différents langages. Au lieu de cela, nous créons des bibliothèques à l'aide d'une bibliothèque liée dynamiquement.
En C #, les espaces de noms se résolvent sans "couche d'indirection" tant qu'ils sont tous dans le même binaire; toute indirection requise est une responsabilité du compilateur et de l'éditeur de liens à laquelle vous n'avez pas à réfléchir. Une fois que vous avez commencé à créer un projet avec plusieurs dépendances, vous référencez ensuite des bibliothèques externes. Une fois que votre projet a fait référence à une bibliothèque externe (DLL), le compilateur la trouve pour vous.
Dans Scheme, si vous devez charger une bibliothèque externe, vous devez faire quelque chose comme d' (#%require (lib "mylib.ss"))
abord, ou utiliser directement l'interface de fonction étrangère, si je me souviens bien. Si vous utilisez des binaires externes, vous avez la même quantité de travail pour résoudre les binaires externes. Il y a de fortes chances que vous ayez principalement utilisé des bibliothèques si couramment utilisées qu'il existe un module d'interface basé sur Scheme qui vous en fait abstraction, mais si vous devez écrire votre propre intégration avec une bibliothèque tierce, vous devrez essentiellement faire un peu de travail pour "charger" " la bibliothèque.
Dans Ruby, les modules, les espaces de noms et les noms de fichiers sont en réalité beaucoup moins connectés que vous ne le pensez. LOAD_PATH rend les choses un peu compliquées et les déclarations de module peuvent être n'importe où. Python est probablement plus proche de faire les choses comme vous pensez que vous voyez dans Scheme, sauf que les bibliothèques tierces en C ajoutent toujours une (petite) ride.
De plus, les langages typés dynamiquement comme Ruby, Python et Lisp n'ont généralement pas la même approche des "contrats" que les langages typés statiquement. Dans les langages typés dynamiquement, vous n'établissez généralement qu'une sorte de "Gentleman's agreement" que le code répondra à certaines méthodes, et si vos classes semblent parler le même langage, tout va bien. Les langages typés statiquement ont des mécanismes supplémentaires pour appliquer ces règles au moment de la compilation. En C #, l'utilisation d'un tel contrat vous permet de fournir des garanties d'adhésion au moins modérément utiles à ces interfaces, ce qui vous permet de regrouper des plugins et des substitutions avec un certain degré de garantie de similitude car vous compilez tous avec le même contrat. Dans Ruby ou Scheme, vous vérifiez ces accords en écrivant des tests qui fonctionnent au moment de l'exécution.
Ces garanties de temps de compilation présentent un avantage mesurable en termes de performances, car une invocation de méthode ne nécessite aucune double répartition. Afin d'obtenir ces avantages dans quelque chose comme Lisp, Ruby, JavaScript ou ailleurs, ce qui est maintenant des mécanismes encore un peu exotiques de compilation statique juste à temps de classes dans des machines virtuelles spécialisées est requis.
Une chose que l'écosystème C # a encore un support relativement immature est la gestion de ces dépendances binaires; Java a eu Maven pendant plusieurs années pour s'assurer que vous disposez de toutes les dépendances requises, tandis que C # a toujours une approche assez primitive de type MAKE qui implique de placer les fichiers stratégiquement au bon endroit à l'avance.