Namespaces
As you have seen, namespaces provide a way of
organizing related classes and other types. Unlike a file or a
component, a namespace is a logical, rather than a physical,
grouping. When you define a class in a C# file, you can include it
within a namespace definition. Later, when you define another class
that performs related work in another file, you can include it
within the same namespace, creating a logical grouping that gives
an indication to other developers using the classes how they are
related and used:
Placing a type in a namespace effectively gives
that type a long name, consisting of the type’s namespace as a
series of names separated with periods (.), terminating with the name of the class. In the
preceding example, the full name of the Subscriber struct is CustomerPhonesiteApp.Subscriber. This allows
distinct classes with the same short name to be used within the
same program without ambiguity.
You can also nest namespaces within other
namespaces, creating a hierarchical structure for your types:
Each namespace name is composed of the names of the
namespaces it resides within, separated with periods, starting with
the outermost namespace and ending with its own short name. So the
full name for the ProCSharp namespace is
Wrox.ProCSharp, and the full name of
NamespaceExample class is Wrox.ProCSharp.Basics.NamespaceExample.
You can use this syntax to organize the namespaces
in your namespace definitions too, so the previous code could also
be written as follows:
Note that you are not permitted to declare a
multipart namespace nested within another namespace.
Namespaces are not related to assemblies. It is
perfectly acceptable to have different namespaces in the same
assembly or to define types in the same namespace in different
assemblies.
The using Statement
Obviously, namespaces can grow rather long
and tiresome to type, and the ability to indicate a particular
class with such specificity may not always be necessary.
Fortunately, as noted at the beginning of the chapter, C# allows
you to abbreviate a class’s full name. To do this, you list the
class’s namespace at the top of the file, prefixed with the
using keyword. Throughout the rest of
the file, you can refer to the types in the namespace simply by
their type names.
As remarked earlier, virtually all C# source code
will start with the statement using
System; simply because so many useful classes supplied by
Microsoft are contained in the System
namespace.
If two namespaces referenced by using statements contain a type of the same name,
you will have to use the full (or at least, a longer) form of the
name to ensure that the compiler knows which type is to be
accessed. For example, say classes called NamespaceExample exist both in the Wrox.ProCSharp.Basics and Wrox.ProCSharp.OOP namespaces. If you then create a
class called Test in the Wrox.ProCSharp namespace, and instantiate one of the
NamespaceExample classes in this class,
you need to specify which of these two classes you’re talking
about:
Because using statements
occur at the top of C# files, in the same place that C and C++ list
#include statements, it’s easy for
programmers moving from C++ to C# to confuse namespaces with
C++-style header files. Don’t make this mistake. The using statement does no physical linking between
files, and C# has no equivalent to C++ header files.
Your organization will probably want to spend
some time developing a namespace schema so that its developers can
quickly locate functionality that they need and so that the names
of the organization’s homegrown classes won’t conflict with those
in off-the-shelf class libraries. Guidelines on establishing your
own namespace scheme along with other naming recommendations are
discussed later in this chapter.
Namespace Aliases
Another use of the using keyword is to assign aliases to classes and
namespaces. If you have a very long namespace name that you want to
refer to several times in your code but don’t want to include in a
simple using statement (for example, to
avoid type name conflicts), you can assign an alias to the
namespace. The syntax for this is:
The following example (a modified version of the
previous example) assigns the alias Introduction to the Wrox.ProCSharp.Basics namespace and uses this to
instantiate a NamespaceExample object,
which is defined in this namespace. Notice the use of the namespace
alias qualifier (::). This forces the
search to start with the Introduction
namespace alias. If a class called Introduction had been introduced in the same scope,
a conflict would happen. The :: operator
allows the alias to be referenced even if the conflict exists. The
NamespaceExample class has one method,
GetNamespace(), which uses the
GetType() method exposed by every class
to access a Type object representing the
class’s type. You use this object to return a name of the class’s
namespace: