Chapter 13
Namespaces

What’s in This Chapter

  • Namespace collisions
  • The using directive
  • Project and item templates
  • Making and resolving namespaces
  • Default and global namespaces

Wrox.com Downloads for This Chapter

Please note that all the code examples for this chapter are available as a part of this chapter’s code download on the book’s website at www.wrox.com/go/csharp5programmersref on the Download Code tab.

In large applications, name collisions are fairly common. A developer working on a billing system might create a Customer class. Meanwhile another developer working on a customer complaint tracking system might define a different Customer class. Each class will have different properties, methods, and events that are useful for its application.

Having two classes with the same name like this won’t cause any problems until you try to integrate the two programs. At that point, the program won’t be able to tell which kind of Customer class to use under different circumstances.

This situation in which multiple items have the same name is called a namespace collision or namespace pollution.

Namespaces enable you to group code so that you can tell the program where to find a particular class. For example, the developer working on the billing system might use the Billing namespace and the developer working on the complaint tracking system might use the CustomerSatisfaction namespace.

Now when you glue code from the two programs together in one mega-program, you can refer to the two classes as Billing.Customer and CustomerSatisfaction.Customer and the two classes can peacefully coexist.

Namespaces can contain other namespaces, so you can build a hierarchical structure that groups different entities. You can divide the Billing namespace into pieces such as OverdueAccounts and PaidAccounts to give developers working on that project some isolation from each other.

Namespaces can be confusing at first, but they are fairly simple. They just group the code in manageable pieces so that you can tell different chunks of code apart from each other.

This chapter describes namespaces. It explains how to use namespaces to categorize programming items and how to use them to select the right versions of items with the same name.

Collisions in .NET

Name collisions are uncommon in the .NET Framework because the designers were careful to give classes with similar purposes different names. If no two classes have the same name, then there’s no problem.

For example, the System.Data namespace includes several subnamespaces such as Odbc and OleDb to work with different kinds of databases (in this case, ODBC and OLE DB databases). Those namespaces contain similar classes, but their names are prefixed with the namespace name so that they don’t conflict. For example, those namespaces contain the OdbcConnection and OleDbConnection classes.

However, there are a few cases in which classes have exactly the same names. For example, the System.Windows.Forms, System.Windows.Controls, and System.Web.UI.WebControls namespaces all define Label, TextBox, and Button classes.

Those namespaces represent different ways of building user interfaces (Windows Forms, WPF/XAML, and web page) so it is quite unusual for a program to include more than one of those kinds of controls. If you ever do, however, you’ll need to use namespaces to indicate which you are using in different parts of the code.

The using Directive

Visual Studio defines thousands of classes, constants, and other entities to provide tools for your applications. It categorizes them in namespaces to prevent name collisions and to make it easier for you to find the items you need.

The .NET Framework root namespaces are named Microsoft and System.

The Microsoft namespace includes namespaces that support different programming languages and tools. For example, typical namespaces include CSharp, JScript, and VisualBasic, which contain types and tools that support the C#, JScript, and Visual Basic languages. The Microsoft namespace also includes the Win32 namespace, which includes classes that handle operating system events and that manipulate the registry.

The System namespace contains a huge number of useful programming items, including many nested namespaces. For example, the System.Drawing namespace contains classes related to drawing; System.Data contains classes related to databases; System.Threading holds classes dealing with multithreading; and System.Security includes classes for working with security and cryptography.

Note that these namespaces are not necessarily available to your program at all times. For example, by default, the Microsoft.JScript namespace is not available to C# programs. To use it, you must first add a reference to the Microsoft.JScript.dll library.

Visual Studio includes so many programming tools that the namespace hierarchy is truly enormous. Namespaces are refined into subnamespaces, which may be further broken into more namespaces until they reach a manageable size. Although this makes it easier to differentiate among all the different programming entities, it makes the fully qualified names of some classes rather cumbersome.

For example, the following code draws a rectangle with a dashed border. Notice the long series of namespaces used by the Dash enumeration value (highlighted in bold).

private void Form1_Paint(object sender, PaintEventArgs e)
{
    using (Pen pen = new Pen(Color.Blue))
    {
        pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
        e.Graphics.DrawRectangle(pen, 10, 10, 100, 50);
    }
}

You can use a using directive at the top of the file to make using namespaces easier. For example, suppose the program begins with the following statement.

