 .NET 1.1.NET Namespaces
The sixteenth part of the C# Object-Oriented Programming tutorial describes the use of namespaces. Namespaces allow classes, structures and other items to be grouped and organised and remove the possibility of class-naming conflicts.
What is a Namespace?
As programming languages have evolved, the frameworks around them have greatly increased in size. The .NET framework provides a huge number of base classes, data types, algorithms, etc. If every one of these items had to be declared with a unique name, the naming task would be equally large and would leave very few good names available for the .NET developer.
To ensure that naming conflicts do not occur, either within the .NET framework or in your own programs, namespaces are used. A namespace simply provides a named group of classes, structures, enumerations, delegates, interfaces and other namespaces. Within the namespace, all declared items must be uniquely named. However, the same name may be duplicated in different namespaces. This means that if you decide to create a mathematics library you will be able to call it 'Math' even though Microsoft have also provided a Math library.
All definitions are created in a namespace, even if one is not explicitly declared. The .NET framework provides the default namespace, which is used automatically for any code that is written outside of any namespace declaration. This can be useful for short programs but has the drawback that all of the benefits that namespaces bring to organising your code are lost.
Declaring a Namespace
Syntax
Namespaces are declared using the namespace keyword. The basic syntax for creating a namespace is very simple, requiring only a name for the new grouping and a code block surrounded by braces:
namespace namespace-name {}
Namespace Example
To demonstrate the creation of a namespace we will create some code. To begin, create a new console application named "NamespaceDemo". If you are creating the project using Visual Studio, you will notice that the Program class that is automatically generated exists within a namespace that is also called "NamespaceDemo".
It is possible to define more than one namespace within the same code file. In this case, we will add our new namespace in the same file as the automatically created one. Add the following namespace and contained class as the foot of the code, outside of the code block of the existing namespace:
namespace FirstNamespace
{
class Test
{
public void ShowMessage()
{
Console.WriteLine("This is the first namespace!");
}
}
}
This code creates a new namespace named "FirstNamespace" and defines a "Test" class containing a single method that outputs a string to the console. We can now add a second namespace, again at the foot of the code:
namespace SecondNamespace
{
class Test
{
public void ShowMessage()
{
Console.WriteLine("This is the second namespace!");
}
}
}
The second namespace also includes a class named "Test". This does not cause a naming conflict because the two classes are logically separated by the namespaces. This means that the program will compile without error.
Additive Namespace Declarations
Namespace declarations are additive. This means that the same namespace definition can be included in your code in multiple locations. Where two or more matching declarations are made, the contents of the two namespaces are combined into one. This is especially useful when an application can be deployed in different versions as the optional code can be placed in separate code files. The files can then be "swapped in" to create the various build configurations.
Adding the following code will extend the "FirstNamespace" namespace, adding a second class:
namespace FirstNamespace
{
class ShortTest
{
public void ShowShortMessage()
{
Console.WriteLine("First namespace!");
}
}
}
Referencing a Class in Another Namespace
Fully Qualified Names
Namespaces prevent conflicts between the names of classes by isolating the contents of each namespace. In the example created above, code created in the "Program" class cannot access the classes in the "FirstNamespace" and "SecondNamespace" namespaces by simply referring to the Test class. If the Main method of the program is updated as follows, the code will not compile because no "Test" class can be found:
static void Main(string[] args)
{
Test t = new Test(); // Does not compile
}
To address this problem, the fully qualified name of the class can be used. The fully qualified name is a name that is unique across the entire application, including the classes of the .NET framework itself. The name is constructed by simply prefixing the class name with the namespace that it exists within. The two names are separated by a full-stop (or period) character. The following updated code adds the namespace and compiles successfully.
static void Main(string[] args)
{
FirstNamespace.Test t = new FirstNamespace.Test();
}
The Using Statement
If you were required to prefix every use of every class, structure, enumeration, etc. with the name of the namespace that contained it, your code would become unwieldy. To avoid this the using statement can be used. You will already have seen this statement in action at the start of almost every code file that you have created.
The using statement tells the compiler that a namespace is in use by the code. When the compiler finds a class name that is not recognised in the current namespace, it instead checks each namespace defined in a using statement to see if the item exists there. This means that the fully qualified name for items within such a referenced namespace need not be used.
To add a using statement to the code, the using keyword and the name of the namespace are added at the start of the code file. For example, to add a reference to the FirstNamespace namespace, add the following line at the start of the code:
using FirstNamespace;
Now that the compiler knows that it is allowed to check inside "FirstNamespace" to match class names, the Main method can be updated to make it easier to read:
static void Main(string[] args)
{
Test t = new Test();
}
Creating Aliases
The using statement as demonstrated above can provide much neater code. However, where two using statements are included and each referenced namespace contains a matching class name, a name conflict is created. To show this problem, add a second using statement to the code referencing the SecondNamespace namespace:
using SecondNamespace;
Now that both of the additional namespaces are referenced the compiler does not know which Test class to use when creating the object in the Main method. Attempting to compile the program generates an error that explains that "Test" is now an ambiguous reference.
This problem could be resolved by simply providing the fully qualified name when using the Test classes. However, namespace names can be very long and this presents the original problem of making the code more difficult to read. Instead, the using statement can be used to create an alias. This allows a shorter name to be linked to a namespace and to be used instead of the namespace to qualify the class.
To create an alias, the following syntax is used:
using alias-name = namespace;
Alias-name is the name of the alias as it will appear elsewhere in the code. Namespace is the namespace to create an alias for. To apply aliases for the two namespaces, modify the using statements as follows:
using First = FirstNamespace;
using Second = SecondNamespace;
Adding the aliases removes the ability to reference the classes without providing either the alias of the fully qualified name. To make it possible to compile the program again, modify the Test object declaration in the Main method to add an alias:
First.Test t = new First.Test();
Nested Namespaces
We have seen how classes and other items can be declared within a namespace to provide grouping of code items and isolation of names to prevent conflicts. In addition, namespaces may be created within other namespaces to create a nested categorisation. This allows for deep tree structures to be created and for great control over the organisation of code modules.
Consider the following nested namespace declarations:
namespace Parent
{
namespace Child
{
namespace Grandchild
{
class Test
{
public void ShowMessage()
{
Console.WriteLine("This is a nested namespace!");
}
}
}
}
}
In this situation there are three levels of namespace with each declared inside its parent. The "Test" class is declared within the third level of nesting. To access this class using its fully qualified name, all three namespaces must be specified with each prefixing its child and separated using a full stop. The fully qualified name for the Test class is therefore "Parent.Child.Grandchild.Test".
As with single layer namespaces, a using statement can be added to remove the requirement to use the fully qualified name. In this case, the using statement would be:
using Parent.Child.Grandchild;
A shorter notation exists for declaring a nested namespace when classes and other declarations only exist at the deepest level of the hierarchy. Rather than define each namespace individually, the namespaces can be created in a single statement, again with each namespace separated by a full stop. The following code is functionally equivalent to the previous nested namespaces sample:
namespace Parent.Child.Grandchild
{
class Test
{
public void ShowMessage()
{
Console.WriteLine("This is a nested namespace!");
}
}
}
Namespace Naming Conventions
To avoid conflicts with the .NET framework's namespaces and those of the many third-party component vendors, Microsoft has proposed a naming convention. The convention should be used for all of the namespaces that you create.
A suggested namespace should be of the format:
CompanyName.TechnologyName.Feature.Design
In this naming convention, CompanyName is the name of the company or brand that owns the code. For long company names, this should be reduced in size but standardised across the organisation. TechnologyName refers to the high-level technology that classes within the namespace relate to. For example, "Data" for database-related items or "Xml" for XML functionality.
Feature is an optional element that gives a name to a feature within a technology namespace. Finally, the namespace "Design" is suggested for namespaces that contain design-time functionality. An example namespace to contain the design-time elements of custom media controls for DVD movies could be:
BlackWasp.Media.Dvd.Design |