Description: This document is a tutorial in a series of tutorials for programmers learning about the .NET Framework development environment. What you will learn is what C# is, how it fits into the .NET Framework. You will also learn how to write a simple C# program as well as gain a taste for the possibilities of this new platform.
Requirements: You should be familiar with at least one programming language, such as C++, Pascal, PERL, Java or Visual Basic. You should have some comfort with object oriented concepts such as instantiating and using objects. You should be comfortable with general computer science concepts. To do the exercises and run the examples you need a PC running Windows with the .NET Framework installed.
Table of Contents
Table of Contents. 1
Figures and Exercises. 3
1 Introducing C#. 4
1.1 HelloWorld a-la C#. 4
1.2 C# and the .NET Framework Class Library (FCL) 6
1.3 Minimal Requirements to Use C#. 6
2 C# Applications. 7
2.1 Creating Console Assemblies. 8
2.2 Creating GUI Assemblies. 10
2.3 Creating Code Library Assemblies. 12
3 Visual Studio.NET. 16
3.1 Creating a Project 16
3.2 Working with Projects. 18
4 Language Concepts. 18
4.1 Syntax Ground Rules. 19
4.2 Primitive Types. 19
4.3 Expressions and Operators. 21
4.4 Methods. 22
4.5 Reference Types and Value Types. 23
4.6 Arrays of Data. 24
4.7 Conditional Statements. 25
4.8 Loops. 26
4.9 Error Handling. 26
5 Simple Object Oriented Programming. 27
5.1 Using Types. 27
5.2 Typecasting. 29
5.3 Extending Types. 30
6 C# in Action. 31
6.1 C#Pad.exe – Sample Application. 31
Figures and Exercises
Figure 1‑1 HelloGUI.cs. 5
Figure 2‑1 Rabbits.cs. 8
Figure 2‑2 Tribbles.cs. 11
Figure 2‑3 FibObj.cs. 13
Figure 2‑4 FibTest.cs. 14
Figure 3‑1 New Project in VS.NET. 17
Figure 3‑2 Solution Explorer 18
Figure 4‑1 Primitive Types. 20
Figure 4‑2 Primitive Code Snippit (Excerpted from Language.cs) 21
Figure 4‑3 C# Operators. 22
Figure 4‑4 Methods Code Snippit (Excerpted From Language.cs) 23
Figure 4‑5 Array Declaration Code Snippit (Excerpted from Language.cs) 24
Figure 4‑6 if Code Snippit (Excerpted from Language.cs) 25
Figure 4‑7 switch Code Snippit (Excerpted from Language.cs) 25
Figure 4‑8 Looping Code Snippit (Excerpted from Language.cs) 26
Figure 4‑9 Exception Code Snippit (Excerpted from Language.cs) 27
Figure 5‑1 FileCopy.cs. 28
Exercise 1‑1 Building and Running HelloGUI.cs. 7
Exercise 2‑1 Build and Run a Console Application. 9
Exercise 2‑2 Build and Run a GUI Application. 12
Exercise 2‑3 Build a Code Library. 15
Exercise 2‑4 Build an App to Test a Code Library. 15
1 Introducing C#
C# (or C-Sharp) is a new programming language. C# is used to write software that runs on the .NET Framework. Although C# is not the only language that you can use to target the .NET Framework, C# is one of the most popular because of its simplified C-based syntax.
Usually, an introductory text to a computer language begins by telling you what you can do with a language as well as where the software you build with the language can run. I would like to address these points right away with C#, since they can be a little confusing.
In brief, C# (unlike C++, PERL, COBOL, Pascal, etc.) is a language that targets one and only one platform. This platform is the .NET Framework. However, the .NET Framework itself is a computing platform that is designed to be hosted by any operating system. At the time of this writing the .NET Framework runs on Windows operating systems, and I know of two other major OSs for which a version of the .NET Framework is being developed. So you can see that although C# is designed to target only the Framework, the Framework itself is flexible enough to run your C# programs on many types of systems.
The relationship between C# and the .NET Framework is somewhat unique. In a way it is similar to the relationship between Java and the Java Virtual Machine, however there are several major differences. First, C# is not the only language that can be used to write .NET Framework applications (called Managed Applications). Second, .NET or managed, applications run in native machine-language and are not interpreted. Third, C# or managed applications do not run in a sandbox.
What you should take away from this introduction, as a programmer learning C#, is that C# is a programming language (that you will find simple to master); however, much of what you can do with C# is really more a part of the .NET Framework itself. C# is a language with syntax so simple that most programmers will be comfortable with it in no time, but the .NET Framework is a platform so powerful that developers will continue to learn the ins and outs of it for years.
Much of what we cover in this tutorial will be C# syntax, but I will certainly discuss .NET Framework concepts as well. In any case the topics in this tutorial are covered from the perspective of a C# programmer. So let’s talk code.
1.1 HelloWorld a-la C#
In any introductory text it is hard to avoid the required HelloWorld application, and this is no exception. (But don’t worry, there are some more advanced applications examples in this tutorial, including a complete text editor application).
using System.Windows.Forms;
using System.Drawing;
class MyForm:Form{
public static void Main(){
Application.Run(new MyForm());
}
protected override void OnPaint(PaintEventArgs e){
e.Graphics.DrawString("Hello World!", new Font("Arial", 35),
Brushes.Blue, 10, 100);
}
}
Figure 1‑1 HelloGUI.cs
This is an example of a simple C# or managed application. (Managed applications are applications that run on the .NET Framework).
Note: Remember the terms Managed Code and Managed Application. They are often used when referring to .NET code generically, as opposed to referring to a specific programming language like C# or Visual Basic.
The source code in Figure 1‑1 displays the text "Hello World!" in a window. (The C# version of a command line hello-world application would be a one-liner). As you can see from the code, C# has a C-based syntax, but with objects like C++ or Java. Every function in C# is a method of a type.
In this example, the MyForm class is defined to derive its functionality from the Form class (part of the .NET Framework Class Library). In addition it defines two new methods, Main() and OnPaint().
All C# (or .NET) applications must have a static method named Main() defined to be the entry point of the application.
Note: C# is a case-sensitive programming language. This means that it would see methods named Main() and main() as different methods. The entry point method for C# programs must be called Main().
The static Main() method can be defined in any class in the application, so long as its name is “Main” and it is declared to be static.
Note: Methods declared as static do not require an object instance to be called. They are similar to global functions, and are often referred to as type-methods (rather than instance methods). I cover static’s in detail later.
The OnPaint() method is an override of a virtual method on the Form class. It is called when the window needs to paint itself. This sample uses this method to draw the text "Hello World!".
Most of the code in Figure 1‑1 will be covered in detail throughout this text. Don’t worry too much about the parts that don’t make sense right now. However, I would take the time to look the code over, perhaps build and run it, and get a feel for the sample.
1.2 C# and the .NET Framework Class Library (FCL)
I mentioned in the introduction that a big requirement of C# programming was learning the .NET Framework. This is true. To be more specific, the Framework Class Library (called the FCL) will be used extensively in almost every C# application.
I will make references to reuseable object types defined in the FCL throughout this tutorial. The documentation for these types ships with the .NET Framework, and also installs with Visual Studio.NET.
This tutorial is the second in a series on the .NET Framework. The first is an overview of the .NET Framework called Introducing the .NET Framework with C#. In the first tutorial I discuss the .NET Framework in an overview fashion, and it includes a fairly lengthy section on the Framework Class Library. If the discussion of the FCL becomes confusing at any point in this tutorial I strongly suggest referring to the FCL sections in the previous tutorial.
To further emphasize the importance of the FCL, look back at Figure 1‑1. While the code is written in C#, a great deal of the functionality comes from types defined in the FCL. For example, the window behaves as it does because MyForm is derived from the FCL class Form. Additionally, the PaintEventArgs, Graphics, Font, and Brushes classes play an important role in the drawing of the string on the form. And even the "Hello World!" string itself is of type String. All of these types (and many more) are defined by the FCL, and useable within your C# applications.
Two more point before we move on. First, types defined in the FCL exist in a hierarchy of namespaces. For example the Form class is really the System.Windows.Forms.Form class. However, since we want to only type Form in our source code we enter a using statement at the beginning of our source code module that indicates that we are using the System.Windows.Forms namespace in this module. Notice that the code in Figure 1‑1 has two using statements.
Second, the types in the FCL are published in files called Assemblies. When you compile your C# application you must include references to the assemblies that are used by your code. Visual Studio.NET lets you add references to your projects, and the command line C# compiler uses the /R switch to indicate referenced assemblies. I will talk more about building your code shortly, but I just wanted to mention assemblies in context with our discussion of the Framework Class Library.
1.3 Minimal Requirements to Use C#
In this section I am going to show you how to build a C# application. More specifically, I am going to show you the minimal requirements to use C#. The most common way of programming with C# is by using the Visual Studio.NET developers integrated development environment (IDE). However, the C# command-line compiler ships with the .NET Framework, and we will use that first.
First, you should install the latest version of the .NET Framework to your PC running Windows. If you like you can install Visual Studio.NET which installs the .NET Framework automatically.
Once you have the .NET Framework installed, you also have the Framework SDK, which includes the command-line compiler for C#. The C# compiler is called csc.exe and exists in the following directory.
C:\WINDOWS\Microsoft.NET\Framework\v1.0.2914
The final directory named v1.0.2914 indicates the version of the .NET Framework that you have installed.
I often set this directory in my path, using the advanced tab of the System control panel applet. (The Visual Studio .NET installation optionally sets my path for me). This lets me type csc from the command line in any directory in my file-system. (I find that I use Visual Studio for my big projects, but I use the command line compiler for my little tests and scripts.)
This is all you need to use C#. You can use any editor you like to create C# source code modules. You should give your C# models a .cs extension, and you are good to go.
If you were to cut and paste the source code from Figure 1‑1 into a file (perhaps using Notepad), and save it as HelloGui.cs, you could then compile it into an executable with the following command.
C:\>csc /Target:winexe HelloGui.cs
Its just that simple.
The /Target:winexe switch indicates to the compiler that you are creating a GUI application. The default is command-line or console applications.
Exercise 1‑1 Building and Running HelloGUI.cs
1. The source code in Figure 1‑1 is a complete GUI application written in C#. It will display a window with some text on the Window.
2. Type in or copy the source code and save it in a .cs file.
3. Compile the source code using the command line compiler or the Visual Studio IDE.
1. Hint: If you are using Visual Studio you will need to add references for the System.Drawing.dll, System.Windows.Forms.dll, and the System.dll.
4. Run the new executable.
2 C# Applications
I am going to take you on a quick tour of the different types of binaries you can build with C#. I use the term binaries, because one of the three is a non-executable binary with types (classes, interfaces, etc.) defined in it that can be used by other managed executables. In fact there is a special name for a compiled binary in the .NET world, and it is Assembly.
All managed executable files are assemblies. All libraries of classes and interfaces are packaged as assemblies (often with a .dll file extension). The concept of an assembly is important, because this is what your managed code must reference when it is using a type defined elsewhere.
Note: Technically assemblies can be comprised of multiple files for a single assembly. In a later tutorial in this series I will describe the reason for this in more detail. For now, it is easiest to deal with single-file assemblies which are by far the most common kind.
The three kinds of assemblies that you can create with C# are the following.
· Console applications
· GUI applications
· Libraries of Types
The important thing to remember about assemblies is that they are not source-code. They are compiled binaries that can be executed directly.
2.1 Creating Console Assemblies
Console applications are the kind of executables that you would use to create a command line utility like dir or xcopy. I actually find that many of my ad-hoc C# projects are console applications.
using System;
class App{
public static void Main(String[] args){
try{
Int32 iterations = Convert.ToInt32(args[0]);
if(iterations > 138){
throw new Exception();
}
Decimal lastNum = 1;
Decimal secondToLastNum = 0;
while(iterations-- > 0){
Decimal newNum = lastNum+secondToLastNum;
Console.WriteLine(newNum);
secondToLastNum = lastNum;
lastNum = newNum;
}
}catch{
Console.WriteLine(
"Usage: Rabbits [Fib Index]\n"+
"\t[Fib Index] < 139");
}
}
}
Figure 2‑1 Rabbits.cs
In the 13th century Leonardo of Pisa discovered the Fibonacci series when he was studying how fast rabbits could bread in ideal circumstances. The C# source code shown in Figure 2‑1 well calculate how many rabbits you get after a certain number of generations (assuming ideal circumstances, of course).
This is an example of a console application. If you build this application you can run it from the command line, and pass it an argument indicating the number of generations you would like to calculate. To build this file using the command line compiler you would use the following command.
C:\>csc Rabbit.cs
You will notice that it is not necessary to use the /Target switch like it was when we built the source code from Figure 1‑1. This is because the default assembly built by the C# compiler is a console executable.
This is how you build a console application; simple stuff! However, the Rabbits.cs sample also shows some interesting things about C# in general, so I will highlight them here (however, we will cover most of this in more detail shortly).
· Like our last application, Rabbits.cs defines a class (arbitrarily named App) which defines an entry point function named Main().
· The Main() method in Rabbits.cs does something a little different by taking a parameter defined as an array of String objects. These are the command line arguments passed to the program.
· Rabbits.cs uses structured exception handling to handle errors. This is the try-catch code block that surrounds the bulk of the application. For those of you unfamiliar with exception handling, I will cover the topic later in this tutorial. In this case if anything goes wrong, the program prints out a usage string.
· You will notice from this sample that C# uses C syntax for its loop constructs. The while loop syntax in Rabbits.cs is exactly what it would be in C, C++, or Java.
· The sample code in Figure 2‑1 uses the static WriteLine() method of the Console type defined in the Framework Class Library to output text to the console window. Notice that an instance of the Console type was not necessary to call the method. This is because WriteLine() is defined as a static method.
· This sample uses a numeric type called Decimal which allows us to calculate many more generations than we would be able to do with a 32-bit integer value. The Decimal type is well suited for financial and scientific applications.
· Rabbits.cs is an iterative Fibonacci calculator. I wrote it that way just for fun. There is nothing about C# that would prohibit you from implementing this algorithm using recursion.
Exercise 2‑1 Build and Run a Console Application
1. The source code in Figure 2‑1 Rabbits.cs is a console Fibonacci number generator written in C#.
2. Like you did in Exercise 1‑1 type in or copy the source code and save it in a .cs file.
3. Compile the source code using the command line compiler.
1. Hint: The line that you would use to compile this application is as follows.
c:\>csc Rabbit.cs
4. Run the new executable.
2.2 Creating GUI Assemblies
Although I have already shown you how to build a GUI application in Figure 1‑1, I will do so again here. The following sample more closely resembles GUI applications in the real world.
using System;
using System.Drawing;
using System.Windows.Forms;
class App{
public static void Main(){
Application.Run(new TribbleForm());
}
}
class TribbleForm:Form{
TextBox generationsTextBox;
ListBox fibList;
public TribbleForm() {
generationsTextBox = new TextBox();
generationsTextBox.Location = new Point(16, 16);
Button tribbleButton = new Button();
tribbleButton.Location = new Point(16, 48);
tribbleButton.Size = new Size(100, 20);
tribbleButton.Text = "Tribble Count";
tribbleButton.Click += new EventHandler(OnClick);
AcceptButton = tribbleButton;
fibList = new ListBox();
fibList.Location = new Point(16, 88);
fibList.Size = new Size(192, 134);
fibList.Anchor = AnchorStyles.Top|AnchorStyles.Bottom
|AnchorStyles.Left|AnchorStyles.Right;
ClientSize = new Size(226, 235);
Controls.AddRange(new Control[]{ generationsTextBox,
tribbleButton, fibList});
Text = "Tribble Calculator";
}
void OnClick(Object sender, EventArgs e){
try{
Int32 iterations = Convert.ToInt32(generationsTextBox.Text);
if(iterations > 138)
throw new Exception();
fibList.Items.Clear();
Decimal lastNum = 1;
Decimal secondToLastNum = 0;
while(iterations-- > 0){
Decimal newNum = lastNum+secondToLastNum;
fibList.Items.Add(newNum);
secondToLastNum = lastNum;
lastNum = newNum;
}
fibList.SelectedIndex = fibList.Items.Count-1;
}catch{
MessageBox.Show("Enter a number from 1-138");
}
}
}
Figure 2‑2 Tribbles.cs
Since we are moving from console applications to the more advanced GUI application, I thought it would be appropriate to shift the theme from rabbits to tribbles. Tribbles.cs is a Fibonacci calculator with a graphical UI.
The source code in Figure 2‑2 is the most complex so far in this tutorial. Don’t worry if some of it is unclear; it’s not the code that we are concerned with here, but rather the compiling of the code.
To compile Tribbles.cs using the command line compiler use this command.
C:\>csc /Target:winexe Tribbles.cs
This command will create a GUI assembly executable. Like before, we need to use the /Target switch to indicate to the compiler that this is a GUI application.
Like the previous example, Tribbles.cs shows a lot more than just how to compile source code. It includes various usages of GUI types like Button and ListBox, as well as exception handling. Here are some highlights.
· Tribbles.cs defines two classes. An App class which has a static Main() method to be used as the programs entry point, and the TribbleForm class which is derived from form and implements the programs window.
· Most of the programs functionality exists in the TribbleForm class’ constructor and the OnClick() handler method for the button.
· Although you can write GUI code from scratch, it is far more common to use a forms designer to help with the task. The .NET Framework ships with a free forms designer called WinDes.exe. Of course Visual Studio.NET has forms designer as well. Both allow you to design your GUI using graphical tools, and the designer will create the code (much like the code you see in Figure 2‑2).
· C# GUI programs use a set of classes in the FCL known as the Windows Forms classes. I will discuss Windows Forms a little more in this tutorial, and then later in this series of tutorials I will cover Windows Forms in much more detail.
Exercise 2‑2 Build and Run a GUI Application
1. The source code in Figure 2‑2 Tribbles.cs is a GUI Fibonacci number generator written in C#.
2. Type in or copy the source code and save it in a .cs file.
3. Compile the source code using the command line compiler.
1. Hint: The line that you would use to compile this application is as follows.
c:\>csc /Target:winexe Tribbles.cs
4. Run the new executable.
2.3 Creating Code Library Assemblies
Knowing how to create executables with C# is important. But component development is going to become increasingly more important as the software industry evolves. More and more application code is going to be housed in reusable objects. These reusable types will often exist in binary modules or assemblies external of the main executable.
In fact, when you design web pages using C#, the entire code that implements the web page exists in a library assembly, with one or more objects derived from the Page type defined in the FCL. But we should start with a more mundane object library.
At the risk of absolutely hammering the Fibonacci series into the ground, I am going to make a Fib object that can be reused from other assemblies. I will admit that the Fib object is perhaps one of the strangest implementations of a Fibonacci generator in existence but it does demonstrate reusable object libraries in C# nicely.
using System;
public class Fib{
Decimal current;
Decimal last;
public Fib(){
current = 1;
last = 0;
}
private Fib(Decimal last, Decimal secondToLast){
current = last+secondToLast;
this.last = last;
}
public Fib GetNext(){
return new Fib(current, last);
}
public Decimal Value{
get{return current;}
}
}
Figure 2‑3 FibObj.cs
The source code in Figure 2‑3 defines an object named Fib that can be used from other assemblies. To compile this object into a library (using the command line compiler) do the following.
C:\>csc /Target:library FibObj.cs
Note: In this case we used the /Target switch to indicate that the assembly we are building is a library. This will create an assembly named FibObj.dll. FibObj.dll does not have a static Main() entry method defined, and it can not be executed directly. If you try to build a non-library assembly without an entry point method defined, the compiler will give you an error message.
We have now built a binary library containing an object with executable code. However, we need a regular executable assembly just to try the code.
using System;
class App{
public static void Main(){
Int32 index = 50;
Fib obj = new Fib();
do{
Console.WriteLine(obj.Value);
obj = obj.GetNext();
}while(index-- != 0);
}
}
Figure 2‑4 FibTest.cs
The sources for FibTest.cs can be used to test the FibObj.dll. However, special measures must be taken when compiling the sources in Figure 2‑4. This is because the source code refers to an object type called Fib, and the compiler needs to be told where the Fib type is implemented. That is done at compile time as follows.
C:\>csc /r:FibObj.dll FibTest.cs
The /r switch indicates to the C# compiler that the source code in FibTest.cs references objects implemented in FibObj.dll. There are a couple of points worthy of note here. First, the referenced file is the binary assembly file FibObj.dll, not the source code file FibObj.cs. Second, the lack of /Target switch indicates to the compiler that FibTest.cs should be compiled into the default assembly type, which is a console application.
Note: Remember the concept of assembly references. It comes up all of the time. Each time you use third party objects you will need to reference the assembly that implements the object. Every time you use a type defined by the FCL you are referencing an assembly that ships with the Framework Class Library. (You might have noticed that the command line compiler automatically references the assemblies in the FCL for you. The Visual Studio.NET environment requires you to explicitly set assembly references).
What is happening here between FibTest.exe and FibObj.dll is the simple foundation of a very important ability. That is the ability for objects to use each other without being compiled together (or even at the same time, or company, or content). This is a major improvement over the way C++ deals with object types, and is much more similar to the way Java deals with types. (However, it would take a lot of text to explain it, but C# and the .NET Framework’s ability to compose your application of binary libraries of objects is far advanced of Java or any other mainstream platform. In a later tutorial on the Common Language Runtime, I will address this in more detail).
Before moving on, lets switch gears a little bit and just walk through some of the interesting points about the source code in Figure 2‑3.
· This is really the first sample code in this tutorial that is an object-oriented construct. The other samples have defined classes, but they were little other than place-holders for functions. The Fib type defines a real object to be instantiated and used by code.
· Note that the first two lines in the Fib type define member fields of type Decimal. These describe the instance state of an object of type Fib.
· Fib defines a constructor that sets the state of an initial object to current = 1 and last = 0. This constructor is automatically called by the .NET Framework when an object of type Fib is created using the new keyword in C#.
· Fib defines a second constructor that is marked as private. Any member field or member method of a class that is marked as private can only be accessed by methods in that type. That means that only code inside of the Fib class can new-up a Fib object using the second constructor that takes two parameters.
· The GetNext() method is a public method which means that it can be called by code outside of the Fib object, such as the code in the FibTest.cs. GetNext() is defined to return an instance of the Fib type which it does by creating a new object and returning it.
· The Value member of the Fib type is a special kind of method called a property. Properties are special because you can reference them with the same syntax that you would use to reference a member field. FibTest.cs references this property with a line of code that looks like this.
Console.WriteLine(obj.Value);
It is good for us to begin talking about C# syntax in context with real code like we have been doing. However, when we get to section 4 Language Concepts, I will address the C# syntax in more detail, so don’t be concerned if some of the syntax of these last couple of samples has been a little unclear. The most important thing so far is that you are comfortable with the concepts of assemblies, and how to build them.
Integrated development environments are great, but it is also good that we spent some quality time with the command line compilers. The command line compilers don’t hide any details from you, and they make it clear what is going on and what is possible. Now, we are ready to talk about Visual Studio.NET.
Exercise 2‑3 Build a Code Library
1. The source code in Figure 2‑3 FibObj.cs is a C# reusable object to be compiled into a library.
2. Type in or copy the source code and save it in a .cs file.
3. Compile the source code using the command line compiler.
1. Hint: The line that you would use to compile this application is as follows.
c:\csc /Target:library FibObj.cs
4. Do a directory to confirm that a file named FibOjb.dll was created.
Exercise 2‑4 Build an App to Test a Code Library
1. The source code in Figure 2‑4 FibTest.cs is a brief console application designed to consume or test the object in FibObj.dll.
2. Type in or copy the source code and save it in a .cs file.
3. Compile the source code using the command line compiler.
1. Hint: The line that you would use to compile this application is as follows.
c:\>csc /r:FibObj.dll FibTest.cs
4. Run the new executable.
3 Visual Studio.NET
If you are going to be doing a lot of C# programming, then it is pretty likely that you will find yourself using Visual Studio.NET. Visual Studio.NET manages a lot of the details of project management, building, debugging, and GUI development. I probably spend about 50% of my C# programming time using Visual Studio.NET and the other 50% just writing quick programs with notepad (to solve a quick problem or test something).
Either way, I would be doing you a disservice if I did not at least run through a few features of VisualSudio.NET. First things first, I should describe what VisualStudio.NET (or VS.NET) is. VS.NET is an Integrated Development Envrionment or IDE. IDE’s commonly include source code editors (usually pretty fancy ones), as well as project managers, online-help and debuggers, all in one software package. The idea is to keep all of your programming related tasks in one place. VS.NET does a good job of this.
When you run VS.NET you are presented with a Start Page. This page has links that help you create a new project or open an existing one. VS.NET projects are arranged in a hierarchy of projects and solutions. Solutions are sort of a collection of projects. Most often you will have one project per solution, but if you are creating a program with an executable and a library of reusable classes, then you might make a solution with two projects, for example.
3.1 Creating a Project
When you select new project you are presented with a screen as follows.
Figure 3‑1 New Project in VS.NET
The type of project that you will select from the pane on the left is Visual C# Projects (unless you don’t want to create a C# project). Then you will build your project from a template on the right. The most common template types.
· Empty Project – This one is hidden down near the bottom of the templates, but it can be the most useful. This is because with the Empty Project template VS.NET lets you add all of the source code modules and it doesn’t set anything up for you. Sometimes, this is what you want.
· Windows Application – This template sets up a project for building a GUI application. It will create a couple of source code modules for you, one of which already includes a class derived from Form to be your main window. When you create a project from this template, it loads up automatically in the Forms Designer tool of VS.NET which lets you drag and drop buttons and other GUI elements onto your window.
· Console Application – If you want to create a console or command line application you should use this template.
· Class Library – If you want to make a library of reusable types, you should use the Class Library template. The assembly that you build from this project will not be executable, but you can use the types in this file from other executables.
· ASP.NET Web Application – This template creates a web forms application for creating web-pages with C#. This is an advanced project type, and you should consider writing a few console or GUI applications in C# before attempting a web-form. I will discuss web forms in detail in a tutorial later in this series.
· ASP.NET Web Service -- This template creates a web service with C#. This is an advanced project type. I will discuss web services in detail in a tutorial later in this series.
3.2 Working with Projects
Once you have created a C# project to work with in VS.NET you can do your regular programming tasks. This includes editing source code files, compiling, and debugging.
Figure 3‑2 Solution Explorer
The Solution Explorer is your command central for dealing with a solution (which is what contains one or more related projects). In Figure 3‑2 the Solution Explorer shows a solution named LearningC# which contains four projects.
From the Solution Explorer you can bring up source modules. Add source modules to a project. Debug a project or add and remover projects from a solution. The general rule is that you right-click your mouse on the item you want to affect, and then select the action from the menu that pops up.
If I wanted to add a .cs source file to the project FibObj shown in Figure 3‑2, I would simple right click on the icon for FibObj, and then select Add->Add New Item.
If you choose to program in C# without using Visual Studio.NET, you can write all the same kinds of software that you can write with VS.Net. If you do choose to use Visual Studio.NET, I would suggest setting aside a couple of hours to just create several trial projects. Build them, debug them, and try making minor modifications to them. This will introduce you to the look and feel of VS.NET and will make it more productive for you when you have to do real work with the tool.
4 Language Concepts
So far you have seen four C# programs, and one C# library containing a reusable class. You have learned how to build your C# assemblies using the command line compiler, as well as with Visual Studio.NET. So you are ready to do some C# programming.
What I haven’t done yet is just talk about the syntax of C#. (The reason I have waited this long is simply that I wanted you to be familiar with how to build code, so that you can test the code snippets that you will see through ought the rest of this tutorial.)
C# has a C based syntax much like C++ or Java. If you are familiar with these other languages than you probably recognized use of the curly braces ({}), and the class keyword, the semi-colons (;) that go at the end of each line, etc. If you are familiar with C, C++ or Java, then you will find C# fairly simple.
4.1 Syntax Ground Rules
The following are some basic rules that apply to C# code syntax.
· C# is a case-sensitive language. This means that method and variable names are distinguished by their case. For example, a method named Hello() and a method named hello() would be seen as different methods in C#.
· White space means nothing in C#. White space includes spaces, carriage returns, and tabs. The following two code blocks are seen as exactly the same by the C# compiler.
if(x==y){DoSomething()}
or
if(x==y){
DoSomething();
}
You should use white space to increase the readability of your source code.
· The semicolon (;) is used to indicate the end of a statement. The only exception is the end of a code block (}), which does not require a terminating semicolon (;). It is because of this rule that the compiler can safely ignore carriage returns (where other languages use carriage returns to indicate the end of a statement).
· Statements can be grouped into a single unit called a code block. This is done by putting the statements inside of a pair of curly braces ({}). Code blocks can be nested.
4.2 Primitive Types
The table shown later in this section shows the types in the Framework Class Library that are treated as primitive types by C#. Primitive types are special because the C# language will allow direct assignment, math operations, and comparison of primitive types. For example, the following line of code would be legal in C# because both variables are of type Int32
x = x + y;
Meanwhile, the very same line of code would not be legal of the x and y variables were of some non-primitive type such as Form or FileStream.
One other interesting point about the primitive types in C# is that the C# language provides Aliases for the type names. For example, whether you declare a variable of type Int32 or of type int, you are always really declaring an Int32 variable. Choosing which name to use in your source code is a matter of style. The most important thing is that you recognize that the FCL name for a primitive type and the alias name for a primitive type both refer to the same underlying type.
C# Primitive
C# Alias
Description
Boolean
bool
Indicates a true or false value. The if, while, and do-while constructs require expressions of type Boolean.
Byte
byte
Numeric type indicating an unsigned 8-bit value.
Char
char
Character type that holds a 16-bit Unicode character.
Decimal
decimal
High-precession numerical type for financial or scientific applications.
Double
double
Double precession floating point numerical value.
Single
float
Single precession floating point numerical value.
Int32
int
Numerical type indicating a 32-bit signed value.
Int64
long
Numerical type indicating a 64-bit signed value.
SByte
sbyte
Numerical type indicating an 8-bit signed value.
Int16
short
Numerical type indicating a 16-bit signed value.
UInt32
uint
Numerical type indicating a 32-bit unsigned value.
UInt64
ulong
Numerical type indicating a 64-bit unsigned value.
UInt16
ushort
Numerical type indicating a 16-bit unsigned value.
String
string
Immutable string of character values
Object
object
The base type of all type in any managed code.
Figure 4‑1 Primitive Types
Here are a couple of points worthy of note about the primitive types in C#.
· C# has a String type, rather than requiring that strings be represented as an array of Char variables like C or C++.
· C# has a Boolean type, and it is not valid to use a numerical type where a Boolean expression is required. C# also includes two keywords for indicating true and false.
· Since C# uses types defined in the FCL as its primitive types, C#’s primitive types are Object’s just like all other types in the system. Said another way, all C# primitive types are derived from Object, and as objects do object-like things such-as implement instance methods, etc.
The following C# method shows the use of some of the primitive types in C#. The primitive variables are shown in red.
public static void Primitives(){
Int32 x = 10;
Int32 y = x/3;
Console.WriteLine(y);
Decimal d = 10;
Decimal e = d/3;
Console.WriteLine(e);
String s = "Hello";
String t = s+" C#";
Console.WriteLine(t);
Boolean f = (x==y);
Console.WriteLine(f);
}
Figure 4‑2 Primitive Code Snippit (Excerpted from Language.cs)
The code in Figure 4‑2 is the first in this tutorial to not be a comple compileable C# module. If you would like to try compiling or running the code in Figure 4‑2 you can cut and paste the function into a console application, complete with a Main() method, or you can build and run the Languages.cs file that is packaged with this tutorial.
4.3 Expressions and Operators
Expressions are an integral part of programming, and C# treats expressions much the same as many languages. Operators such as +, *, and / are used to manipulate or affect the data in variables, and operators have precedence. And as is common, operator precedence can be modified using parenthesis. Expressions like the following are common in programming, and are also common with C#.
Int32 x = 10*(y+z);
C# is a very type safe language, however, and expressions are strongly typed. For example, the preceding line of code is an expression of type Int32. The compiler will automatically convert between numeric primitive types, so long as no data-loss occurs in the conversion. However, some conversions that are common in languages like C or C++ are illegal in C#.
An example of such an illegal type conversion in an expression is the conversion from an Int32 or some other numeric type to a Boolean type. The following code would be valid in C, but is an error in C#, because a Boolean expression is expected, and an Int32 expression is provided.
Int32 x = 10;
while(x--) DoSomething();
The operators in the following table can be used in C# expressions.
Operator category
Operators
Arithmetic
+ - * / %
Logical (boolean and bitwise)
& | ^ ! ~ && || true false
String concatenation
+
Increment, decrement
++ --
Shift
<< >>
Relational
== != < > <= >=
Assignment
= += -= *= /= %=
&= |= ^= <<= >>=
Member access
.
Indexing
[]
Cast
()
Conditional (Ternary)
?:
Delegate concatenation and removal
+ -
Object creation
new
Type information
is sizeof typeof
Overflow exception control
checked unchecked
Indirection and Address
* -> [] &
Figure 4‑3 C# Operators
4.4 Methods
C# like, most languages, organizes code in terms of functions. However, unlikely some languages, C# functions are always member functions or methods of a type (usually a class). Methods can take any number of parameters, or no parameters. Methods can return any type or void to indicate no return type.
There are two main types of methods. These are static and instance methods. Static methods are defined by using the static keyword. Static methods can be called without creating an instance of the encapsulating type. This is the syntax for calling a static method.
TypeName.MethodName();
The following sample shows the definition and use of several methods, shown in red.
// The next three lines of code test the methods in SomeClass
SomeClass.Method1();
SomeClass obj = new SomeClass();
Int32 z = obj.SumMethod(12, 13);
Console.WriteLine(z);
}
}
class SomeClass{
public static void Method1(){
Console.WriteLine("Here I am inside of SomeClass.Method1");
}
public Int32 SumMethod(Int32 x, Int32 y){
return x+y;
}
}
Figure 4‑4 Methods Code Snippit (Excerpted From Language.cs)
Notice that the class SomeClass defines two methods named Method1 and SumMethod. Method1 is static, and SumMethod is an instance method. The code that calls Method1 does not require an instance of SomeClass, however the code that calls SumMethod needs to new-up an instance of SomeClass first, before calling the method.
Note: C# use the curly braces {} to indicate the beginning and end of a code block. This is how you define the beginning and end of a method.
Meanwhile, notice that SumMethod takes two parameters and has a return value, while Method1 takes no parameters and returns nothing.
Note: The first line of source code in Figure 4‑4 indicates a comment line in C#. You can also use the C-style /* comments */ in C#.
Your C# applications will be structured as a collection of types (usually classes) and the methods defined by the types.
4.5 Reference Types and Value Types
All managed code (and therefore C# code) supports two kinds of types. Reference Types and Value Types. The difference is in where, in memory, the data for the type is stored.
The concepts of reference types and value types are best described by example. So lets take for example the String and Int32 types. The String type is a reference type, and the Int32 type is a value type. Both types are primitive types, you can call methods on both types, and both types are ultimately derived from Object.
But they do differ in the way variables for these types are handled. The basic idea is that a String variable will always be a reference to a String object in memory, or it will be a null reference (indicating that it references no object). Meanwhile an Int32 variable is the value of the integer, and does not reference anything in memory. So you can assign the value null to a String reference variable, but you cannot assign the value null to an Int32 value variable.
The following two lines of code look very similar.
Int32 x;
String s;
But there is a fundamental difference. The first line declares an Int32 variable x, which means that an Int32 variable was created with an initial value of zero. However, the declaration of the String variable s did not cause an instance of String to be created, so s begins life as a null reference. It isn’t until you assign a String or new-up an instance of a String that s references an object.
Most types in the Framework Class Library (FCL) are reference types. However, most of the primitive types are value types (String being the noteworthy exception).
The way to know whether a type is a Reference type or a Value Type is to see how the type is listed in the FCL documentation. If is listed as a class, then it is a reference type. If it is listed as a struct(ure), then it is a value type.
You can decide whether your custom types are reference types or value types by using the class or struct keywords respectively. Creating struct types is an advanced topic, and you should declare class types as the rule.
4.6 Arrays of Data
As a programmer you have probably worked with languages that support array variables. C# also allows you to use arrays, and for the most part the concepts are the same as any other language. There are some differences though.
public static void Arrays(){
// A one dimensional array of 10 Bytes
Byte[] bytes = new Byte[10];
// A two dimensional array of 4 Int32s
Int32[,] ints = new Int32[5,5];
// A one dimensional array of references to Strings
String[] strings = new String[10];
}
Figure 4‑5 Array Declaration Code Snippit (Excerpted from Language.cs)
The code snippit in Figure 1‑1 shows the declaration of three arrays. The first two are a one dimensional and two dimensional array of the value type Byte. The syntax is pretty straightforward, and what you get is an array of Byte variables with the initial value of zero.
In the third example a one dimensional array of String references is created. In this case, no String objects have been created (although an array has been created), and each element in the array is set to its initial value of null.
Another interesting thing about arrays in managed code is that the array itself is an object. When you declare an array in C# the C# compiler automatically derives the array from the Array class in the FCL. This is actually a very cool feature of managed code, because the Array class has a number of useful methods for doing things like finding the length of an array, or the number of dimensions of an array, etc.
Arrays themselves are reference types, although as we have seen their elements can be either instances of value types, or references to reference types.
If arrays seem confusing at first, don’t let it bother you too much. Just use them they way you would have used arrays in any language, and things will typically work they way you intuitively expect them to.
4.7 Conditional Statements
Like any other programming language, C# must have the ability to control the flow of code logic with conditional statements.
The most common conditional statement is the if statement, which is structures as follows.
if(/*condition*/){/*conditional code*/}
The if statement in C# also supports the use of an else clause.
public static void Conditional(){
Int32 x = 10;
Int32 y = 20;
// if statement
if(y == x){
Console.WriteLine("y equals x");
}
// if statement with an else clause
if(y == x){
Console.WriteLine("y equals x");
}else{
Console.WriteLine("y does not equal x");
}
}
Figure 4‑6 if Code Snippit (Excerpted from Language.cs)
In the example above, only the call to Console.WriteLine() shown in green is executed, because y does not contain a value equal to the value in x.
The lesser common of the two conditional statements in C# is the switch statement. The switch statement is used to select between a number or case code blocks or a default code block, shown as follows.
switch(x){
case 1:
Console.WriteLine("x equals 1");
break;
case 2:
Console.WriteLine("x equals 2");
break;
default:
Console.WriteLine("x does not equal 1 or 2");
break;
}
Figure 4‑7 switch Code Snippit (Excerpted from Language.cs)
4.8 Loops
C# offers the typical looping constructs like for and while, plus a new one that can be pretty useful.
public static void Loops(){
Int32 index;
// while loop
index = 10;
while(index != 0){
Console.WriteLine(index);
index--;
}
// for loop
for(index = 0;index<100;index++){
Console.Write(index);
Console.Write("\t");
}
// do-while loop
index = 0;
do{
Console.WriteLine("Happens at least once");
}while(index < 0);
// foreach loop
Int32[] myArray = new Int32[]{10, 20, 30, 40};
foreach(Int32 i in myArray){
Console.WriteLine(i);
}
}
Figure 4‑8 Looping Code Snippit (Excerpted from Language.cs)
The while, for and do-while loops work just like they do in C or C++. However, the new foreach loop keyword warrants some discussion. The foreach loop is used to iterate through each element in a collection of items. This collection can be either an instance of a collection class or an array, which is what the code in Figure 4‑8 uses.
The syntax for foreach is a little different than for, because you don’t control the iteration nor do you use an index variable. What you provide is the type of the elements contained in the collection (such as the Int32 in the example), a variable name (the variable i in the example) to indicate the current element to the code inside the loops code block, and then you use the in keyword to indicate which instance of a collection (myArray in the example) you want to iterate over.
4.9 Error Handling
Error handling in managed code and C# is always done through the use of structured exception handling. What this means is that you should not write methods that return values indicating failure. Instead, you should throw exceptions, so that the caller of your function can find out about the failure this way.
The reusable types in the Framework Class Library indicate error situations this way. So even if you never throw an exception, you will most likely have to catch a few just to make sure that your application is not terminated (which is what happens if an exception goes unhandled).
public static void Exception(){
// try-catch
try{
Int32 index = 10;
while(index-- != 0){
Console.WriteLine(100/index);
}
}catch(DivideByZeroException){
Console.WriteLine("A division by zero exception was caught");
}
Console.WriteLine("Exception caught; code keeps running");
// try-finally
try{
return;
}finally{
Console.WriteLine("Code in finally blocks always runs");
}
}
Figure 4‑9 Exception Code Snippit (Excerpted from Language.cs)
The loop inside of the try-catch block in Figure 4‑9 eventually reaches zero and causes the division to throw a DivideByZeroException. All exception types are derived from Exception, which is a type in the Framework class library.
Note: If you do not know what type of exception is thrown by a certain error situation, you should purposefully cause the exception to happen, and then the system will tell what type of exception went unhandled.
When you use a try-finally construct, the code inside of your finally block is guaranteed to execute, whether or not an exception is thrown or something else happens, such as a function return. The finally block is great for method cleanup code.
5 Simple Object Oriented Programming
The .NET Framework is an object oriented platform, from the ground up. Since C# is a language built for the .NET Framework, it is also object oriented. The next tutorial in this series covers object oriented concepts in some detail. However, to do just about anything you do need some introduction.
In this section I am going to describe how to use existing defined types, as well as how to do some very basic extending of existing types.
5.1 Using Types
You will make heavy use of existing objects in your C# programs. This is because the Framework Class Library as well as third party component libraries provide so much useful functionality.
There are two basic ways to use types. One is to call static methods of a type. This is similar to calling a global function, only the static method is logically grouped with other methods by being defined in a type. An example of this is the static WriteLine() method of the Console type. Many of the code examples in this tutorial have written text to the console window using the Console type. The following line of code is an example of such use of the Console type.
Console.WriteLine(“Something to write”);
The other way of using types is to create instances of the type. This is more common when there is some data or state associated with the type (unlike Console which is really just a bunch of functions). An example of this would be to use the new keyword to create a new FileStream object, and then use the new instance of the object to read or write data from the file.
using System;
using System.IO;
class App{
public static void Main(String[] args){
try{
FileStream fromStream =
new FileStream(args[0], FileMode.Open, FileAccess.Read);
FileStream toStream =
new FileStream(args[1], FileMode.Create, FileAccess.Write);
Byte[] buffer = new Byte[fromStream.Length];
fromStream.Read(buffer, 0, buffer.Length);
toStream.Write(buffer, 0, buffer.Length);
}catch{
Console.WriteLine("Usage: FileCopy [FromFile] [ToFile]");
}
}
}
Figure 5‑1 FileCopy.cs
The FileCopy.cs sample code shows the instantiation and use of the FileStream object. In fact, the sample code uses the new keyword to create two instances of the FileStream object to represent two different files on the user’s hard drive.
When you use the new keyword to create an instance of an object you must indicate what type of object you wish to create, and then you must provide a method parameter list (even if it is an empty one). This is because when you use new to instantiate an object, a special method defined by the object called a constructor is called, so that the object can initialize the state of its data.
If you look at the documentation for the Framework Class Library, you will see that many types define more than one constructor, so that you can create objects using a variety of data to set its initial state.
In the code in Figure 5‑1 I create a FileStream object and assign a reference to a variable with a line of code that looks like this.
toStream = new FileStream(args[1], FileMode.Create, FileAccess.Write);
In this code, I am creating a new FileStream object, and the items in red are parameters to pass to the constructor of the object, once its memory has been allocated by C#. The variable name toStream is of type FileStream, which is why it can reference my new object.
Once I have a reference to an object I can do things such as access methods or other members on the object. The following line of code is taken from Figure 5‑1 and shows how to call an instance method on an object, once you have a reference to the object.
fromStream.Read(buffer, 0, buffer.Length);
In this case, the fromStream reference variable indicates which object I want to call the Read() method for. The Read() method itself accesses the object referenced by fromStream to know which file in the file system to read data from.
In object oriented code, and certainly C# code you will be newing up objects and calling methods on them left and right. When you are finished using an object the .Net Framework will simply free up the memory for the object for you. And to know what types exist, and what you can do with a specific type, you should look at sample code, and read the online documentation for the Framework Class Library.
5.2 Typecasting
Typecasting or casting can be used to do one of two things. First, it can be used to change the data of one primitive type to another compatible primitive type. For example, you could typecast a Double value into an Int32 value as follows.
Double d = 1.34;
Int32 i = (Int32) d;
The casting operator is the begin and end parenthesis () inside of which is indicated the type that you wish to cast the expression to. This is common when working with numeric primitive types.
The second kind of typecasting is when you want to manage an object as though it were of another type. In this case, neither the data nor the object changes (unlike the previous example), but your view of the object changes. This is only possible when the type you are casting to is a base class (or interface off) the object that you are casting.
Stream s = new FileStream();
At first glance you might think that the preceding line of code would be an error in a type-safe language like C#, because Stream and FileStream are not the same type. It is not an error, however, and the reason is that Stream is a base class of FileStream. Another way of saying it is that FileStream is a Stream, but it is an extension of Stream. This is an example of an implicit cast, because it does not require the use of the casting operator ().
A more similar but more interesting cast is the one that might happen later on when you want to handle your object as a FileStream again.
FileStream f = (FileStream) s;
This is called an explicit cast, because the cast operator is required. The reason that the cast operator is required is that we are casting to a more derived or more extended type. Stream is a base class of FileStream, so the cast from Stream to FileStream is a cast up.
Remember that in both the explicit cast up and the implicit cast down you are still referencing the same object in memory, but you are referencing it with a different type of reference variable.
You might wonder why you would ever cast an instance up or down, and the answer is usually that you want to use a specialized object in a generic fashion. For example, the collection classes in the FCL store instances of Object. Since every type you use or create is derived from Object, you can store anything in one of these collection classes, but when you call the Add() method to add an item, you are implicitly casting your instance down to Object. Then when you want to reference the object again you must explicitly cast it back up to a reference more useful than an Object reference.
If you want to test the type of a referenced object you can use the is operator.
Object obj = new FileStream();
Boolean f = obj is Stream;
The variable f in the preceding code will be set to true because obj references a Stream (or more specifically a FileStream) object.
5.3 Extending Types
Even as a beginner C# programmer you must create types. From our very first code sample in Figure 1‑1 we were creating classes that extended other classes. And even if you are writing only a simple console application, your Main() method must be in a class, and that class is implicitly derived from Object.
When you create a new class that extends the functionality of an existing class the new class is called a derived class. Now the original class is a base class of your class. The following code shows a type named MyForm that is derived from the existing type Form.
MyForm:Form{
}
The syntax for indicating a base class for your class is simply to follow your class’ name with a colon (:) and the name of the base class.
The reason to created derived classes is to get functionality for free. For example, every window in the same shares some base functionality with every other window. So it would be silly to write the code to do all of the drawing and resizing of windows over and over again. The solution to this is to create a base class that does these base things, then each specific class can derive its functionality from the base.
Refer back to the code in Figure 1‑1 to see a very simple example of a functioning application that includes a derived class.
Class derivation is a very common part of object oriented programming, and it is used heavily in the Framework Class Library. Pay close attention to the hierarchies of the types in the Framework Class Library. A base class has a lot to do with how its derived classes behave.
6 C# in Action
We have covered a lot of C# concepts in a relatively short tutorial and there is plenty more to be learned. In the next tutorial in this series I will cover object oriented programming with C# in much more depth. We will discuss advanced object creation and a variety of fun things.
To close this tutorial, I thought it would be fun to show the source code to a couple of fully functioning C# applications. The first is a C# implementation of a text editor (like notepad), and the second is a C# implementation of a web-based game.
6.1 C#Pad.exe – Sample Application
The C#Pad sample application source code is packaged with this tutorial. It implements a text-editor much like notepad. The application was designed using Visual Studio.Net and C#.
There are a number concepts used in the sources for C#Pad that have not yet been discussed in a tutorial in this series. However, if you browse through the source code, you will most likely be able to get the gist for how it works.
The code in the method InitializeComponent() is the only code in this file that was generated by a tool (the Visual Studio.NET form designer). This function sets up all of the controls and menus for the program, before the window pops up. This function also hooks a number of member functions of the CSPad class to events such as menu-click events, with code that looks something like this.
this.menuFormatWordWrap.Click += new
System.EventHandler(this.menuFormatWordWrap_Click);
(The preceding line of code tells the system that the menuFormatWordWrap_Click method should be called when the user raises the Click event for the Word Wrap menu item).
The core functionality of this application is an instance of the TextBox class. The instance is placed on the Form derived class, and it is the “editor”. Menus are added just to allow for features like loading and storing of files.
Enjoy C# and the .NET Framework. Have fun, and I will see you in the next tutorial!
No comments:
Post a Comment