C# Quick Start

1. Hello, Universe

As a supporter of SETI, we thought it’d be appropriate to do a “Hello, Universe” program rather than the canonical “Hello, World” program:

using System; class Hello {

public static void Main(string[] args)

{

Console.WriteLine(“Hello, Universe”);

// iterate over command-line arguments,

// and print them out

for (int arg = 0; arg < args.Length; arg++)

Console.WriteLine(“Arg {0}: {1}”, arg, args[arg]);

}

}

As discussed earlier, the .NET runtime has a unified namespace for all program informa­tion (or metadata). The using System clause is a way of referencing the classes that are in the System namespace so they can be used without having to put System in front of the type name.

The System namespace contains many useful classes, one of which is the Console class, which is used (not surprisingly) to communicate with the console (or DOS box or command line, for those who have never seen a console).

Because C# doesn’t have global functions, the example declares a class called Hello that contains the static Main() function, which serves as the starting point for execution. Main() can be declared with no parameters or with a string array. Since it’s the starting function, it must be a static function, which means it isn’t associated with an instance of an object.

The first line of the function calls the WriteLine() function of the Console class, which will write “Hello, Universe” to the console. The for loop iterates over the parameters that are passed in and then writes out a line for each parameter on the command line.

2. Namespaces and using

Namespaces in the .NET runtime organize classes and other types into a single hierarchical structure. The proper use of namespaces will make classes easy to use and prevent collisions with classes written by other authors.

You can also think of namespaces as a way to specify really long names for classes and other types without having to always type a full name.

Namespaces are defined using the namespace statement. For multiple levels of organization, namespaces can be nested:

namespace Outer {

namespace Inner {

class MyClass {

public static void Function() {}

}

}

}

That’s a fair amount of typing and indenting, so it can be simplified by using the following instead:

namespace Outer.Inner {

class MyClass {

public static void Function() {}

}

}

Each source file can define as many different namespaces as needed.

As mentioned in the “Hello, Universe” section, using imports the metadata for types into the current program so the types can be more easily referenced. The using keyword is merely a shortcut that reduces the amount of typing that’s required when referring to elements, as Table 3-1 indicates.

Collisions between types or namespaces that have the same name can always be resolved by a type’s fully qualified name. This could be a long name if the class is deeply nested, so the following is a variant of the using clause that allows an alias to be defined to a class:

using ThatConsoleClass = System.Console; class Hello {

public static void Main()

{

ThatConsoleClass.WriteLine(“Hello”);

}

}

To make the code more readable, the examples in this book rarely use namespaces, but you should use them in most real code.

3. Namespaces and Assemblies

An object can be used from within a C# source file only if the C# compiler can locate that object. By default, the compiler will open only the single assembly known as mscorlib.dll, which contains the core functions for the CLR.

To reference objects located in other assemblies, the name of the assembly file must be passed to the compiler. You can do this on the command line using the /r:<assembly> option or from within Visual Studio by adding a reference to the C# project.

Typically, a correlation exists between the namespace an object is in and the name of the assembly in which it resides. For example, the types in the System.Net namespace reside in the System.Net.dll assembly. Types are usually placed in assemblies based on the usage patterns of the objects in that assembly; a large or rarely used type in a namespace might be placed in its own assembly.

You can find the exact name of the assembly that an object is contained in within the documentation for that object.

4. Basic Data Types

C# supports the usual set of data types. For each data type that C# supports, there’s a corre­sponding underlying .NET CLR type. For example, the int type in C# maps to the System.Int32 type in the runtime. You can use System.Int32 in most of the places where you use int, but that isn’t recommended because it makes the code tougher to read.

Table 3-2 describes the basic types. You can find all the runtime types in the System namespace of the .NET CLR.

The distinction between basic (or built-in) types in C# is largely an artificial one, as user- defined types can operate in the same manner as the built-in ones. In fact, the only real differ­ence between the built-in data types and user-defined data types is that it’s possible to write literal values for the built-in types.

Data types are separated into value types and reference types. Value types are either stack allocated or allocated inline in a structure. Reference types are heap allocated.

Both reference and value types are derived from the ultimate base class object. In cases where a value type needs to act like an object, a wrapper that makes the value type look like a reference object is allocated on the heap, and the value type’s value is copied into it. This process is known as boxing, and the reverse process is known as unboxing. Boxing and unboxing let you treat any type as an object. This allows the following to be written:

using System; class Hello {

public static void Main(string[] args)

{

Console.WriteLine(“Value is: {0}”, 3);

}

}

In this case, the integer 3 is boxed, and the Int32.ToString() function is called on the boxed value.

C# arrays can be declared in the multidimensional form or the jagged form. You can find more advanced data structures, such as stacks and hash tables, in the System.Collections namespace.

5. Classes, Structs, and Interfaces

In C#, the class keyword declares a reference (heap-allocated) type, and the struct keyword declares a value type. Structs are used for lightweight objects that need to act like the built-in types, and classes are used in all other cases. For example, the int type is a value type, and the string type is a reference type. Figure 3-1 details how these work.

C# and the .NET runtime don’t support multiple inheritance for classes but do support multiple implementation of interfaces.

6. Statements

The statements in C# are close to C++ statements, with a few modifications to make errors less likely and a few new statements. You can use the foreach statement to iterate over arrays and collections, the lock statement for mutual exclusion in threading scenarios, and the checked and unchecked statements for controlling overflow checking in arithmetic operations and conversions.

7. Enums

Enumerators declare a set of related constants—such as the colors that a control can take— in a clear and type-safe manner. For example:

enum Colors {

red,

green,

blue

}

8. Delegates and Events

Delegates are a type-safe, object-oriented implementation of function pointers and are used in many situations where a component needs to call back to the component that’s using it. They’re used most heavily as the basis for events, which allow a delegate to easily be registered for an event. Chapter 23 discusses them.

The .NET Framework use delegates and events heavily.

9. Properties and Indexers

C# supports properties and indexers, which are useful for separating the interface of an object from the implementation of the object. Rather than allowing a user to access a field or array directly, a property or indexer allows a statement block to be specified to perform the access while still allowing the field or array usage. Here’s a simple example:

using System; class Circle {

public int Radius {

get

{

return(radius);

}

set

{

radius = value;

Draw();

}

}

public void Draw()

{

}

int radius;

}

class Test {

public static void Main()

{

Circle c = new Circle();

c.Radius = 35;

}

}

In this example, the get or set accessor is called when the property Radius is referenced.

10. Attributes

Attributes are used in C# and the .NET Framework to communicate declarative information from the writer of the code to other code that’s interested in the information. You could use this to specify which fields of an object should be serialized, what transaction context to use when running an object, how to marshal fields to native functions, or how to display a class in a class browser.

Attributes are specified within square braces. A typical attribute usage might look like this:

[CodeReview(“12/31/1999″, Comment=”Well done”)]

Attribute information is retrieved at runtime through a process known as reflection. New attributes can be easily written, applied to elements of the code (such as classes, members, or parameters), and retrieved through reflection.

Source: Gunnerson Eric, Wienholt Nick (2005), A Programmer’s Introduction to C# 2.0, Apress; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *