Saturday, April 20, 2024 04:09

Table of contents >> Objects > Namespaces

Namespaces

In OOP, namespaces are containers for a group of classes which have a common context or are categorized by common functionality. Namespaces do not have any kind of functionality, and it is not mandatory to use them. However, they offer a few advantages, most notably being the ability of sorting and grouping your code into logical units.

Let’s consider the following example: we want to create two objects in code: a robot and a human. Quite easy thing to do, until we discover that we created the class Hand for our human, but we cannot create another class Hand for the robot, because of a name conflict. The project already contains a class with that name. How would it be able to tell which Hand class did we intend to reference to – the human, or the robot one?

Though this is not mandatory in C#, it is considered a good practice that classes should have the name of the files containing them, and namespaces should have same name as the folders in which they reside. In Java, this is even a mandatory process, you will not be able to compile your code if you don’t obey this rule.

We have worked with namespaces before, in fact we saw them in each of our lessons, because Visual Studio adds them automatically when we create a new solution, project or file. You can test this by adding a new class to your project, as we did here. If you analyze the code, you will notice that if you added a class named MyClass, in a project named MyProject, the code of this class will look like this:

And while we instantiate this new class, we can do it directly like this:

What you don’t know is that the fully qualified name (the entire address) of the class is actually this:

because MyClass was declared inside the MyProject namespace, hence its entire address is MyProject.MyClass. The reason why the compiler even lets us to instantiate it without specifying the namespace is because we are instantiating it in another class, also declared in the same namespace. And since a namespace can only contain a single class with a given name, it is not necessary to specify the namespace when we are using that class name, the compiler already knows that there can be only one class with that name inside itself.

Now, let’s declare our new class in another namespace. Modify the name of the namespace as following:

As you can see, we didn’t change much of the code, we only renamed our namespace. But now, this piece of code:

or this piece of code:

will generate a compiler error: The type or namespace name ‘MyClass’ does not exist in the namespace ‘MyProject’ (are you missing an assembly reference?). This happens because we renamed the namespace in which MyClass is declared, and when we try to instantiate it inside the Program class declared inside the MyProject namespace, the compiler sees that there is no class named MyClass declared inside a namespace called MyProject.

To get rid of this error, we need to change the namespace address:

You should always remember that when calling a class declared in another namespace other than the one where you are making the call, you must use fully qualified names (including the namespace). In simple words, if you have a namespace A, containing classes x and y, x can call y directly, just by the class name; but if you have two namespaces, A containing class x, and B containing class y, class x can only call class y by also specifying the namespace in which y lives, and vice-versa.

You should also always remember that classes are required to have unique names only within the namespaces in which they are defined. We can have two classes with the same name, as long as they are declared in different namespaces.

Namespaces can also be nested. This is an easy way to create a logical hierarchy, which allows an even more precise distribution of classes. The path of the nested namespaces is specified using the . (dot) operator. Lets consider following example:

We have declared a namespace System, which contains a namespace Collections, which contains a namespace Generic, which contains a generic class named List. Then, we have declared another namespace called MyNameSpaceName, containing a class named MyClass, inside which we want to instantiate the List class we previously declared.

As I said above, because the class we wish to instantiate and the class where we wish to instantiate, are in separate namespaces, we need to specify the fully qualified name:

Do you start to see the meaning of these long names?

Yes, you are right; those are namespaces and nested namespaces. It is also the right time to explain the using directives.

I already said that when declaring two classes inside the same namespace, we don’t need to specify the name of the namespace when we instantiate on of those classes in the other class, because compiler already assumes we mean the class declared in the same namespace. However, if the class is declared in another namespace, we need to use the fully qualified name. Now, consider this code:

Do you notice the difference to the usual way our codes look like? First off, we don’t have any using directives at the top of our file. Second, we used the fully qualified path for the int and string variable declarations. From these two modifications, we can draw the following conclusion: using directives are only used so we can use the short notation of types and classes.

Additional Information


You may have noticed the usage of System.Int32 and System.String in the fully qualified names, instead of System.int and System.string. This is because int and string are actually just aliases for System.Int32 and System.String, as explained here

The fully qualified path of the Console class is System.Console. So, when we want to write something on the console, we should write it like this:

but we always write it like this:

The using keyword states that the program is using the names in the given namespace, in our case, System. And because Console class is declared inside System namespace, we don’t need to specify the entire path, because the

line already told our program that when we are calling a class, it should look for the name of that class inside the namespace System too.

There is a consequence of this, though. Consider following code:

Each of the above namespaces contains a class named Control. Even if we imported those namespaces with the help of the using keyword, how would the compiler know to which one we referred to, if we wanted to instantiate the Control class using the short notation? It wouldn’t, it would actually display this error: ‘Control’ is an ambiguous reference between ‘System.Windows.Controls.Control’ and ‘System.Windows.Forms.Control’. In the case where you are having classes with same names, even if you use using statements, you need to specify the fully qualified name, so that the compiler knows to which class you referred to.

Finally, keep in mind that using directives are NOT recursive! Using the instruction

does not imply the import of System.Windows.Forms, althought the namespace System.Windows.Forms is declarred inside namespace System.Windows.

 

EXERCISES
1. Define your own namespace Animals and place in it two classes, Cat and Dog. Define one more namespace and make a class which calls the classes Cat and Dog inside it.

Solution


Make a new project in Visual Studio, right click on the folder and choose the menu Add → New Folder. Then enter the name of the folder and press [Enter], right click on the newly made folder and choose Add → New Item…. From the list choose Class, and for the name of the new class enter Cat and press [Add]. Change the namespace name to Animal. Make the same to the class Dog.

2. Rewrite the following code, making sure you do not use any using directive:

Solution


Guidelines: Use fully qualified names for all the types and classes. Delete the using statements at the top of the file.

Tags: , ,

Leave a Reply



Follow the white rabbit