The using Directive
Fully qualified namespace names can become quite long and unwieldy. It is possible, however, to import all the types from one or more namespaces into a file so that they can be used without full qualification. To achieve this, the C# programmer includes a using directive, generally at the top of the file. For example, in Listing 5.7, Console is not prefixed with System. The namespace may be omitted because of the using System directive that appears at the top of the listing.
Listing 5.7: using Directive Example
// The using directive imports all types from the // specified namespace into the entire file using System; class HelloWorld { static void Main() { // No need to qualify Console with System // because of the using directive above Console.WriteLine("Hello, my name is Inigo Montoya"); } }
The results of Listing 5.7 appear in Output 5.2.
Output 5.2
Hello, my name is Inigo Montoya
A using directive such as using System does not enable you to omit System from a type declared within a child namespace of System. For example, if your code accessed the StringBuilder type from the System.Text namespace, you would have to either include an additional using System.Text; directive or fully qualify the type as System.Text.StringBuilder, not just Text.StringBuilder. In short, a using directive does not import types from any nested namespaces. Nested namespaces, which are identified by the period in the namespace, always need to be imported explicitly.
Frequent use of types within a particular namespace implies that the addition of a using directive for that namespace is a good idea, instead of fully qualifying all types within the namespace. Accordingly, almost all C# files include the using System directive at the top. Throughout the remainder of this book, code listings often omit the using System directive. Other namespace directives are included explicitly, however.
One interesting effect of the using System directive is that the string data type can be identified with varying case: String or string. The former version relies on the using System directive, and the latter uses the string keyword. Both are valid C# references to the System.String data type, and the resultant Common Intermediate Language (CIL) code is unaffected by which version is chosen.2
using static Directive
The using directive allows you to abbreviate a type name by omitting the namespace portion of the name—such that just the type name can be specified for any type within the stated namespace. In contrast, the using static directive allows you to omit both the namespace and the type name from any member of the stated type. A using static System.Console directive, for example, allows you to specify WriteLine() rather than the fully qualified method name of System.Console.WriteLine(). Continuing with this example, we can update Listing 5.2 to leverage the using static System.Console directive to create Listing 5.9.
Listing 5.9: using static Directive
using static System.Console; class HeyYou { static void Main() { string firstName; string lastName; WriteLine("Hey you!"); Write("Enter your first name: "); firstName = ReadLine(); Write("Enter your last name: "); lastName = ReadLine(); WriteLine( $"Your full name is { firstName } { lastName }."); } }
In this case, there is no loss of readability of the code: WriteLine(), Write(), and ReadLine() all clearly relate to a console directive. In fact, one could argue that the resulting code is simpler and therefore clearer than before.
However, sometimes this is not the case. For example, if your code uses classes that have overlapping behavior names, such as an Exists() method on a file and an Exists() method on a directory, then perhaps a using static directive would reduce clarity when you invoke Exists(). Similarly, if the class you were writing had its own members with overlapping behavior names—for example, Display() and Write()—then perhaps clarity would be lost to the reader.
This ambiguity would not be allowed by the compiler. If two members with the same signature were available (through either using static directives or separately declared members), any invocation of them that was ambiguous would result in a compile error.
Aliasing
The using directive also allows aliasing a namespace or type. An alias is an alternative name that you can use within the text to which the using directive applies. The two most common reasons for aliasing are to disambiguate two types that have the same name and to abbreviate a long name. In Listing 5.10, for example, the CountDownTimer alias is declared as a means of referring to the type System.Timers.Timer. Simply adding a using System.Timers directive will not sufficiently enable the code to avoid fully qualifying the Timer type. The reason is that System.Threading also includes a type called Timer; therefore, using just Timer within the code will be ambiguous.
Listing 5.10: Declaring a Type Alias
using System; using System.Threading; using CountDownTimer = System.Timers.Timer; class HelloWorld { static void Main() { CountDownTimer timer; // ... } }
Listing 5.10 uses an entirely new name, CountDownTimer, as the alias. It is possible, however, to specify the alias as Timer, as shown in Listing 5.11.
Listing 5.11: Declaring a Type Alias with the Same Name
using System; using System.Threading; // Declare alias Timer to refer to System.Timers.Timer to // avoid code ambiguity with System.Threading.Timer using Timer = System.Timers.Timer; class HelloWorld { static void Main() { Timer timer; // ... } }
Because of the alias directive, “Timer” is not an ambiguous reference. Furthermore, to refer to the System.Threading.Timer type, you will have to either qualify the type or define a different alias.