using System.Drawing.Drawing2D;

Now the program can use the following simpler code to draw the dashed rectangle.

private void Form1_Paint(object sender, PaintEventArgs e)
{
    using (Pen pen = new Pen(Color.Blue))
    {
        pen.DashStyle = DashStyle.Dash;
        e.Graphics.DrawRectangle(pen, 10, 10, 100, 50);
    }
}

The using directive tells the compiler where to look for classes, structures, enumerations, and other items that are not defined locally. When it sees the value DashStyle.Dash, the compiler tries to locate DashStyle in the current file. When it doesn’t find it, the compiler searches the namespaces listed in using directives. In this example, it eventually finds DashStyle in the System.Drawing.Drawing2D namespace.

If a program contains two using directives for namespaces that define classes with the same names, C# may become confused and give you an Ambiguous Reference error. To fix the problem, the code must use fully qualified names to select the right versions.

For example, the following code creates two Customer objects, one using the class defined in the Billing namespace and one using the class defined in the CustomerSatisfaction namespace.

Billing.Customer customer1 = new Billing.Customer();
CustomerSatisfaction.Customer customer2 = new CustomerSatisfaction.Customer();

Sometimes, including a fully qualified namespace can make the code unwieldy and hard to read. In this example, the CustomerSatisfaction namespace makes the second statement so long it barely fits on a single line. In other cases, such as with the System.Drawing.Drawing2D.DashStyle.Dash value used earlier, deeply nested namespaces makes the code awkward.

In addition to including a namespace for the compiler to use, the using directive can define an alias to make using long namespaces easier. For example, the following using directives define aliases for the System.Drawing.Drawing2D and CustomerSatisfaction namespaces.

using D2D = System.Drawing.Drawing2D;
using CS = CustomerSatisfaction;

The following code shows how you could use those aliases.

pen.DashStyle = D2D.DashStyle.Dash;
CS.Customer customer2 = new CS.Customer();

Project Templates

When you create a new project, Visual Studio includes whatever using directives are defined by the project’s template. The included directives were chosen by the Microsoft development team because it thought those directives would be useful under many common situations, but the results may not suit your needs.

If the template doesn’t do exactly what you need, it’s easy to add a few using directives manually. It’s even easier to remove unwanted directives by using the Remove Unused Usings context menu command. (See the tip “Overusing Using” earlier in this chapter.)

However, if you build a large number of projects or add a lot of modules that need the same directives, you can make the process a bit easier by defining your own templates.

To create a project template, start a new project. Add any forms, windows, or other modules that you want the template to have, and edit them so they contain the wanted using directives. When you have the project the way you want it, use the File menu’s Export Template command to open the Export Template Wizard, as shown in Figure 13-1.

To create a project template, select the Project Template option, and click Next to display the page shown in Figure 13-2. Enter a name and description for the template. If you want, you can also define an icon for Visual Studio to display and a preview image. When you finish, click Finish to create the template.

c13f001.tif

Figure 13-1: The Export Template Wizard enables you to build project or item templates.

c13f002.tif

Figure 13-2: Specify a name and description for the new template. You can also define an icon and preview image if you like.

Now when you start a new project, the New Project dialog includes your template, as shown in Figure 13-3.

c13f003.tif

Figure 13-3: The New Project dialog includes your templates.

When you create a new project based on your template, the new project includes whatever files you included in the template, and those files contain the using directives that you included.

Item Templates

When you add a new form or other modules to a project, Visual Studio uses an item template to determine what code and what using directives are added. Just as you can define new project templates, you can define new item templates.

For example, create a form that includes the using directives you want. Then select the File menu’s Export Template command to display the Export Template Wizard as before. On the wizard’s first page, select Item Template and click Next to display the page shown in Figure 13-4.

Check the parts of the program that you want to include in the template and click Next to display the page shown in Figure 13-5.

c13f004.tif

Figure 13-4: An item template lets you export various pieces of a program such as forms or settings.

c13f005.tif

Figure 13-5: An item template lets you include references to system libraries.

Check the references that you want to include, and click Next to display the page shown in Figure 13-2. Enter the template name and description as before, and click Finish to create the template.

Now when you select the Project menu’s Add New Item command, you can select the item you defined.

The Default Namespace

Every project has a default namespace, and every item in the project is contained directly or indirectly within that namespace.

