10

In implementations of the Scheme programming language (R6RS standard) I can import a module as follows:

(import (abc def xyz))

The system will try to look for a file $DIR/abc/def/xyz.sls where $DIR is some directory where you keep your Scheme modules. xyz.sls is the source code for the module and it is compiled on the fly if necessary.

The Ruby, Python, and Perl module systems are similar in this respect.

C# on the other hand is a little more involved.

First, you have dll files that you must reference on a per project basis. You must reference each one explicitly. This is more involved than say, dropping dll files in a directory and having C# pick them up by name.

Second, There isn't a one-to-one naming correspondence between the dll filename, and the namespaces offered by the dll. I can appreciate this flexibility, but it can also get out of hand (and has).

To make this concrete, it would be nice if, when I say this using abc.def.xyz;, C# would try to find a file abc/def/xyz.dll, in some directory that C# knows to look in (configurable on a per project basis).

I find the Ruby, Python, Perl, Scheme way of handling modules more elegant. It seems that emerging languages tend to go with the simpler design.

Why does the .NET/C# world does things in this way, with an extra level of indirection?

5
  • 10
    .NET's assembly and class resolution mechanism has worked just fine for over 10 years. I think you're lacking a fundamental misunderstanding (or not enough research) about why it's designed that way - e.g. to support assembly redirecting etc at bind time and many other useful resolution mechanisms
    – Kev
    Commented May 17, 2012 at 16:51
  • I'm pretty sure that DLL resolution from a using statement would break side-by-side execution. Also if there was a 1 to 1 you'd need 50 dlls for all the namespaces in mscorlib, or they'd have to drop the idea of namespaces Commented May 17, 2012 at 19:30
  • An extra level of indirection? Hmmmmm.... dmst.aueb.gr/dds/pubs/inbook/beautiful_code/html/Spi07g.html
    – user541686
    Commented May 17, 2012 at 22:25
  • @gilles Thanks for the editing and improving the question!
    – dharmatech
    Commented May 20, 2012 at 14:25
  • Some of your points are peculiar to Visual Studio, and comparing them to a language isn't quite fair (e.g., DLL references in Projects). A better comparison for the full .NET environment would be Java+Eclipse. Commented May 20, 2012 at 16:12

3 Answers 3

22

The following annotation in the Framework Design Guidelines Section 3.3. Names of Assemblies and Dlls offers insight into why namespaces and assemblies are separate.

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.

2
  • Seems like the most authoritative source so far. Thanks Conrad!
    – dharmatech
    Commented May 17, 2012 at 20:55
  • +1 for getting the damned-authoritative answer. But also, every "why does C# do <X>" question also needs to be viewed through the lens of "Java does <X> as follows: ...", because C# was (explicitly or not) a reaction to Sun's and Microsoft's differing agendas for Java on Windows. Commented May 20, 2012 at 16:07
7

It adds flexibility and allows to load the libraries (what you call modules in your question) on demand.

One namespace, multiple libraries:

One of the advantages is that I can easily replace one library by another. Let's say I have a namespace MyCompany.MyApplication.DAL, and a library DAL.MicrosoftSQL.dll, which contains all the SQL queries and other stuff which may be specific to the database. If I want the application to be compatible with Oracle, I just add DAL.Oracle.dll, keeping the same namespace. From now, I can deliver the application with one library for customers who need the compatibility with Microsoft SQL Server, and with the other library for customers who use Oracle.

Changing the namespace at this level would lead to either duplicate code, or the necessity to go and change all the usings inside the source code for each database.

One library, multiple namespaces:

Having several namespaces in one library is also advantageous in terms of readability. If, in a class, I use only one of the namespaces, I put just this one at the top of the file.

  • Having all the namespaces of a large library would be rather confusing both for the person who read the source code and for the writer herself, with Intellisense having too many things to suggest in a given context.

  • Having smaller libraries, one library per file would have a performance impact: each library must be loaded on demand in memory and processed by the virtual machine when running the application; less files to load means slightly better performance.

1
  • The second case does not require file names to be separate from namespaces (though allowing such separation makes the separation significantly easier), as a given assembly can easily have a many subfolders and files. Further, it is also possible to embed multiple assemblies into the same DLL (e.g., using ILMerge). Java works takes this approach.
    – Brian
    Commented May 18, 2012 at 15:13
3

It looks like you're choosing to overload the terminology of both "namespace" and "module." It shouldn't be any surprise that you see things as "indirect" when they don't fit your definitions.

In most languages that support namespaces, including C#, a namespace is not a module. A namespace is a way of scoping names. Modules are a way of scoping behavior.

In general, while the .Net runtime supports the idea of a module (with a slightly different definition than the one you are using implicitly), it's rather rarely used; I've only seen it used in projects built in SharpDevelop, mostly so that you could build a single DLL from modules built in different languages. Instead, we build libraries using a dynamically linked library.

In C#, namespaces resolve without any "layer of indirection" as long as they are all in the same binary; any indirection required is a responsibility of the compiler and linker that you don't have to give much thought to. Once you start building a project with multiple dependencies, you then reference external libraries. Once your project has made a reference to an external library (DLL), the compiler finds it for you.

In Scheme, if you need to load an external library, you have to do something like (#%require (lib "mylib.ss")) first, or use the foreign function interface directly, as I recall. If you're using external binaries, you have the same amount of work to resolve external binaries. Chances are you have mostly used libraries so commonly used that there's a Scheme-based shim that abstracts that from you, but if you ever have to write your own integration with a 3rd party library, you'll essentially have do some work to "load" the library.

In Ruby, Modules, Namespaces, and File Names are actually far less connected than you seem to assume; the LOAD_PATH makes things a bit complicated, and Module declarations can be anywhere. Python is probably closer to doing things the way you think you are seeing in Scheme, except that 3rd party libraries in C still add a (small) wrinkle.

Additionally, dynamically typed languages like Ruby, Python and Lisp typically don't have the same approach to "contracts" as statically typed languages. In dynamically typed languages, you usually only establish a sort of "Gentleman's agreement" that code will respond to certain methods, and if your classes appear to be speaking the same language, all is good. Statically typed languages have additional mechanisms to enforce these rules at compile time. In C#, using such a contract allows you to provide at least moderately useful guarantees of adherence to these interfaces, which allows you to bundle plugins and substitutions with some degree of guarantee of commonality because you all compile against the same contract. In Ruby or Scheme, you verify these agreements by writing tests that work at runtime. There are costs and benefits to either solution, but conflating the idea of a namespace, a module and a library won't help.

There is a measurable performance benefit from these compile time guarantees, as a method invocation requires no double dispatch. In order to get these benefits in something like Lisp, Ruby, JavaScript, or elsewhere, what are now still slightly exotic mechanisms of just-in-time statically compiling classes in specialized VMs is required.

One thing that the C# ecosystem still has relatively immature support for is the management of these binary dependencies; Java has had Maven for several years to deal with making sure you have all your requisite dependencies, whereas C# still has a fairly primitive MAKE-like approach that involves strategically placing files in the right place ahead of time.

3
  • 1
    Regarding the management of dependencies you might want to take a look at NuGet. Here's a nice article about it from Phil Haack Commented May 17, 2012 at 20:46
  • In the R6RS Scheme implementations I've used (Ikarus, Chez, and Ypsilon, for example) dependencies are handled automatically, based on the library imports. The dependencies are found and if necessary compiled and cached for future imports.
    – dharmatech
    Commented May 17, 2012 at 21:00
  • Familiar with Nuget, and thus my comment that it's "relatively immature"
    – JasonTrue
    Commented May 17, 2012 at 22:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.