.NET for Visual FoxPro Developers

Chapter 3
Introduction to C#

Some developers are “afraid” of C# because C-based languages have a reputation for being advanced, and therefore difficult to learn and use. This chapter aims to dispel any misgivings you might have by providing a side-by-side comparison of C# and Visual FoxPro—you’ll be amazed at their similarities. It will also help you leverage your knowledge of Visual FoxPro in learning the basics of C#.

There has been a lot of excitement generated about C#, Microsoft’s newest programming language. It’s a very clean and elegant implementation of an object-oriented .NET language. However, new doesn’t mean it’s untested or “wet behind the ears”. Microsoft put C# to the test and used it to write the .NET Framework’s base classes (For the record, the .NET Framework could have been written in Visual Basic .NET. C# was chosen because most developers working on the .NET Framework were C++ developers and the C# compiler was completed before the VB .NET compiler).

Of all the existing programming languages, C# is the most similar to Java. The October, 2000 issue of Java™ Report states: “C# is Java with single inheritance; automatic memory management; 41 of the same 43 operators; all the same flow of control statements, plus foreach; 38 of 50 Java keywords; and if you allow for synonyms, 46 of the same 50 keywords.”

Although C# is new, it still has many features that are associated with more mature languages such as Visual FoxPro. In this chapter you will start out learning the basics—strong typing vs. weak typing, variables, constants, data types, conditional statements, flow of control loops, jump statements, and converting between different data types.

Although I will touch on basic object-oriented elements of C# in this chapter, such as declaring a simple class, the real discussion of C# and VB .NET’s object-oriented features takes place in Chapter 5, “Object Orientation in C# and Visual Basic.NET”.

Text Box: ¥

For a side-by-side comparison of C#, Visual Basic.NET, and Visual FoxPro’s operators and keywords, check out “Appendix A – Language Comparison Tables”. For a comparison of each language’s data types, see “Appendix B – Data Type Comparison”.

Weak typing vs. strong typing

One of the biggest differences between C# and Visual FoxPro is weak typing vs. strong typing. Visual FoxPro is a weakly typed language. This means that you can store a value of any type in a variable. For example, as shown in the following code, you can store a DATETIME value in a variable, save a string in the same variable, and then turn around and store an integer in the same variable without having the Visual FoxPro compiler complain:

LOCAL MyVariable

MyVariable = DATETIME()

MyVariable = "Hello World!"

MyVariable = 6

While this may be incredibly flexible, it has its downside. First of all, from a readability perspective, it’s far better to create three different variables to hold each of these values, rather than reusing the same variable. Second, and more importantly, weak typing allows bugs to sneak through. For example, if you store an integer value in MyVariable, and then attempt to run the following code, you’ll get an “Operator/operand mismatch” error at runtime:

REPLACE CustomerName WITH "Customer " + MyVariable IN Customer

You would probably notice this bug pretty quickly when you run the code—but that’s
the problem—“when you run the code”. What if the code never gets run until it’s in the end-user’s hands?

In contrast, C# is a strongly typed language (VB .NET is also a strongly typed language, but C# is more strongly typed. See Chapter 4, “Introduction to Visual Basic .NET” for details). In the world of variables, strong typing means that you must declare the type of all variables. For example, the following code creates a date variable named MyDate, a string variable named MyString, and an integer variable named MyInteger:

DateTime MyDate;

string MyString;

int MyInteger;

If you do not declare the type of a variable, the C# compiler gives you an error and exits without finishing the compilation of your application.

Another common error in Visual FoxPro coding that causes bugs is using a variable without declaring it first. For example, the following code doesn’t declare MyVariable before using it:

MessageBox("Hello World" + MyVariable, 0+48, "Weak Typing Demo")

The Visual FoxPro compiler does not catch this kind of error. However, when this
code is executed at runtime, your end users see a message such as “Variable ‘MyVariable’ is not found”.

In contrast, C# does not allow you to use variables unless they have been declared and initialized first. For example, if you create the following code:

string MyString;

MessageBox.Show(MyString, "Strong Typing Demo");

the compiler displays the error “Use of unassigned local variable ‘MyString’”.

To change these lines of code so they work properly, you need to assign an initial value
to MyString:


string MyString = "I love strong Typing!";

MessageBox.Show(MyString, "Strong Typing Demo");

C#’s strong typing also applies to variables that store object references. This aspect of strong typing is covered in Chapter 5, “Object Orientation in C# and VB .NET”.

The practical value I’ve seen in my coding boils down to this—in Visual FoxPro, when I create a page worth of code, invariably there are a few bugs. I can’t find those bugs until I step through the code line by line. With .NET languages, I can write the same amount of code and the compiler will catch a majority of the syntax bugs (sorry, no compiler will catch all of the bugs in your application logic!).

A simple C# program

