In deference to Martin Richards, the founder of the BCPL language and author of the first "Hello, World!" program, I present a "Hello, World!" program. Actually, I offer an enhanced version that displays "Hello, World" in English, Italian, or Spanish. The program is a console application. For simplicity, no error checking is performed. C# is case sensitive.
Here is my version of the HelloWorld program:
using System; namespace HelloNamespace { class Greetings{ public static void DisplayEnglish() { Console.WriteLine("Hello, world!"); } public static void DisplayItalian() { Console.WriteLine("Ciao, mondo!"); } public static void DisplaySpanish() { Console.WriteLine("Hola, imundo!"); } } delegate void delGreeting(); class HelloWorld { static void Main(string [] args) { int iChoice=int.Parse(args[0]); delGreeting [] arrayofGreetings={ new delGreeting(Greetings.DisplayEnglish), new delGreeting(Greetings.DisplayItalian), new delGreeting(Greetings.DisplaySpanish)}; arrayofGreetings[iChoice-1](); } } }
Csc.exe is the C# compiler. The following csc command compiles the hello.cs source file to create the executable hello.exe, which is a .NET assembly:
csc hello.cs
As a .NET assembly, the Hello.exe contains metadata and MISL code but not native binary.
Run the Hello application from the command line. Enter the program
Hello 2
The source code of the Hello application has the features common to most .NET applications: a
using
statement, a namespace, types, access modifiers,
The HelloNamespace namespace contains the Greetings and HelloWorld types. The Greetings class has three static methods, and each method displays "Hello, World" in a different language. Static methods are invoked on the type ( classname . member ), not an instance of that type. The static methods of the Greetings type are also public and therefore visible inside and outside the class.
Delegates define a type of function pointer. The delGreeting delegate is a container for functions pointers that point to functions that return void and have no parameters. This is not so coincidentally the function signature of the methods in the Greetings type.
The entry point of this and any other C# application is the
Main
method. Command-line parameters are passed as the
args
parameter, which is a string array. In the Hello program, the first element of the
args
array is a number indicating the language of choice, as inputted by the
arrayofGreetings[iChoice-1]()
The variable
iChoice
is an index into the delegate array. It is decremented to account for the fact that arrays in C# are
The remainder of the chapter discusses the important features of the Hello and any other C# application, except for types that will be reviewed in the next chapter.
Namespaces provide hierarchical clarity and organization of types and other members. A container of hundreds of classes, the .NET Framework Class Library (FCL) is an example of effective use of namespaces. The Framework Class Library would sacrifice clarity if planned as a single namespace with a flat hierarchy. Instead, the Framework Class Library organizes its
A namespace identifier must be unique within the namespace declaration space, which contains the current namespace but not a nested namespace. A nested namespace is
A namespace at file scope, not nested within another namespace, is considered part of the compilation unit and included in the global namespace. A compilation unit is a source code file. A program partitioned into several source files has multiple compilation units—one compilation unit for each source file. Any namespace, including the global namespace, can span multiple compilation units. For example, all types defined at file scope are included into a single global namespace that
The following code has two compilation units and three namespaces. Because of identical identifiers sharing the same scope, errors occur when the program is compiled.
// file1.cs public class ClassA { } public class ClassB { } namespace NamespaceZ { public class ClassC { } } // file2.cs public class ClassB { } namespace NamespaceY { public class ClassA { } } namespace NamespaceZ { public class ClassC { } public class ClassD { } }
Compile the code into a library:
csc /t:library file1.cs file2.cs
In the
ClassB and ClassC are ambiguous. ClassB is ambiguous because it is defined twice in the global namespace, once in each compilation unit. ClassC is defined in the NamespaceZ namespace in both compilation units. Because NamespaceZ is one cohesive namespace, ClassC is also ambiguous.
The relationship between compilation units, the global namespace, and nonglobal namespaces are
Figure 1-3:
Global vs. non-global namespaces
The
using
directive makes a namespace implicit. You can access members of the named namespace directly without the fully qualified
The using directive must precede any members in the namespace where it is defined. The following code defines the namespace member ClassA . The fully qualified name is NamespaceZ.NamespaceY.ClassA . Imagine having to type that several times in a program!
using System; namespace NamespaceZ { namespace NamespaceY { class ClassA { public static void FunctionM() { Console.WriteLine("FunctionM"); } } } } namespace Application { class Starter { public static void Main() { NamespaceZ.NamespaceY.ClassA.FunctionM(); } } }
The following using directive makes NamespaceZ.NamespaceY implicit. Now you can directly access ClassA .
namespace Application { using NamespaceZ.NamespaceY; class Starter { public static void Main() { ClassA.FunctionM(); } } }
Ambiguities can occur when separate namespaces with identically named members are made implicit. When this occurs, the affected members can be assessed only with their fully qualified names.
The using directive can also define an alias for a namespace or type. Aliases are typically created to resolve ambiguity or as a convenience. The scope of the alias is the declaration space where it is declared. The alias must be unique within that declaration space. In this source code, an alias is created for the fully qualified name of ClassA :
namespace Application { using A=NamespaceZ.NamespaceY.ClassA; class Starter { public static void Main() { A.FunctionM(); } } }
In this code, A is a nickname for NamespaceZ.NamespaceY.ClassA and can be used synonymously.
Using directive statements are not cumulative and are evaluated independently.