Team LiB
Previous Section Next Section

Namespaces

Throughout the examples in Parts One and Two of this book, and in most of the examples to follow, we've commonly placed a using statement (more formally referred to as a using directive) at the top of our programs to allow us to access the elements of a particular namespace by their simple names. By way of review, a namespace is a logical grouping of related programming elements, as was discussed in Chapter 1; the .NET Framework libraries are so vast that namespaces are used to divide the libraries up into more manageable sublibraries.

A simple name is the name of the class as it appears in the class declaration; for example:

// This class has the simple name "Student".
public class Student {
  // Details omitted.
}

When a class is placed inside a namespace, its name "changes" in that it acquires its namespace as part of its fully qualified name. For example, as discussed earlier in the book, because the String class is contained in the System namespace, the fully qualified name of the class is System.String; the simple name of the class remains String.

It's conceivable that two classes, belonging to two different namespaces A and B, could be given the same simple name X, just as, by way of analogy, it's possible to create two different Microsoft Word documents with the same name—e.g., xyz.doc—as long as they are located in different Windows folders—e.g., C:\MyDocs and D:\Stuff. When we fully qualify the names of such like-named classes—A.X and B.X—these names are guaranteed to be unique, just as in the Word document analogy, the two like-named documents in our example would have different fully qualified file names, e.g., C:\MyDocs\xyz.doc and D:\Stuff\xyz.doc.

To be absolutely certain that the compiler knows which class we wish to use in any given situation, we could always use fully qualified class names in our program:

// Note that we've provided no "using" directives
// with this program.

public class SimpleProgram3
{
  static void Main() {
    System.String name = "Jackson";
    System.Console.WriteLine("The name is " + name);
  }
}

Having to type the fully qualified name of every namespace member that we're using in a program is cumbersome, however, and makes for less readable code. Fortunately, the C# language provides the using directive to afford us the convenience of accessing the members of a namespace using the members' simple names.

As we've seen numerous times before, a using directive is placed at the top of a source code file for every namespace whose members are to be accessed within that file; we're then free to refer to the classes of interest by their simple names throughout the code in this source file:

// We plan on using the "Console" class from the System namespace.
using System;

// We plan on using the "Foo" class from a DIFFERENT namespace
// named "BarStuff".
using BarStuff;

public class SimpleProgram3
{
  static void Main() {
    // We may now refer to Foo and Console by their simple names.
    Foo x = new Foo();
    Console.WriteLine("A Foo is born every minute!");
  }
}

The compiler will search each of the specified namespaces in turn to ensure that it can find declarations of Console and Foo in one or the other of them.

A small problem arises if a class name that we're referring to in our code exists in more than one of the namespaces that we've specified in using directives. As an example, let's assume that we wish to use two different versions of a class called Course in the same program, one that is defined by the SRS namespace and another that is defined by the ObjectStart namespace. Even if we were to provide using directives for these two namespaces, we would still have to fully qualify each use of the Course class names to disambiguate the situation:

// Example.cs
using ObjectStart;
using SRS;
public class Example
{
  static void Main() {
    // Use the SRS version of Course here ...
    SRS.Course math = new SRS.Course();

    // ... and the ObjectStart version here.
    ObjectStart.Course english = new ObjectStart.Course();
    // etc.
  }
}

Thus, there is no point in providing using directives for namespaces SRS or ObjectStart in this particular case.

Of course, if we wanted to use additional uniquely named classes from either the SRS or ObjectStart namespaces that we weren't planning on fully qualifying, then such using directives would be helpful. Say, for example, that we were not only using the two versions of Course as discussed previously, but also using a class named Student that exists in the SRS namespace but not in ObjectStart, and conversely, a class named Professor that exists in the ObjectStart namespace but not in SRS, as illustrated in Figure 13-7.

Click To expand
Figure 13-7: Course exists in two namespaces; Professor and Student do not.

We'll need to fully qualify references to the name Course once again, but won't have to fully qualify Student or Professor if we include using directives in our program as illustrated in the following code:

using SRS;
using ObjectStart;
public class Example
{
  static void Main() {
    // We are still qualifying Course wherever we use it in
    // this program ...
    SRS.Course math = new SRS.Course();
    ObjectStart.Course english = new ObjectStart.Course();

    // ... but simple name Professor is OK because of the using directive
    // at the top of the code listing.
    Professor p = new Professor();
    math.AssignProfessor(p);

    // Ditto for Student.
    Student s = new Student();

    // etc.
  }
}

We'll use predefined classes from five of the .NET FCL namespaces in developing the SRS application in Chapters 14 through 16:

Programmer-Defined Namespaces

The C# language also gives us the ability to create our own namespaces. The reasons we might want to do so are the same reasons that were used by Microsoft in creating/designing the .NET FCL:

  • To logically partition our classes so as to facilitate their reuse. For example, a rocket scientist might want to put all of the classes she designed relating to planets into a Planets namespace for reuse by astronomers, and all of the classes she designed relating to rocket design in a Rockets namespace for reuse by rocket manufacturers.

  • To ensure unique fully qualified names for our user-defined classes. For example, if we were to want to design a class with the same simple name as a class found in one of the .NET namespaces—say, perhaps a class called Console—then by putting it into a namespace of our own creation— say, perhaps ObjectStart—we'd facilitate use of both the System.Console and ObjectStart.Console classes in the same program.

To assign a particular programming element (e.g., class, interface) to a namespace, we place the namespace keyword followed by the name that we're inventing for the namespace (observing Pascal casing conventions) at the top of the code listing, followed by a pair of braces {} enclosing the declarations of one or more programming elements that are to be included in that particular namespace.

For example, if we wanted to create a namespace called PetStore that is to include classes representing different types of pets, we might create a source code file named PetRat.cs as follows:

// PetRat.cs

// Simply by using "PetStore" in a namespace declaration, the PetStore
// namespace is born!
namespace PetStore
{
  // Every class or interface declared within this namespace code block
  // becomes part of the PetStore namespace.

  // "using" directives for any OTHER namespaces required by the code
  // that follows are inserted here. In this example, we are using two such
  // classes -- Console and Seed -- which come from the System and AnimalFood
  // namespaces, respectively.
  using System;         // a Framework Class Library (FCL) namespace
  using AnimalFood;     // a different user-defined namespace,
                          // defined elsewhere
  // The PetRat class now becomes part of the PetStore namespace; its fully
  // qualified name is "PetStore.PetRat".
  public class PetRat
  {
    // Fields.
    string name;
    string coatColor;

    // Seed is a class in the AnimalFood namespace, but since we've
    // included a "using AnimalFood" directive above, we may
    // reference the Seed class by its simple name.
    Seed favoriteSeedType;

    // Properties for all three fields are assumed to exist, but
    // are omitted from this example.

    public void DisplayRatInfo() {
      // The "using System" directive above enables us to refer to the
      // Console class by its simple name.
      Console.WriteLine("Rat's Name: " + Name)
      Console.WriteLine("Coat Color: " + CoatColor);
      Console.WriteLine("Favorite Seed Type: " +
            FavoriteSeedType.Name);
      // etc.
    }
  }

  // Other classes/interfaces to be inserted into PetStore could be defined
  // here, if desired, or in separate source files (preferred).
} // end of namespace declaration

The PetRat class would thus be assigned to the PetStore namespace.

Then, if we had a second class—say, Tarantula—that we also wanted to include in the same PetStore namespace, we could do so in the same file or, preferably, in a separate file named Tarantula.cs, as shown here:

// Tarantula.cs

// We're inserting the Tarantula class into the SAME namespace
// as the PetRat class.
namespace PetStore
{
  // "using" directives for any OTHER namespaces required by the Tarantula
  // class code are inserted here; details omitted.

  // The Tarantula class now becomes part of the PetStore namespace; its fully
  // qualified name is "PetStore.Tarantula".
  public class Tarantula
  {
    // Details omitted.
  }
}

Then, if we wish to access either the PetRat or Tarantula class by its simple name from client code, we'd insert a using PetStore; directive at the top of that code:

// Example.cs

using PetStore;
// Any other using directives required by the Example program would
// be inserted here, as well ...

public class Example
{
  static void Main() {
    // We're able to use the simple name "PetRat" here ...
    PetRat r = new PetRat();
    r.Name = "Baby Grode";

    // ... and the simple name "Tarantula" here.
    Tarantula t = new Tarantula();
    t.Name = "Fuzzy";

    // etc.
  }
}

Having provided the using PetStore directive, the compiler will be able to find the definitions of the PetRat and Tarantula classes in the PetStore namespace.

The Global Namespace

One final point about namespaces is that if we don't include an explicit namespace directive in a source file, the class or interface that we're defining in that source file will be assigned to the "nameless" global namespace. As long as the source code (.cs) files for two classes A and B reside in the same working directory

  • A can make references to B using B's simple name, and

  • B can make references to A using A's simple name, and

  • Both classes will compile properly

all without either class having to include a using directive to find the other, because they are both coresident in the global namespace.

Given that

  • All of the code that we write for the SRS application will be housed in the same working directory, and

  • None of the SRS-related classes—Student, Professor, SRS, etc.,—includes a namespace directive in their respective definitions so as to "insert themselves" into a particular named namespace,

then all of the SRS-related classes will fall within the global namespace. This is what enables us to write code such as

public class SRS
{
  static void Main() {
    Student s = new Student();    // Using a simple name.
    Course c = new Course();      // Ditto.
    // etc.
  }
}

without having to include using directives to qualify the simple names Student and Course for the compiler.


Team LiB
Previous Section Next Section