13.1. 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 briefly 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 that belong 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 (for example, xyz.doc) as long as they are located in different Windows folders (for example, 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; for example, C:MyDocsxyz.doc and D:Stuffxyz.doc.

To be absolutely certain that the compiler knows which class we want 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 specified in using directives. As an example, let's assume that we want 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, 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-1.

Figure 13.1. 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;
using System;

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 string is OK because of the using

// directive at the top of the code listing.
    string name = "Dinesh Prabhu";
    math.Professor = name;

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

    // etc.
  }
}

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

  • The System namespace, which includes the String, Console, and Array classes

  • The System.Collections.Generic namespace, which includes the List and Dictionary collection classes

  • The System.IO namespace, which includes the FileStream, StreamReader, and StreamWriter classes that we'll use to save and restore the data used by the SRS to/from files in Chapter 15

  • The System.Windows.Forms and System.Drawing namespaces, which include the GUI classes and support classes that we'll use in creating a GUI front-end for the SRS in Chapter 16

13.1.1. 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 used by Microsoft in creating/designing the .NET Framework:

  • To logically partition our classes to facilitate their reuse. For example, a rocket scientist might want to put all the classes she designed relating to planets into a Planets namespace for reuse by astronomers and all 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 want to design a class with the same simple name as a class found in one of the .NET namespaces (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 (for example, a class or 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 classes, interfaces, or other elements that are to be included in that particular namespace.

For example, if we were designing an application to manage the inventory of a pet store, we might want to create a namespace called PetStore that is to include classes representing different types of pets. To include a class representing a pet rat as part of the namespace, we might create a source code file named PetRat.cs as follows:

// PetRat.cs

// "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 standard .NET Framework namespace
						using AnimalFood;    // a different user-defined namespace,
                     // defined elsewhere

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

  // The PetRat class now becomes part of the PetStore namespace;
  // its fully qualified name is PetStore.PetRat.
  public class PetRat
  {
    // Fields.
    private string name;
    private 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;

    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. However, it is much more common for namespaces to span (be defined in) multiple files. For example, we might want to add a class representing a tarantula to the PetStore namespace. The class is implemented in a separate file named Tarantula.cs, as shown here:

// Tarantula.cs

// "using" directives for any OTHER namespaces required by the
// Tarantula class code are inserted here; details omitted.

// We're inserting the Tarantula class into the SAME namespace
// as the PetRat class.
namespace PetStore
						{
  // The Tarantula class now becomes part of the PetStore
  // namespace; it's fully qualified name is "PetStore.Tarantula".
  public class Tarantula
  {
    // Details omitted.
  }
}

Then, if we want 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:

// NamespaceDemo.cs

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

public class NamespaceDemo
{
  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.
  }
}

The compile commands don't change for source files that include a namespace definition. For example, if the NamespaceDemo.cs, PetRat.cs, and Tarantula.cs files were in the same directory, the application could be compiled using the following command:

csc NamespaceDemo.cs PetRat.cs Tarantula.cs

When dealing with applications that require a large amount of source files or that reuse files from an existing namespace, it is common practice to place the source files in separate folders or directories. If the PetRat.cs and Tarantula.cs files were placed in a folder named PetStore that existed in the folder that contained the NamespaceDemo.cs file, the compile command would be the following:

csc NamespaceDemo.cs PetStorePetRat.cs PetStoreTarantula.cs

13.1.2. 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. If two classes A and B reside in the global namespace:

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

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

  • Both classes will compile properly.

All without either class having to include a using directive to find the other because they both reside in the global namespace. To simplify things a bit when we start writing the SRS-related classes in Chapter 14, we won't make use of a user-defined namespace to house the SRS classes. Instead, we'll place them in the (default) global namespace. Thus, all the SRS classes will "know" each other, enabling us to write code such as the following without having to include using directives to qualify the simple names Student and Course for the compiler:

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

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset