Table of contents >> Objects > Namespaces
High importance article

In OOP (Object Oriented Programming), namespaces are containers for a group of classes or other structures that 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? And, no, we cannot use the same class, one is made of flesh and has muscles, the other is made up of metal and contains motors.

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:

1
2
3
4
5
6
7
8
9
10
11
12
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace MyProject
{
    class MyClass
    {
    }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace MyProject
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = new MyClass();
        }
    }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace MyProject
{
    class Program
    {
        static void Main(string[] args)
        {
            MyProject.MyClass myClass = new MyProject.MyClass();
        }
    }
}

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:

1
2
3
4
5
6
7
8
9
10
11
12
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace MyNameSpaceName
{
    class MyClass
    {
    }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace MyProject
{
    class Program
    {
        static void Main(string[] args)
        {
            MyProject.MyClass myClass = new MyProject.MyClass();
        }
    }
}

or this piece of code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace MyProject
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = new MyClass();
        }
    }
}

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, which is still 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace MyProject
{
    class Program
    {
        static void Main(string[] args)
        {
            MyNameSpaceName.MyClass myClass = new MyNameSpaceName.MyClass();
        }
    }
}

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 use 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 use 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 (this is also one of the primary reasons for using 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections.Generic;
 
namespace MyNameSpaceName
{
    class MyClass
    {
        System.Collections.Generic.List<string> myList = new System.Collections.Generic.List<string>();
    }
}
 
namespace System
{
    namespace Collections
    {
        namespace Generic
        {
            public class List<T>
            {
            
            }
        }
    }
}

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:

1
System.Collections.Generic.List<string> myList = new System.Collections.Generic.List<string>();

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

1
2
3
4
5
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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 one of those classes in the other class, because the 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:

1
2
3
4
5
6
7
8
9
10
11
namespace MyProject
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Int32 myInt = 3;
            System.String myString = "this is a fully qualified path string declaration";
        }
    }
}

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:

1
2
3
4
5
6
7
8
9
10
namespace MyProject
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Console.WriteLine("Test");
        }
    }
}

but we always write it like this:

1
2
3
4
5
6
7
8
9
10
11
12
using System;
 
namespace MyProject
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Test");
        }
    }
}

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 using System; 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:

1
2
3
using System.Windows.Forms;
using System.Web.UI;
using System.Windows.Controls;

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 have 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 are referring to.

Finally, keep in mind that using directives are NOT recursive! Using the instruction using System.Windows; does not imply the import of System.Windows.Forms, althought the namespace System.Windows.Forms is declarred inside namespace System.Windows.

When you have long or freqently used namespaces, C# offers you namespace aliases. They can help improve code readability and reduce the risk of naming conflicts, especially when you're working with multiple libraries that have similar type names. To create a namespace alias, you use the using keyword, followed by an alias name, an equals sign (=), and the fully qualified namespace you want to alias.

For example, imagine you have the following namespaces:

1
2
using System.Data.SqlClient;
using System.Data.SQLite;

Both namespaces contain a class named Command. To avoid ambiguity and improve readability, you can create namespace aliases like this:

1
2
using SqlClient = System.Data.SqlClient;
using SQLite = System.Data.SQLite;

Now, when you need to reference the Command class from either namespace, you can use the alias:

1
2
SqlClient.Command sqlCommand = new SqlClient.Command();
SQLite.Command sqliteCommand = new SQLite.Command();

In addition to namespace aliases, C# also provides the namespace alias qualifier, or "::" operator. The namespace alias qualifier is used to access types or members within a namespace that has been aliased.

For instance, given the same namespace aliases as in the previous example:

1
2
SqlClient::Command sqlCommand = new SqlClient::Command();
SQLite::Command sqliteCommand = new SQLite::Command();

This syntax makes it clear that you're accessing the Command class through a namespace alias, rather than a regular namespace. However, it's important to note that the namespace alias qualifier is less commonly used in C# code, as the standard dot (.) notation is typically preferred for readability. The "::" operator can be helpful in cases where you need to differentiate between an alias and a type or member with the same name.

C# 10 introduced the concept of file-scoped namespaces, which is a more concise way to declare a namespace, that encompasses the entire file. File-scoped namespaces can help reduce the level of indentation in your code and improve readability. So, this:

1
2
3
4
5
6
7
8
using System;
 
namespace HelloWorld
{
    class Program
    {
    }
}

becomes this:

1
2
3
4
5
6
7
using System;
 
namespace HelloWorld;
 
class Program
{
}

Notice the semicolon after the namespace declaration. This change allows the HelloWorld definition to exist at the top level, without any additional indentation. The file-scoped namespace applies to all the types and members defined within the file where it's declared.

Keep in mind that you cannot mix file-scoped and block-scoped namespaces in the same file. If you choose to use file-scoped namespaces, all namespaces in that file must follow the same convention.

C# 10 also introduced the concept of implicit usings, also known as global usings. This feature allows for the automatic inclusion of common using directives in your project without having to explicitly write them in each file. This can lead to cleaner code and improved readability, especially in large projects.

To use implicit usings, of course, you need to use at least C# version 10, but you also need to create a new file, typically named Usings.cs or GlobalUsings.cs in your project. Inside this file, you can declare the using directives you want to apply globally, prefixed with the global keyword:

1
2
3
global using System;
global using System.Collections.Generic;
global using System.Linq;

After this, you don't need to include the specified using directives in any other files in your project, as they are implicitly included. However, you can still add explicit using directives in individual files, if needed.

Keep in mind that while implicit usings can simplify your code and make it more readable, they may also increase the risk of naming conflicts, as I explained earlier. As a general rule, be cautious when including many global usings, as you may accidentally introduce ambiguous references to types or namespaces.

Another downside effect might be the introduction of magic in your codes, meaning, things work, though, apparently, they shouldn't. You use Console.WriteLine() in your codes, but there is no using System; at the top of your file. The real reason is, of course, that you use it in a global using, but a beginner, or someone unfamiliar with this concept, might have a hard time understanding how this works. Magic might be wonderful in real life, but it's bad in programming. The more obvious things work, the better. The less detective work you need to do in order to understand how the code works, the better.

Still related to namespaces (and not only), in the next lesson I will discuss the concept of top level statements, where you don't even need a namespace or a class, and why this is bad.