Although this chapter is not about object-orientation, you need to know the basics of creating a simple program and a simple class in order to understand how the basic constructs of the C# language work in context. So, you will create the obligatory “Hello World” program now. The call to Console.WriteLine displays “Hello .NET World!” in a .NET console application:

using System;

 

class MyFirstCSharpProgram

{

  public static int Main()

  (

       Console.WriteLine("Hello .NET World!");

       return 0;

  }

}

There are a few things to note about this code. Every C# program must have a Main method. This method is called when the program is first executed and it must return nothing (void) or an integer. Optionally, you can specify that the Main method accept a string array parameter. If any command-line parameters are sent when the program is executed, they are placed into this array:

using System;

 

class MyFirstCSharpProgram

{

  public static int Main(string[] args)

  {

       // Here is a comment

       Console.WriteLine("Hello .NET World"); //Here is another comment

 

       // Process any parameters

       if (args.Length > 0)

       {

          // Build a string containing all parameters

          String Parms = "";

          foreach (string parameter in args)

          {

              Parms += parameter + " ";

          }

          // Display the parameters

          Console.WriteLine("\nParameters: " + Parms);

       }

 

       return 0;

       }

}

The code I’ve placed in this sample for processing parameters contains some C# features I haven’t covered yet, such as array handling and foreach loops. You will find information on these features later in this chapter.

C# syntax

In this section, I’ll give you an overview of C#’s syntax compared to Visual FoxPro’s. I’ll start out by showing you the two things that take the most getting used to in C#.

Case sensitivity

Visual FoxPro is a case-insensitive language. The following code declares a variable “MyString” and assigns it a value. In the next line, I use that variable, but I refer to it as “mystring” (all lowercase). In Visual FoxPro, the two names refer to the same variable.

MyString = "Visual FoxPro is case-insensitive"

MESSAGEBOX(mystring, 0+48, "Case insensitive")

C# is a case-sensitive language. “MyString” and “mystring” refer to two completely different variables. In addition, all keywords are in lower case. If you enter a keyword with mixed case, the compiler flags it as an error. Some developers have a hard time getting used to C#’s case sensitivity. However, if you already write case-sensitive code in Visual FoxPro, it’s not as big an issue.

Semicolons and multi-line statements

In Visual FoxPro, you don’t need to put an ending character after each statement. However, if a single statement continues over multiple lines, you need to add a semi-colon, which is Visual FoxPro’s continuation character:

MESSAGEBOX("This command continues " + ;

  "to another line", 0+48, "Continuation Character")

This is the opposite of how C# works. As you can see by looking at the code sample in the previous section, all C# statements must end in a semicolon. This allows a single statement to continue to the next line without a continuation character. For example:

MessageBox.Show("This command continues " +

  "to another line without a continuation character");

For Visual FoxPro developers using C#, this can be a little irritating at first, but it doesn’t take long before you’re typing a semi-colon without even thinking about it.


Code placement

In Visual FoxPro, you place code in classes or in standalone functions, procedures, and programs. This allows you to do a mix of both procedural and object-oriented programming.

In C#, all code must be contained within classes or structures which are similar to classes. Structures are discussed in detail in Chapter 5, “Object-Orientation in C# and Visual Basic .NET”. Given a choice between the two approaches, I think C#’s approach is better. VFP’s approach may be more flexible, but it can, and often does, promote poor object-oriented programming practices.

Grouping statements together

The convention in Visual FoxPro for grouping statements together is to use a beginning and ending keyword. For example, you group all the members of a class together within DEFINE CLASS and ENDDEFINE keywords. You group code contained in class methods within PROCEDURE and ENDPROC or FUNCTION and ENDFUNC keywords:

DEFINE CLASS MyClass AS Session

 

  PROCEDURE MyProcedure

  ENDPROC

 

  FUNCTION MyFunction

  ENDFUNC

 

ENDDEFINE

C# uses curly braces consistently to group code together. Here’s a code sample that defines a class named CodeGroupingDemo and it contains two methods named MyMethod1 and MyMethod2:

public class CodeGroupingDemo

{

 

  public void MyMethod1()

  {

  }

 

  public void MyMethod2()

  {

  }

 

}

Although at first you may find this style of coding difficult to read, it is less verbose than Visual FoxPro (and VB .NET for that matter), it means less typing, and it’s consistent throughout the language.

Comments

In Visual FoxPro, you use the familiar asterisk (*) to indicate a comment at the beginning of a line. You can also use a double ampersand (&&) to add a comment at the end of a line.

C# uses two forward slashes consistently to indicate a comment either at the beginning of a line or at the end of a line of code. For multi-line comments, it uses a forward slash and asterisk (/*) at the beginning of the first comment line and an asterisk and forward slash  (*/) at the end of the last comment line:

public class CommentDemo

{

  /* Multi-line comment

     Here's the second line */

 

  // Comment

  public void MyCommentedMethod()  // Another comment

  {

  }

}

