Aug. 6, 2008, 4:27 a.m.
posted by vendetta
C# Programming Basics
Though it’s considered a bare-bones development environment, the .NET Framework SDK contains quite a few tools that allow you to create, compile, and debug C# programs. This section describes some of the tools that are at your disposal.
Creating C# Programs
If you are using one of the Microsoft Visual products to develop your programs (Visual Studio .NET or Visual C# .NET), you have a complete program editing environment, including help files, graphical wizards, and command completion wizards. If you are using the .NET Framework SDK package, you are on your own for producing and compiling your C# code. Although this SDK’s features pale in comparison to the fancy Visual packages, it is nonetheless just as valid a way to produce C# applications.
The first step to working with C# programs in the .NET Framework development environment is to associate the C# source code filename extension with a text editor. This will make editing programs much easier; you can just double-click a program from within the Windows Explorer program to begin editing. The type of editor you select is important. Choose one that allows you to save your source code files in text mode rather than a Microsoft Word .doc file or other word processing document, because the C# compiler must be able to interpret each line of code. If you do select a word processing package to edit your C# programs, make sure that you save all of the files in text format.
After you select an editor, associate the .CS file type to the editor application within the Windows Explorer: right-click a C# program, select the Open With option, and select the appropriate application from the list.
If you are new to C#, you may want to practice compiling and debugging C# programs. To do that, you must first have a sample program to work with. Listing 1.1 shows a simple program that demonstrates some basic C# programming principles.
class DataClass
{
private int a;
private int b;
public DataClass(int x, int y)
{
a = x;
b = y;
}
public int addem()
{
return a + b;
}
}
class SampleClass
{
static int sampleX;
static int sampleY;
public SampleClass()
{
DataClass sample = new DataClass(sampleX, sampleY);
System.Console.WriteLine("The result is: {0}", sample.addem());
}
public static void Main(string[] argv)
{
if (argv.Length != 2)
{
System.Console.WriteLine(" Usage: SampleClass x y");
return;
}
sampleX = System.Convert.ToInt16(argv[0]);
sampleY = System.Convert.ToInt16(argv[1]);
SampleClass starthere = new SampleClass();
}
}
The sample program contains two separate C# classes, DataClass and SampleClass. DataClass declares two private integers (that is, they are only accessible from the DataClass class), a constructor for the class, and a method that manipulates the data. The DataClass constructor defines what happens when DataClass is instantiated from a program:
public DataClass(int x, int y)
{
a = x;
b = y;
}
The default constructor requires two integers. The two integers are assigned to the internal private variables a and b defined in the class. The one method that is defined, addem, returns an integer value that is the addition of the two private variables:
public int addem()
{
return a + b;
}
| Note |
Once DataClass is defined in the program, other classes in the program can use it. In C#, unlike C++, you can use classes before they are defined without first declaring them. The SampleClass code could just as easily have been defined first, before the DataClass definition. The C# compiler will realize that the required class is located later in the program. You can even declare classes in separate files, as long as you include them on the command line when compiling. The compiler will only complain if declared classes are never found in any of the program files listed on the command line. |
SampleClass contains two static integer variables, a constructor, and a Main() method, which instructs the C# compiler where to start execution of the program. The Main() method first checks to ensure that two command-line parameters have been entered, converts them to integer values, and assigns them to the two integer variables defined. It then creates an instance of SampleClass using the statement
SampleClass starthere = new SampleClass();
This forces the CLR to execute the SampleClass constructor to create a new instance of the class.
The SampleClass constructor code creates an instance of DataClass, passing the two integers to the DataClass class constructor. The addem() method is called from the instantiated SampleClass variable and returns the result of the addition method. The following line is used to display the result of the addem() method to the console screen:
System.Console.WriteLine("The result is: {0}", sample.addem());
The symbol {0} is used as a placement value to represent a variable listed after the text string, in this case replaced with the return value of the sample.addem() method. You can add additional variables by continuing the placement numbers ({1}, {2}, and so on). Each additional variable is added to the variable list separated by commas.
After typing the program code, you must save the file using a .CS extension, which identifies the file as a C# code file. Once you save the file, you are ready to compile it.
Compiling and Running C# Programs
The .NET Framework SDK and Redistributable packages both contain the C# compiler, csc.exe. Any C# program, no matter how complex, can be compiled using just this compiler. Many different switches can be used on the command line to control the behavior of the compiler function. Some of the most common are listed in Figure.
|
Switch |
Function |
|---|---|
|
/out:filename |
Defines the executable filename of the program |
|
/main:classname |
Defines the class that contains the Main() method |
|
/target:target |
Defines the type of program. The target can be exe for console-based apps, winexe for Windows graphical apps, library for Windows DLL files, or module for assembly modules |
|
/debug:type |
Creates debugging information for the executable file. The type can be full (the default), which enables attaching the debugger to a running process, or it can be pdbonly, which only creates a .pdb database file for debugging within a debugging tool |
|
/resource:<res> |
Embeds the resource specified in the executable file |
After you determine what command-line options (if any) you need, compiling the C# program using the csc command-line compiler is simple:
C:\>csc SampleClass.cs Microsoft (R) Visual C# .NET Compiler version 7.00.9466 for Microsoft (R) .NET Framework version 1.0.3705 Copyright (C) Microsoft Corporation 2001. All rights reserved. C:\>
The compile was successful if the command prompt returns with no text messages. If any errors or warnings are indicated by the C# compiler, you must edit the source code file to correct them. Each error or warning produced by the compiler indicates the line where the error occurred. Here is an example of the error produced by csc when a typo occurs within the source code:
C:\>csc SampleClass.cs
Microsoft (R) Visual C# .NET Compiler version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (C) Microsoft Corporation 2001. All rights reserved.
SampleClass.cs(36,12): error CS0117: 'System.Convert' does not contain a
definition for 'oInt16'
C:\>
Note the line number, along with position in the line, shown in parentheses within the error message. Also, you get a fairly descriptive error message, helping you to determine the cause of the error. If you are using a text editor that supports line numbers, it is easy to go back into the source code and correct the mistake. If not, happy counting!
Once you successfully compile the program, you can run it from the command line:
C:\>SampleClass 5 10 The result is: 15 C:\>
You can see that the program has successfully run and displayed the result of the addition of the command-line arguments. Of course, this simple example does not do much error checking on the command-line arguments, so be careful to only enter numbers or the program will blow up and produce an error message—but more on that later in the C# Exception Programming section.
Using Multiple Source Files
The SampleClass.cs example program defined two separate classes in one source code file. This was easy to do for a small example, but larger programs can get confusing when you’re combining classes into one source code file. Often it is best to create a separate source code file for each class used in the application. This allows better code management, especially when several people are working on an application that contains hundreds of classes. For example, two separate files could be created:
There are a few things to be careful of when you separate classes out into discrete source code files. First, you must ensure that the C# compiler can find them at compile time. The easiest way to do this is to include all related source code files on the command line, as follows:
C:\>csc SampleClass2.cs DataClass.cs
Be careful when you do this, however, because the source code file listed first will be the default .exe filename. If you want to change the .exe filename, you can use the /out: command line switch:
C:\>csc /out:SampleClass2.exe DataClass.cs SampleClass2.cs
Another issue is the importance of telling the compiler where the program execution starts. If only one class has a Main() section defined, this will work fine. However, sometimes different classes can use methods from other classes, but both classes may contain a Main() method. This would confuse the compiler, as it would not know from which Main() method to start to run the program.
A command-line switch for the csc.exe program solves this problem. The /main:switch defines the class that contains the Main() method you want to use:
C:\>csc /main:SampleClass SampleClass2.cs DataClass.cs
Notice that you must specify the class that the Main() method is in, not the source code filename.
Debugging C# Programs
The .NET Framework SDK offers two excellent ways to debug C# programs:
-
•dbgclr is a GUI debugging program
-
•cordbg is a command-line text debugging program
The graphical dbgclr program and the text mode cordbg program have similar features but present different interfaces. Both allow you to step through the C# program and watch variables and outputs as execution proceeds. To do this, though, you must compile the executable program using the /debug option on the csc compiler:
C:\>csc /debug SampleClass.cs
This command performs two actions: an attribute is set in the executable file that informs the CLR JIT compiler that code tracking must be done, and a programmer database (PDB) file is created that contains code tracking information for the debugger. The added attribute is called the JITTracking flag. It informs the CLR JIT compiler that the code must be disassembled from the generated native code back to MSIL instructions and ultimately mapped back to the original source code. All of this information is contained in the PDB file for the executable file.
Using the dbgclr Program
The dbgclr program provides a Windows environment that can be used to watch and trace a running C# program to look for coding errors. The dbgclr program is located under the Microsoft.Net directory you specified when installing the SDK. The default location is as follows:
C:\Progam Files\Microsoft.Net\FrameworkSDK\GuiDebug\dbgclr.exe
When dbgclr is run, you must specify the source code and executable file location for the application. To do this, follow these steps:
-
From the Menu Bar, click Debug ‚ Program to Debug.
-
Next to the Program text box, click the ellipsis (...) button and select the SampleClass.exe program you want to debug. (Remember that the executable program must have been compiled with the /debug switch.) The Working Directory text box will automatically display the directory location of the executable file. Also, in the Arguments text box, type in any required arguments for the program; for the SampleClass program, type in any two numbers. Click OK when you are finished.
-
Click File ‚ Open ‚ File. Select the SampleClass.cs source code file for the application, and click Open.
At this point, the dbgclr program will display four separate windows:
-
The source code file
-
The Solution Explorer
-
The application output
-
The command window
You should see the SampleClass.cs file in the source code window, and the Solution Explorer should list this file in the Miscellaneous Files section. To start debugging, from the menu bar, click Debug ‚ Step Into. This starts the program and allows you to single step though the code (see Figure).
When the debugging process starts, a new window appears showing various variables used within the application code. Note in Figure that the command-line argument values you entered are displayed under the Locals tab, along with the Length value (which should be 2). This allows you to easily watch variable values throughout the execution of the program. This is handy if you are getting a corrupt variable value within the program and want to investigate.
The Step Into function starts executing the program line by line, starting at the Main() section. The current code line is highlighted in yellow. By pressing F11, you can single step through the entire program. You can also click buttons on the toolbar to step over code, as well as step out of (or back up from) a code segment. This gives you great control in watching the program execute.
Using the cordbg Program
The cordbg command-line tool has similar functionality to that of dbgclr, without the graphical windows. It, too, allows you to single step through a program and monitor variable values as you go along, but with allowing text input and providing a text output. Listing 1.2 shows a sample debug session using cordbg.
C:\>cordbg
Microsoft (R) Common Language Runtime Test Debugger Shell Version 1.0.3705.0
Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
(cordbg) run SampleClass.exe 5 10
Process 356/0x164 created.
Warning couldn't load symbols for c:\winnt\microsoft.net\framework\v1.0.3705\ms
corlib.dll
[thread 0xff] Thread created.
031: if (argv.Length != 2)
(cordbg) sh
026: System.Console.WriteLine("The result is: {0}", sample.addem());
027: }
028:
029: public static void Main(string[] argv)
030: {
031:* if (argv.Length != 2)
032: {
033: System.Console.WriteLine(" Usage: SampleClass x y");
034: return;
035: }
036: sampleX = System.Convert.ToInt16(argv[0]);
(cordbg) pro
PID=0x164 (356) Name=C:\SampleClass.exe
ID=1 AppDomainName=SampleClass.exe
(cordbg) p argv
argv=(0x00e718b8) array with dims=[2]
argv[0] = (0x00e718d0) "5"
argv[1] = (0x00e718e4) "10"
(cordbg) s
036: sampleX = System.Convert.ToInt16(argv[0]);
(cordbg) so
037: sampleY = System.Convert.ToInt16(argv[1]);
(cordbg) so
038: SampleClass starthere = new SampleClass();
(cordbg) s
023: public SampleClass()
(cordbg) s
[0007] nop
(cordbg) s
[001c] mov ecx,0B65210h
(cordbg) s
006: public DataClass(int x, int y)
(cordbg) s
[0007] nop
(cordbg) s
[0014] mov dword ptr [esi+4],edi
(cordbg) s
009: b = y;
(cordbg) s
010: }
(cordbg) s
025: DataClass sample = new DataClass(sampleX, sampleY);
(cordbg)
Note that when the cordbg program is started, you can use the run command with the filename of the executable program, along with any pertinent command-line parameters for the executable program. Alternatively, you can run the cordbg command with the executable program and arguments on the command line.
The cordbg program uses text commands to step through the program and display pertinent information. Figure describes some of the text commands that can be used.
|
Command |
Function |
|---|---|
|
s |
Step into one line of source code |
|
si |
Step into one line of source code |
|
so |
Step over the next line of code |
|
ss |
Step into the next native or IL instruction |
|
p arg |
Print the current value of the variable arg |
|
pro |
Show the system process information for the running program |
|
reg |
Display the CPU registers for the current thread |
|
run prog |
Run the progam prog in the debugger |
|
break |
Set or display a breakpoint in the code |
|
sh |
Show the current line of code, along with five lines before and after |
As demonstrated, you can do everything in cordbg that you can in dbgclr. In fact, many advanced developers prefer to use cordbg because it can be faster than waiting for the graphical dbgclr program to do its thing.
Watching the C# program execute is one way to debug your application. The next section describes a tool for observing the actual MSIL code generated by the csc compiler and run by the CLR.
Debugging MSIL Code
If you really want to get under the hood of your program, you must look at the MSIL code—the actual code that is compiled by the CLR JIT compiler to create the native system code for the host. The .NET Framework SDK gives you a tool that helps you do this: the Microsoft Intermediate Language Disassembler (IL DASM). You must run the ildasm.exe program from the command line, along with the name of the CLR executable program to monitor to see the code:
C:\>ildasm SampleClass.exe
Figure shows the IL DASM window with the classes and variables that are contained in the program. IL DASM gives you a hierarchical view of the code, separating the classes and the variables and methods within classes. To see the actual MSIL code, double-click an individual section. Figure shows the result from clicking the addem() method. Even without knowing much about MSIL, you can see that this section of code retrieves two values from memory and adds them.
| Note |
If you want to debug your applications at the CLR level, you must learn the MSIL assembly code, which is way beyond the scope of this book. |
Now that you are familiar with the C# development environment, it is time to start working on C# code. Let’s begin by looking at some features of C# that are different from other programming languages and that are often used in network programs. If you are already familiar with the C# language, feel free to skip to the next chapter.