Initially, the default namespace has the same name as the project. For example, if you create a project named OrderExplorer, then every module’s code is initially contained in the OrderExplorer namespace.

To view or change the project’s default namespace, use Project ⇒ Properties to open the project’s property pages, and select the Application page, as shown in Figure 13-6. You can view and change the default namespace in the Default Namespace text box.

c13f006.tif

Figure 13-6: Use the Application property page to change an application’s default namespace.

If you change the default namespace, any modules you add to the project in the future use that namespace.

Making Namespaces

You can create new namespaces nested within the default namespace to further categorize your code. Simply add a namespace statement to the code.

You can create a namespace outside of any other namespace. You can also place a namespace inside another namespace at the top level (not inside any class, structure, or other code item). For example, consider the following code.

namespace OrderExplorer
{
    public class Person
    {
        public CustomerData.Customer customer;
    }

    namespace CustomerData
    {
        public class Customer : Person
        {
            public Person person;
        }
    }
}

namespace DrawingTools
{
    public class Shape
    {
        public OrderExplorer.CustomerData.Customer customer;
    }
}

This code defines two top-level namespaces: OrderExplorer and DrawingTools. The OrderExplorer namespace contains the CustomerData namespace.

The code inside a namespace can refer to items defined in that namespace or any enclosing namespace without explicitly giving a namespace path. For example, in the previous code, the Customer class can refer to the Person class without indicating that its fully qualified namespace path is OrderExplorer.CustomerData.

In contrast, the Person class must refer to the Customer class as CustomerData.Customer because the Person class is not contained in the CustomerData namespace. Similarly, the Shape class must refer to the Customer class as OrderExplorer.CustomerData.Customer.

A using directive can change this behavior. For example, if the module contains the following using directive, then all the classes in this example could refer to the Customer class without any namespace information.

using OrderExplorer.CustomerData;

A program can place code in a namespace in multiple places by using multiple namespace statements. For example, two modules could use the statement namespace DrawingTools to add code to the DrawingTools namespace.

Resolving Namespaces

Normally, C# does a good job of resolving namespaces, so you don’t need to worry too much about the process. You can insert a using directive and then omit the namespace in the declarations that you use. If you don’t include a using directive, you can still use fully qualified declarations. You can even create a namespace alias so that you can specify the namespace without typing it out in full.

However, there are some in-between cases that can be confusing. To understand them, it helps to know a bit more about how C# resolves namespaces.

When the compiler sees a reference that uses a fully qualified namespace, it looks in that namespace for the item it needs and that’s that. It either succeeds or fails. For example, the following code declares a variable of type System.Collections.Hashtable. The compiler looks in the System.Collections namespace and tries to find the Hashtable class. If the class is not there, the declaration fails.

System.Collections.Hashtable hashtable = new System.Collections.Hashtable();

When the compiler finds a reference to a qualified namespace, it initially assumes the namespace is fully qualified. If it cannot resolve the reference as described in the preceding paragraph, it assumes the reference is partially qualified, and it looks in the current namespace for a resolution. For example, suppose you declare a variable as shown in the following code.

CustomerData.Customer customer;

In this case, the compiler searches the current namespace for a nested namespace called CustomerData. If it finds such a namespace, it looks for the Customer class in that namespace.

If the compiler cannot resolve a namespace using these methods, it moves up the namespace hierarchy and tries again. Movement up the namespace hierarchy can sometimes be confusing. It may lead the compiler to resolve references in an ancestor of the current namespace, in some sort of uncle/aunt namespace, or in a cousin namespace.

For example, consider the namespace hierarchy shown in Figure 13-7. The clouds represent namespaces and the boxes represent classes.

c13f007.eps

Figure 13-7: To resolve namespace references, the C# compiler may search far across the namespace hierarchy.

Suppose the Customer class includes the following declaration.

public Employee SalesRep { get; set; }

The Customer class is in the BusinessClasses namespace. That namespace defines an Employee class so that’s the class the compiler uses for the SalesRep property.

Now suppose the Customer class uses the following declaration instead of the preceding one.

public AssignmentTools.Employee SalesRep { get; set; }

In this case, the compiler first assumes AssignmentTools.Employee is a fully qualified name. It looks at the root of the namespace hierarchy for the AssignmentTools namespace, but it doesn’t find one.