C# has a very cool feature that allows you to document your code using XML. See the “XML Documentation” section at the end of this chapter for details.

Namespaces

All classes in .NET belong to a namespace. Remember that namespaces are not a physical grouping of classes, such as a project or a class library in Visual FoxPro. They are simply a naming convention that logically groups classes together. For example, all of the source code associated with this book belongs to the namespace HW.NetBook.Samples. For more information on namespaces, see Chapter 1, “Introduction to .NET”.

To assign a class to a namespace, all you have to do is place the class within the curly braces of a namespace declaration:

namespace HW.NetBook.Samples

{

  public class MyDemoClass

  {

  }

 

  public class MyOtherDemoClass

  {

  }

}

Since a namespace is not a physical designation, you can place code in different physical source files or assemblies and assign them to the same namespace. This is different from Visual FoxPro, where a class is designated by its physical location—either a class library or a PRG file.

In Visual FoxPro, before you can instantiate a class, you have to SET CLASSLIB or SET PROCEDURE to the class library or PRG file that contains your class definitions. Similarly, in C#, you must specify that you are “using” the namespace to which a class belongs. The following code specifies that it is using two different namespaces, System and System.Windows.Forms:


using System;

using System.Windows.Forms;

 

namespace HW.NetBook.Samples

{

}

When you declare that you are using the System namespace, it does not give you automatic access to the namespaces that are “below” the System namespace such as System.Windows.Forms. You must explicitly declare each namespace you are using. The using declaration tells C# where to find classes that are referenced within the code below it.

Defining a simple class

As I promised, I won’t go into too much object-orientation in this chapter, but in order to understand the rest of the sample code in this chapter, you need to at least understand how to define a simple class and a simple method.

Here is the simplest class definition in C# (not much fat here):

class MyFirstCSharpClass

{

}

Below is the simple class with a single, simple method. Notice I’ve put it in the context of a namespace (HW.NetBook.Samples) and have referenced the System.Windows.Forms namespace. This is necessary because the MessageBox class belongs to this namespace.

using System.Windows.Forms;

 

namespace HW.NetBook.Samples

{

  public class MyFirstCSharpClass

  {

       public void SayHello()

       {

          MessageBox.Show("Hello .NET World!", "C#");

       }

  }

}

The default base class

In Visual FoxPro, the term “parent class” is used to specify the class from which a class is derived. In the .NET world, the term “base class” is used instead. In VFP, the term ‘base class’ refers to the top-level Visual FoxPro classes (such as textbox, combobox, custom, container, and so on). The reason I bring this up is because in the definition of MyFirstCSharpClass, no parent or base class was specified.

In C#, if you don’t specify otherwise, a class is assumed to be derived from the Object class. The Object class is the topmost class in the .NET hierarchy (the queen mother of all base classes). Every class in the .NET Framework can trace its heritage back to the Object class.


Defining class methods

You’ve already seen an example of a simple class method. Here’s the official syntax for a class method:

[modifiers] returnType MethodName([parmType parmName])

{

  // Method body

}

In C#, both the return value’s type and the parameter’s types must be explicitly specified. This is in keeping with C#’s strongly typed nature. Methods that return nothing have a return type of void.

Declaring variables

Variables declared within methods are local variables (there is no other kind). This means they are only visible within the method they are declared. Based on this, you don’t have to specify a scope (local, private, public) when declaring a variable.

To declare a variable, specify its type, followed by its name:

int Count;    // Declare an integer variable named "Count"

Count = 5;    // Assign Count the value of 5

You can also declare a variable and assign it a value in a single command:

// Declare an int variable named Height and assign it a value of 6

int Height = 6;     

If you want to declare multiple variables of the same type in a single statement, simply separate them with commas:

int Top = 5, Left = 39;

Fields

As mentioned in the previous section, variables declared at the method level are known as local variables. However, as shown in the following code, variables declared at the class level are known as fields:


public class FieldsAndLocalVars

{

  string Test1 = "Test1";   // Declares a field

 

  public void ShowFieldsAndVars()

  {

     string Test2 = "Test2";  // Declares a local var

     MessageBox.Show("Displaying Field " + Test1);            // Display the field

     MessageBox.Show("Displaying Local Variable " + Test2);   // Display the field

  }

}

You might think at first that fields are equivalent to Visual FoxPro’s properties, but they’re not. In addition to fields, C# classes can also have properties which are discussed in Chapter 5, “Object-Orientation in C# and Visual Basic .NET”. For now, just think of fields as variables defined at the class level that accessible to all methods in the class.

One other point to note is you can reference fields the same way you reference local variables. You don’t need a this qualifier in C# as you do in Visual FoxPro. In the previous example, you could have referenced the Test1 field as this.Test1, but it’s not required.

Text Box: ¥

After teaching a good number of C# classes, I highly recommending using “this” to preface members. Since C# is case-sensitive, using “this” allows IntelliSense to kick in and allow you to select members from a list rather than having to type them manually, with the potential of typing them incorrectly.

If you declare a local variable with the same name as a field in the containing class, the local variable, while in scope, hides the field. In this situation, if you need to refer to the field, you must use a this prefix before the field name.

Field modifiers

A field modifier is a keyword used to amend a field declaration. For example, there are five accessibility modifiers you can apply to fields (Table 1) that specify the visibility of a field:

Table 1. Field modifiers allow you to specify the visibility of your fields

Modifier

Description

public

Access is not restricted

internal

Access is limited to the current project

protected

Access is limited to the containing class or subclasses

protected internal

Access is limited to the current project or to subclasses

private

Access is limited to the containing class

 

It’s considered good object-oriented form to declare fields as private. To promote this practice, if you don’t specify a modifier, C# considers a field private by default. If you want to allow other classes to access this field, you can create a protected or public property for this purpose (see the “Properties” section in Chapter 5, “Object-Orientation in C# and Visual Basic .NET” for details).

Value and reference types

.NET makes a strong distinction between value types and reference types. Value type variables store their data directly and reference type variables store the address where their associated data can be found. Reference variables are actually pointers dressed up in an object—but you can’t access the pointer directly. To understand this concept more clearly, you need to learn how C# allocates memory behind the scenes.

Understanding the stack and the heap

There are two places where the .NET runtime allocates memory for your data types—the stack and the heap. The stack is an area of memory used to store value types that are a fixed length. This includes basic data types such as integers, boolean, long, float, double, and character. Figure 1 provides a conceptual view of how this works. When you declare the integer variable “x”, the runtime allocates space for the variable on the stack. When you assign a value to the variable, the .NET runtime stores the value into the space it already allocated on the stack.

Figure 1. When you declare a value-type variable, the .NET runtime allocates space for the variable on the stack. When you assign a value to the variable, the value is stored in the space already allocated on the stack.

What happens when you copy one value variable to another value variable as shown here?


int x;

int x = 5;

int y = x;

This creates two copies of the same data on the stack. When you declare variable x it allocates space for the variable on the stack. When you assign variable x a value of 5, it stores a 5 in the allocated space for variable x on the stack. When you assign x to y, it allocates space for variable y on the stack and copies the value 5 from variable x into the space allocated for variable y. This gives you two copies of the same data on the stack.

The heap is an area of memory used to store reference type data such as classes, arrays, and strings. When you declare a reference type variable, the runtime allocates space for the reference variable on the stack—the same as it does for a value type variable. However, when you instantiate the reference type object (see Figure 2), rather than storing the object’s data on the stack, it is stored on the heap, and a pointer to the address of the object’s data is stored in the reference variable located on the stack.

Figure 2. When you declare a reference variable, the .NET runtime allocates space for the variable itself on the stack. However, when you instantiate the object, it stores the object’s data on the heap and stores a pointer to the heap data in the reference variable on the stack.

What happens when you copy one reference variable to another reference variable?

Invoice Inv;

Inv = new Invoice();

Invoice Inv2 = Inv;

This creates two references to the same data located on the heap. When you declare the variable Inv, the runtime allocates space for the reference variable on the stack. When you instantiate the Invoice object, it stores the object’s data on the heap and stores a pointer to the Invoice object data in the Inv variable. When you copy the Inv variable to the Inv2 variable, it first allocates space for the Inv2 variable on the stack. Afterwards, it adds a pointer to the same Invoice data pointed to by the Inv variable.

Value and reference type performance

Primitive base types such as integers, bool, decimal, and char are value types and provide better performance because you don’t have the overhead of creating an object on the stack every time you use one of these types.

The string type

The string class is a reference type; although from all outward appearances, the commands you use to manipulate strings make them seem like value types.

You can assign a value to a string variable using a string literal enclosed in double quotes:

string Caption = ".NET for VFP Developers";

Unlike Visual FoxPro, you can’t use single quotes or square brackets to delimit strings. You concatenate strings in C# the same way as in VFP—by using a plus (+) sign:

string Title = "Sir"

string Name = Title + "Laurence Olivier";

The next logical question is “How do I create a string literal that contains double quotes such as Louis “Satchmo” Armstrong?” This requires the use of character escapes.

Table 2 shows a list of special characters called character escapes that you can add to a string. For example to add a line break to a string, you use the \n character escape. The following code adds a new line between the city/state and the zip code:

string CityStateZip = "Charlottesville, VA \n22903";

To answer the original question, you can display double quotes contained within a string by using the double quote escaped character \":

string ManlyMen = "Louis \"Satchmo\" Armstrong";

Table 2. This table shows a list of common character escapes, or special characters, you can add to your strings.

Escaped character

Description

\a

Alarm (bell)

\t

Tab

\r

Carriage return

\n

New line

\”

Double quote

 

You tell C# to ignore escaped characters in a string by prefixing the string with an @ sign:

string Folder = @"c:\temp”;

Assigning one string to another

The .NET runtime treats strings differently than other reference types. At first, when you assign the value of one string to another string:

string State1 = "California";

string State2 = State1;

it acts as expected. This code creates two variables on the stack, State1 and State2, and it points both variables to the same “California” string located on the heap.

However, if you change the first string:

string State1 = "Sunny California";

the .NET runtime creates a new string object on the heap and stores a reference to the new data in the State1 variable located on the stack while State2 still points to the old data on the heap. Armed with this information, you can make better choices about how you manipulate strings so as not to create excess string data on the heap. For example, if you need to concatenate a number of strings together in a loop, it’s best to use the .NET StringBuilder class instead of using strings. The StringBuilder class has an Append method to concatenate strings together:

StringBuilder StrBuilder = new StringBuilder;

StrBuilder.Append("Texas, ");

StrBuilder.Append("New Mexico, ");

StrBuilder.Append("Colorado");

MessageBox.Show(StrBuilder.ToString());

Enumerations

C# lets you define your own complex value types. One of these value types is enumerations (the other is structures, covered in Chapter 5, “Object Orientation in C# and VB .NET”).

Enumerations are a convenience feature of C# that allow you to group related integer-based constants together. You can think of them as an array of constants. There is no Visual FoxPro equivalent to enumerations, but it’s easy to see how they can be very useful.

The following code lists a group of constant definitions found in the FoxPro.h include file (located in the Visual FoxPro home directory). These constants define all the possible values for a VFP cursor’s “Buffering” setting:

#DEFINE DB_BUFOFF               1

#DEFINE DB_BUFLOCKRECORD        2

#DEFINE DB_BUFOPTRECORD         3       

#DEFINE DB_BUFLOCKTABLE         4

#DEFINE DB_BUFOPTTABLE          5

When you use one of these constants in a command, such as CursorSetProp(), you often have to open up the FoxPro.h file to see what the possible values are. Here’s where enumerations can help.

In C#, you can define an enumeration that groups together all the possible values for the Buffering setting:

public enum Buffering

{

  BufOff = 1,

  BufLockRecord = 2,

  BufOptRecord = 3,

  BufLockTable = 4,

  BufOptTable = 5

}

You access an enumeration by referring to its name and specifying the desired member. The compiler converts it to its associated integer value:

Buffering.BufOptRecord

As shown in Figure 3, enumerations hook into VS .NET IntelliSense. When you type the name of the enumeration and a period, IntelliSense shows you a list of all possible values in the enumeration.

Figure 3. Enumerations hook into VS .NET IntelliSense, making it easy to select the proper value from a qualified list.

Even though enumerations are integer-based, the compiler complains if you try to store an enumeration to an integer variable as shown here:

int BufferMode = Buffering.BufOptRecord;

The compiler complains that it can’t implicitly convert “Buffering” to an integer. Although you can use a cast to explicitly convert the enumeration value to an integer, (see the “Casting” section later in this chapter) you can also get around this by specifying the variable as the type “Buffering”:

Buffering BufferMode = Buffering.BufOptRecord;

Although enumerations are integer types by default, you can specify them as byte, sbyte, short, ushort, int, uint, long, or ulong instead. For example:

public enum Buffering : long

In this context, : long specifies that the Buffering class is derived from the type long. For details on subclassing and inheritance, see Chapter 5, “Object Orientation in C# and Visual Basic.NET”.

Arrays

There are many similarities between arrays in Visual FoxPro and in C#. One of the big differences is arrays in C# are treated as objects with methods and properties. Another difference is Visual FoxPro allows you to mix different types of data in an array (integers, strings, dates, etc), but in C# all array elements must be of the same type.

Text Box: ¥

You can get around the limitation of array elements having to be of the same type by declaring the array contains items of the type “Object”, which is the base type of all classes in the .NET Framework.

A third difference is that C# arrays are zero-based, meaning the first array element is zero (0). In Visual FoxPro, the first array element is one (1).

Declaring arrays

In C#, arrays are declared the same way as other variables, except you add square brackets [] between the variable type and the variable name. For example, the following statement declares an integer array named “KidsAges”:

int [] KidsAges;

You can also declare an array and size it in one step:

string [] KidsNames = new string[3];

Text Box: ¥Once you declare the length of an array, you can’t resize it. If you want an array whose size is changable at run time, use the ArrayList object instead, found in the System.Collections namespace. See the .NET Help file for details.

You determine the length of any array dimension by calling the array’s GetLength method and passing the desired array dimension:

MessageBox.Show("Number of kids = " +    KidsNames.GetLength(0));

Storing values to arrays

You set initial values of array elements when you declare the array:

string [] KidsMiddleNames = {"Christopher","Mark","James"};

Or you can store values in the array elements after declaring the array:

string [] KidsNames = new string[3];

KidsNames[0] = "Jordan";

KidsNames[1] = "Timothy";

KidsNames[2] = "Alexander";

Sorting arrays

To easily sort the elements of an array, you call the Array class’s Sort method:

Array.Sort(KidsNames);

You reverse the sort order by calling the Array class’s Reverse method:

Array.Reverse(KidsNames);

Multidimensional arrays

All of the arrays you’ve seen so far have only one dimension. Visual FoxPro allows you to create both one and two-dimensional arrays. C# goes a step further and allows you to create multidimensional arrays—arrays with three or more dimensions! However, unless you have special requirements, you probably won’t need more than a two-dimensional array.

Multidimensional arrays are either rectangular (every row has the same number of columns) or jagged (each row can have a different number of columns). Visual FoxPro has rectangular arrays, but does not have jagged arrays.

Defining multidimensional rectangular arrays

You declare an array as multidimensional and rectangular by adding one or more commas within the square brackets of the array declaration. Each comma you add specifies an additional array dimension. Here is the definition of a two-dimensional, rectangular array with two columns and three rows:

string[,] ArrayKids = { {"Jordan", "McNeish"},

                     {"Timothy", "McNeish"},

                     {"Alexander", "McNeish"} };

Defining jagged arrays

A jagged array is an “array of arrays”. They are multidimensional, but, unlike rectangular arrays, each row can have a different number of columns. You define a jagged array by adding an extra set of square brackets for each dimension. In the following example, the jagged array has three elements in its first dimension. Its second dimension has a different size for each row. The first row has three columns, the second row has two columns, and the third row has one column:

string[][] Musicians = new string [3][];

Musicians[0] = new string[] {"Stevie", "Ray", "Vaughn"};

Musicians[1] = new string[] {"Jimi", "Hendrix"};

Musicians[2] = new string[] {"Bono"};

Type conversions

When working with strongly typed languages, converting between different data types is an important consideration. C# provides both implicit and explicit type conversion.

Text Box: ¥

For a list of C# data types, and a comparison between C#, VB. NET and VFP data types, check out “Appendix C – Data Type Comparison”.

Implicit type conversions

C# implicitly converts some values from one type to another. In the following example, C# automatically converts a short value to an integer and then to a double:

short x = 10;

int y = x;

double z = y;

Table 3 lists the types that can be implicitly (automatically) converted in C#.


Table 3. Types that can be implicitly converted in C#

From Type

To Type

byte

ushort, short, uint, int, ulong, long, float, double, decimal

sbyte

short, int, long, float, double, decimal

short

int, long, float, double, decimal

ushort

uint, int, ulong, long, float, double, decimal

char

ushort, uint, int, ulong, long, float, double, decimal

int

long, float, double, decimal

uint

long, ulong, float, double, decimal

long

float, double, decimal

ulong

float, double, decimal

float

double

Explicit type conversion using casts

If the compiler refuses to implicitly convert a value from one type to another, you use a cast instead. As shown in the following example, you specify that you want to cast a value from one type to another by prefixing the value to be converted with the destination type in parentheses. This code converts an integer to a byte (which the compiler will not do implicitly):

int x = 20;

byte y = (byte)x;

Converting to string

If you need to convert a value to a string, the Object class has a ToString method (inherited by all classes in the .NET Framework) that returns the string representation of the object. So, to get a string representation of an integer, you do the following:

int i = 10;

string s = i.ToString();

Using Parse to convert strings

If you need to convert strings to other value types, you use the Parse method of the desired destination type. For example, if you want to convert a string to an integer, you use the Parse method of the int type:

string StringAmt = "100";

int IntAmt = int.Parse(StringAmt);

If you want to convert string value to long, you use the Parse method of the long type:

string StringAmt = "100";

long IntAmt = long.Parse(StringAmt);

You can also convert strings to boolean values using Parse. However, the string value must be either “True” or “False” (case insensitive) or you will get a runtime error stating “string was not recognized as a valid boolean”.

Boxing and unboxing values

Boxing describes the process of converting a value type to a reference type. Unboxing describes converting the value of a reference type back to a value type.

Boxing occurs implicitly when you store an integer type into an object:

int i = 10;

object o = i;

Figure 4 demonstrates what happens when this code is executed. In the first line, space for int “i” is allocated on the stack and a value of 10 is assigned to the variable. In the second line, a reference variable for object “o” is allocated on the stack, and the value of 10 is copied from integer “i” to the space allocated on the heap for object “o”’s data.

Figure 4. Value types can be implicitly boxed when you store the value of an integer (or other value type) into an object.

Unboxing only occurs explicitly. You can only unbox a variable that has been previously boxed. For example:

// Boxing

int MyInteger = 10;

object MyObject = MyInteger;

 

// Unboxing

int MyUnboxedInt = (int)MyObject;

The is operator

You determine if an object is a specific type by using the is operator. This is useful in situations where a method receives a parameter of a generic object type and wants to determine its specific type. For example, the following method receives a parameter of type object. It then checks to see if it is of type MyClass1:

public void CheckClass(object testObject)

{

  if (testObject is MyClass1)

  {

       MessageBox.Show("TestObject is MyClass1",

          "is operator");

  }

  else

  {

       MessageBox.Show("TestObject is not MyClass1",

          "is operator");

  }

}

if statement

C#’s if statement is similar to Visual FoxPro’s IF…ENDIF statement. If the expression in the parentheses following the if evaluates to true, the code within curly braces is executed. Optionally, you can add an else statement to the if that gets executed if the condition expression evaluates to false. C# also allows you to nest if statements.

You don’t need to add the curly braces if there is only one statement following the if or else, however it’s considered good form to do so.

bool HavingFun = true;

 

if (HavingFun)

{

  MessageBox.Show("We are having fun!","if demo");

}

else

{

  MessageBox.Show("We are NOT having fun","if demo");

}

switch statement

The C# switch statement is equivalent to Visual FoxPro’s DO CASE statement. Based on the value of the expression, one of the case statements is selected for execution. You can specify a default case, which is the same as VFP’s OTHERWISE clause. If the switch expression does not match any of the cases, control is passed to the default case. If no default case is present, control passes to the first command after the end of the switch.

testValue int = 2;

switch (testValue)

{

  case 1:

       MessageBox.Show("testValue = 1","switch");

       break;

  case 2:

       MessageBox.Show("testValue = 2","switch");

       break;

  case 3:

  case 4:

       MessageBox.Show("testValue = 3 or 4","switch");

       break;

  default:

       MessageBox.Show("testvalue is not 1,2,3 or 4",

          "switch");

       break;

}

As shown in the previous example, you cannot fall through one case to another unless a case is empty. This means you must specify one of the following jump statements:

·         break – indicates the end of a case and control is passed to the statement following the switch.

·         goto – specifies control be passed to a specific case (e.g. “goto case 2”) or to the default case (e.g. “goto default”).

·         return – indicates the end of a case and terminates the execution of the current method.

for loop

C# for loops are equivalent to Visual FoxPro’s FOR…ENDFOR command. It iterates through a loop while a particular condition is true. 

In the parentheses that follow the for statement, there are three different items. The first is the initializer (e.g. i=0;). You place any variables to be initialized in this part of the statement, although it’s most common to specify a single variable to be initialized. The second part of the parenthetical statement is the expression to be evaluated (e.g. i < 4). This expression is evaluated before the loop is executed. The third part of the parenthetical statement is the iterators (e.g. i++).  This is the expression used to increment or decrement the loop counter.

In C#, the ++ operator increments a variable and the -- operator decrements a variable. For more information on C# operators, see “Appendix A – Language Comparison Tables”

int i;

 

string Message = "";

 

for (i=0; i < 4; i++)

{

  Message += "Message " + i + "\n";

}

MessageBox.Show(Message, "For Loop");

while loop

The C# while loop is equivalent to Visual FoxPro’s DO…WHILE loop. It is a pre-test loop, which means if the condition evaluates to false, the loop is not executed at all. While loops are often used to repeat a block of code for an undetermined number of times.

bool Condition = true;

while (Condition)

{

  // This loop only executes once

  int i = 1;

  MessageBox.Show("While loop count " + i, "while loop");

  i++;

  Condition = false;

}

do loop

The C# do loop is similar to C#’s own while loop, except the condition is checked at the end of the iteration rather than at the beginning. This means the loop is always executed at least once. This is different than Visual FoxPro’s DO…WHILE loop, which evaluates at the top of the loop.

bool Condition = false;

do

{

  // This loop only executes once because the condition

  // is tested at the end of the loop

  int i = 1;

  MessageBox.Show("Do While loop count " + i,   "do...while loop");

  i++;

} while(Condition);

You exit a do loop by using the break, goto, or return statements.

foreach loop

The C# foreach loop executes a group of statements for each element in an array or item in a collection. It is equivalent to Visual FoxPro’s FOR EACH command.

string VersionList = "";

string[] VSNetVersions = {"Standard","Professional",

                      "Enterprise Developer", "Enterprise Architect"};

 

foreach (string Version in VSNetVersions)

{

  VersionList += "VS .NET " + Version + "\n";

}

MessageBox.Show(VersionList,"ForEach loop");


XML Documentation

C# has a very cool feature that allows you to document your code using XML.

If you type three forward slashes in a row in the line immediately preceding a user-defined type (such as a class, structure, enumeration, delegate, or interface) or preceding a member (such as a field, property or method), an XML documentation template is automatically inserted where you can add your comments. For example, suppose you have the following C# class:

public class CommentDemo2

{

  public string GetString(int value)

  {

       return value.ToString();

  }

}

If you enter three forward slashes on the line before the start of the class definition and preceding the method definition, the following XML documentation template is added to
your code:

/// <summary>

///

/// </summary>

public class CommentDemo2

{

  public string GetString(int value)

  {

  /// <summary>

  ///

  /// </summary>

  /// <param name="value"></param>

  /// <returns></returns>

  return value.ToString();

  }

}

You enter a description of your class between the <summary> tag preceding the class definition and a description of your method in the <summary> tag preceding the method definition. Notice that C# is smart enough to create XML tags for parameters and return values. You can also place descriptions in the <param> and <returns> tags. For example:

/// <summary>

/// Comment Demo class

/// </summary>

public class CommentDemo2

{

  /// <summary>

  /// Converts an integer value to a string

  /// </summary>

  /// <param name="value">Integer value to be converted</param>

  /// <returns>Converted string value</returns>

  public string GetString(int value)


  {

       return value.ToString();

  }

}

So what does placing your comments in XML format do for you? The C# development environment in Visual Studio .NET can read your XML documentation and automatically convert it to “comment web pages”!

To build these web pages, open your C# solution in VS .NET and select Tools | Build Comment Web Pages from the menu. This displays the Build Comment Web Pages dialog (Figure 5). This dialog allows you to specify whether you want to create comment web pages for your entire solution or for selected projects. You can even add it to your “Favorites” in your Web browser.

Figure 5. You can build comment web pages for all projects in your solution or for a single project.

When you click OK, web comment pages are generated for you. When it’s done, you’ll see a Code Comment Web Report displayed in a tab of Visual Studio .NET (Figure 6).

Figure 6. After building comment web pages, a Code Comment Web Report page is displayed that lists all of the projects for which you have generated web pages.

If you click the project hyperlink, a web page displays a list of all namespaces in your project. If you expand the namespace node, a list of classes belonging to the namespace appears. Selecting a particular class displays information about the class and all of its members, derived from the XML documentation you have inserted in your C# source code (Figure 7).

Figure 7. The comment web pages display information about classes derived from the XML documentation you have inserted in your C# source code.

If you click on a member name such as a method, it displays details regarding the method’s parameters and return values (Figure 8).

Figure 8. Comment web pages also display details regarding a method’s parameters and return value.

Comment web pages are a great addition to the Visual Studio .NET environment because, not only are they cool, they encourage developers to document their code!

Unsafe Code

C# has an advanced feature that is not available in Visual Basic .NET—the ability to create unsafe code.

Unsafe code is often misconstrued as being unmanaged code. As mentioned in Chapter 1, “Introduction to .NET”, unmanaged code is code that executes outside of the CLR and not able to take direct advantage of .NET services such as garbage collection. An example of running unmanaged code is calling a method on an object in a Visual FoxPro COM server.

In contrast, unsafe code is still managed code, but it allows you to access memory directly by means of pointers. The term unsafe doesn’t necessarily mean your code is a danger to itself and to you. It simply means the runtime cannot verify that it is safe.

So, why would you want to write unsafe code? There are a few reasons. The most common reason is to improve performance. When you are able to access memory addresses using pointers rather than reference variables, you are going through one less layer, which provides a performance advantage. If you have time-critical portions of your application where you want to wring out the highest possible speed, you may consider creating unsafe code.

Another reason you may want to create unsafe code is to access a legacy DLL, such as a Windows API call or a legacy COM DLL that calls for a pointer to be passed as a parameter.

For more information on creating unsafe code in C#, check out the MSDN article Unsafe at the Limit by Eric Gunnerson of Microsoft:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp10182001.asp

C# as an international, industry standard language

Microsoft has done something of great interest with the C# language. In October of 2000, Microsoft submitted C# for review to the European Computer Manufacturers Association (ECMA), an international standards organization, hoping to make it an industry standard language. On December 13, 2001, the ECMA ratified C# and the Common Language Infrastructure (CLI) as international standards. (The CLI is similar to Java’s virtual machine—it includes libraries and components that allow C# to run on non-Windows operating systems).

What does this mean for Microsoft and the C# developer? First of all, it means that C# and the CLI are official standards administered by ECMA, although Microsoft retains the right to decide who gets to license the technology. In addition, although there are a number of theories stating that Microsoft has done this “just for show” (Sun Microsystems submitted their Java programming language to the ECMA, then pulled it back again), ultimately, it means that you may be able to write programs in C# that run on non-Windows operating systems such as Linux, Unix and the Palm—something that has been a key feature of Java since its inception. This is similar to the capabilities you have today for creating a C++ application for multiple platforms.

Conclusion

Hopefully this chapter proved useful in giving you a good overview of C#’s syntax, data types, conversions, conditional, and flow of control statements. Although the use of curly braces and semicolons may throw you at first, many developers are surprised at how easy it is to learn C# and how straightforward it is to use.

Although I’ve covered the basics of C# syntax in this chapter, you can learn more from books such as Professional C# - Second Edition, by Wrox Publishing.

 


.NET for Visual FoxPro Developers