What’s in This Chapter
using
directiveWrox.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.
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.
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();
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.
Now when you start a new project, the New Project dialog includes your template, as shown in Figure 13-3.
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.
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.
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.
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.
If you change the default namespace, any modules you add to the project in the future use that namespace.
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.
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.
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.
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.
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.
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?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.)
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?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?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.Customer
class to include both kinds of Order
objects? Show code that defines objects of both classes.Invoice
class?