Next, the compiler looks in the current namespace BusinessClasses to see if it contains a namespace AssignmentTools. The compiler doesn’t find such a namespace, so it moves up the hierarchy to the ContractBuilder namespace.

The compiler looks again for an AssignmentTools namespace and this time finds it. The compiler looks in the new namespace for the Employee class and finds it, so its search is over.

If you understand how the compiler resolves namespaces, you can eventually figure out that the property in this example has type ContractBuilder.AssignmentTools.Employee. If you add using directives to the code, things can get more confusing.

Suppose the program includes the following directives.

using BusinessClasses;
using AssignmentTools;

Now if a piece of code in the ContractBuilder namespace declares a variable of type Employee, the compiler won’t know which version to use. At that point it gives up and reports an Ambiguous Reference error.

You can come up with combinations of using directives, namespace aliases, and fully or partially qualified namespace paths to make the code do exactly what you want, but the result can be confusing. In this example, you would probably be better off rearranging the namespaces and possibly renaming one of the Employee classes to make things more obvious.

The global Namespace

Different namespaces can contain program items that have the same names. One namespace might define a class named Shutdown; another might create an enumeration named Shutdown; and a third might define a structure named Shutdown. As long as you use the right namespaces, you can use any of the versions.

One situation in which this can cause problems is if a namespace defines a symbol that hides a symbol in the global namespace. For example, suppose you define the following Player class for a game program.

public class Player
{
    public string Name { get; set; }
    private ConsoleTyes Console { get; set; }

    // Display the Person's name.
    public void ShowName()
    {
        Console.WriteLine(Name);
    }
}

The intent is for the Console property to store the player’s console type. Unfortunately, when the compiler sees Console in the ShowName method, it finds the property, not the class, that displays messages in the Console window.

If you place global:: in front of a namespace, the compiler begins searching for the symbol in the global namespace instead of looking for it locally. That means you can use the following statement to correctly find the Console.WriteLine method.

global::System.Console.WriteLine(Name);

Usually it is better to avoid names that conflict with the system namespaces. For example, if you change the property’s name to ConsoleType, there’s no confusion.

Summary

Namespaces are everywhere in C#. Every piece of code you write is contained in some namespace, even if it is only the application’s root namespace. Despite the pervasiveness of namespaces, many developers never need to use them explicitly, so they find them somewhat mystifying.

Namespaces are quite simple. They merely divide programming items into a hierarchy to prevent name collisions, and they enable you to group related items.

The using directive lets a program refer to items in a namespace without using fully qualified names. The using directive can also define an alias for a namespace, so you can refer to it by using a short abbreviation. This is particularly useful for resolving names that appear in more than one of the namespaces that your program uses.

The .NET Framework contains hundreds of namespaces, some of which are more useful than others. The next chapter describes classes that are in two of the most useful namespaces: System.Collections and System.Collections.Generic. The classes in those namespaces let you arrange and manage objects in particularly useful ways such as in stacks, queues, lists, and dictionaries.

Exercises

  1. Suppose you are writing a program that uses the System.Security.Cryptography.SHA512Managed class but you (understandably) don’t want to type all that out. Give two methods for shortening this in your code. What are the advantages and disadvantages of each?
  2. Suppose your program needs to use the System.Windows.Controls.Calendar control and the System.Globalization.Calendar class. Give three methods for differentiating between the two classes in your code. What are the advantages and disadvantages of each?

    For Exercises 3 through 8 use the namespace hierarchy shown in Figure 13-8. (No, I don’t recommend this kind of design.)

    c13f008.eps

    Figure 13-8: Clouds represent namespaces and rectangles represent classes.

  3. Without any using directives, what is the shortest way code in the Algorithms namespace can refer to the Order classes in both the OrderClasses and Fulfillment namespaces?
  4. Without any using directives, what is the shortest way code in the OrderTools namespace can refer to the Order classes in both the Fulfillment and OrderClasses namespaces?
  5. What using directives would you create to allow code in the Algorithms namespace to refer to either of the Order classes with an alias? Show code that uses those aliases to define objects of the two classes.
  6. What is the most concise way for the Customer class to include both kinds of Order objects? Show code that defines objects of both classes.
  7. What is the easiest way to allow code throughout the namespace hierarchy to use the Invoice class?
  8. How could you improve the design shown in Figure 13-8?
..................Content has been hidden....................

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