C and Delphi for .NET Languages


C# and Delphi for .NET Languages

The C# language is the "default" language of the .NET framework (the FCL is written in C#). It is a .NET-only, case-sensitive, and fully object-oriented language that in many ways resembles C++, Delphi, and Java programming languages. If you've read the C++ portions of this book or if you already know C++ or Java, you'll have no trouble learning the C# language since it is syntactically closest to these two languages.

To learn more about the C# language, let's create a C# console application project. When you select the Console Application item to create a C# console application project, the IDE will first ask you to name the project (see Figure 29-1) and then will automatically save all project files to disk. The code generated for the C# console application is displayed in Listing 29-2.

image from book
Figure 29-1: Creating a C# console application

Listing 29-2: The source code of a C# console application

image from book
using System; namespace Project1 {    /// <summary>    /// Summary description for Class.    /// </summary>    class Class    {       /// <summary>       /// The main entry point for the application.       /// </summary>        [STAThread]       static void Main(string[] args)       {         //         // TODO: Add code to start application here         //       }    } }
image from book

Besides the project code, the list of references in a project is extremely important. In order to use a specific class, we must first reference the assembly in which the class is implemented. For instance, to use the Console class (declared in the System namespace), we have to reference the System assembly. If you look at the References node in the Project Manager (see Figure 29-2), you'll notice that the console application already references not only the most important System but also the System.Data and System.XML assemblies.

image from book
Figure 29-2: Project references

To reference other assemblies, right-click the References node in the Project Manager and select Add Reference to display the Add Reference dialog box (see Figure 29-3). The Add Reference dialog box allows you to reference standard and custom .NET assemblies.

image from book
Figure 29-3: The Add Reference dialog box

The purpose of the first directive in the code, the using directive, is to allow us to use the types in a specific namespace without having to specify the namespace. In this case, we can use the Console class and its methods without having to specify the System namespace before the Console class:

// without using System; System.Console.WriteLine("Hello from C#!"); // with using System; Console.WriteLine("Hello from C#!");

The using directive can also be used to create class or namespace aliases, which reduce typing even more. For instance, here's how you can create an alias for the System.Console class and use the class through the alias:

using System; using Con = System.Console; namespace Project1 {    class Class    {        [STAThread]       static void Main(string[] args)       {         Con.WriteLine("Hello from C#");         Con.WriteLine("Press any key to continue...");         Con.ReadLine();       }    } } 

Each project also gets its own namespace. The syntax of a C# namespace is:

namespace namespaceName { }

or

namespace name1.name2.nameN { }

If you don't like the generated namespace name, you can either change it manually or use Rename Namespace refactoring. When naming namespaces, you should use the CompanyName.TechnologyName format:

namespace Wordware.InsideDelphi  { }

Since C# is completely object-oriented, the source code of the console applications also contains a class and a static method called Main. The Main method is the most important method in a C# project, as it is the entry point of the program.

The summary comments in the code are special comments that can be used by the compiler to generate XML documentation for the project. To have the compiler generate the documentation, check Generate XML documentation in the Code generation group box on the Project Options dialog box, as shown in Figure 29-4. Generated XML documentation for a simple console application is displayed in Figure 29-5.

image from book
Figure 29-4: C# Project Options dialog box

image from book
Figure 29-5: XML documentation generated by the compiler

C# Essentials (Variables, Constants, and Casting)

Variables in C# are declared as they are in C++: data type followed by an identifier and a semicolon. Variables declared in C++ can be initialized, but they don't have to be; if they aren't initialized, they contain random values. In C#, you can declare a variable, but you cannot use it until you initialize it:

static void Main(string[] args) {    int x;    // Error: Use of unassigned local variable 'x'    Console.WriteLine(x); }

You can initialize a variable in C# at the same time you declare it or, of course, by using the assignment operator after the variable is declared:

int x = 1; int y; y = 2;

Constants in C# are defined with the reserved word const, followed by the data type, identifier, assignment operator, and value:

const int ConstValue = 2005;

Explicit typecasts in C# are written as they are in C++, by writing the target data type in parentheses before the value. All objects in the .NET framework can be converted to string by calling the ToString() method.

Here's an example of an explicit typecast and the usage of the ToString() method:

static void Main(string[] args) {    int x = (int)1.2;    /* since 2005 is an int object, we can       call its ToString() method */    string s = 2005.ToString();    string s2 = x.ToString();    Console.WriteLine("x = {0}", x);    Console.WriteLine("s = {0} and s2 = {1}", s, s2);    Console.ReadLine(); } 

The above example also illustrates how to use the WriteLine method to output several values to the console. The {0} and {1} parts of the string are placeholders for values, just like %d and %s values are placeholders for values in the Delphi Format function.

Value and Reference Types

The .NET framework supports two categories of types: value types and reference types. Value types are derived from the System.ValueType class; they are allocated on the stack and directly contain their data. Value types in the .NET framework are numeric data types; Boolean, Char, and Date types; enumerations; and structures (records).

Reference types are derived directly from the System.Object class, contain a pointer to the data allocated on the heap, and are managed by the framework's garbage collector. Reference types are string, arrays, classes, and delegates (covered in the next chapter).

The .NET framework also allows us to convert value to reference and reference to value types. The process of converting a value type to a reference type is known as boxing. The process of converting a reference type to a value type is known as unboxing.

Boxing

When boxing occurs, a new object is allocated on the managed heap and the variable's value is copied into the managed object stored on the heap.

The following code illustrates when boxing occurs. In this case, boxing is done implicitly by the C# compiler:

static void Main(string[] args) {    char c = 'A';    object o = c; /*  implicit boxing */    Console.WriteLine(o);    Console.ReadLine(); }

Here's the same code in Delphi for .NET, which also results in implicit boxing of the c variable:

program Boxing; {$APPTYPE CONSOLE} var   c: Char = 'A';   o: System.Object; begin   o := c; { implicit boxing in Delphi for .NET }   Console.WriteLine(o);   Console.ReadLine(); end. 

The .NET SDK includes an extremely useful tool that allows us to view the IL code produced by a .NET compiler — the IL Disassembler (ILDASM). ILDASM can show us how things work in the .NET framework, which is great, because the .NET framework, unlike Delphi's RTL and VCL, doesn't ship with its source code.

In this case, we can use ILDASM to confirm that boxing actually occurs in the object o = c line. The ildasm.exe file is located in the Program Files directory under \Microsoft.NET\SDK\v1.1\Bin. When you run it and load the appropriate assembly (in this case the project's exe file), ILDASM shows not only the IL code but also the namespaces, classes, types, and methods to which the code belongs.

Figure 29-6 shows what ILDASM looks like and also shows the disassembled C# version of the above code that contains an implicit box operation.

image from book
Figure 29-6: Using ILDASM to view assembly contents

Unboxing

Unboxing occurs when a reference type is explicitly typecast to a value type. The unboxing operation first checks whether the typecast value is a boxed value and then copies the value from the object instance to a value type variable.

The following C# code shows when boxing and unboxing operations occur and Figure 29-7 shows the results:

image from book
Figure 29-7: Boxing and unboxing

static void Main(string[] args) {    int x = 2005;    object o = x; /* box */    int y = (int)o; /* unbox */    Console.WriteLine(x);    Console.ReadLine(); }

Conditions

The C# language provides us with the if and switch statements for testing conditions. The syntax of both statements and the relational and logical operators that are used with them are the same in C++ and C#.

Although syntactically the same, the switch statement in C# differs from the C++ switch statement. The C# switch statement doesn't allow fall- through (all cases must be followed by the break statement) and it supports string cases.

The following listing shows both statements in action and also shows how to use the System.Convert class to convert types in .NET (in this case, how to convert a string to an integer).

Listing 29-3: if and switch statements

image from book
using System; namespace Wordware.InsideDelphi {    class Conditions    {        [STAThread]       static void Main(string[] args)       {         Console.Write("Enter a number: ");         string userValue = Console.ReadLine();         // convert string to int using the Convert class         int num = Convert.ToInt32(userValue);         if ((num < 1) || (num > 5))            Console.WriteLine("Invalid number");         else {            /* C# switch doesn't allow fall through */            switch(num)            {               case 1: Console.WriteLine("One");                     break;               case 2: Console.WriteLine("Two");                     break;               case 3: Console.WriteLine("Three");                     break;               default: Console.WriteLine("Four or five");                     break;            }         }         Console.ReadLine();       }    } }
image from book

Arrays and Loops

Arrays in C# are reference types, and because of that, they cannot be declared as simply as variables of primitive types like integer.

To declare an array in C#, the following syntax is used:

data_type[] array_name;

To actually use an array, it must be instantiated with the reserved word new:

data_type[] array_name = new data_type[nr_of_elements];

Arrays can also be automatically initialized using the following syntax:

type[] array = new type[nr] {val1, val2, valn};

The following listing illustrates both how to declare and automatically initialize a one-dimensional array in C# and how to loop through the array using all four C# iteration statements: for, while, do, and foreach (C# version of Delphi's for-in loop). The for, while, and do loops work as they do in C++, so only the foreach loop needs to be described.

Like Delphi's for-in loop, the foreach loop is used to loop through arrays and collections. The syntax of the C# foreach loop is:

foreach(data_type identifier in array_or_collection) statement; 

The listing also shows how to determine the length of the array using its Length property. C# arrays inherit the Length property from the System.Array class.

Listing 29-4: Arrays in C#

image from book
using System; namespace Wordware.InsideDelphi {    class Arrays    {        [STAThread]       static void Main(string[] args)       {         int[] arr = new int[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};         int i;         /* for */         for(i = 0; i < arr.Length; i++)            Console.WriteLine(arr[i]);         /* while */         i = 0;         while (i < arr.Length)            Console.WriteLine(arr[i++]);         /* do..while */         i = 0;         do {            Console.WriteLine(arr[i++]);         } while (i < arr.Length);         /* foreach */         foreach(int x in arr) {            Console.WriteLine(x);         }         Console.ReadLine();       }    } }
image from book

When you're building .NET applications, you can either declare an array as you always do, or you can create an instance of the System.Array class. To instantiate the System.Array class, you need to call its CreateInstance method and pass the element type and the number of elements you want the array to have.

When calling the CreateInstance method, you can't directly pass a type like string to the method; you need to use the typeof operator (TypeOf in Delphi for .NET), which returns the required System.Type object that describes a data type.

Listing 29-5 shows how to create an instance of the System.Array class in Delphi for .NET.

Listing 29-5: Instantiating and using the System.Array class

image from book
program Project1; {$APPTYPE CONSOLE} uses   SysUtils; var   arr: System.Array;   s: string;   i: Integer; begin   { array[0..9] of string }   arr := System.Array.CreateInstance(TypeOf(string), 10);   arr[0] := 'Using the ';   arr[1] := 'System.Array ';   { the SetValue method can be used to assign a value to an element }   arr.SetValue('class...', 2);   for s in arr do   begin     { if s <> '' can also be used }     if s <> nil then       Console.Write(s);   end;   { the GetValue method can also be used to read element values }   for i := 0 to Pred(arr.Length) do     Console.Write(arr.GetValue(i));   Console.ReadLine(); end.
image from book

Besides the System.Array class, which can be used to create arrays with a known number of elements, we can also use the ArrayList class, which allows us to dynamically increase or decrease the number of elements in the array. The ArrayList class exists in the System.Collections namespace. Listing 29-6 shows how to use the ArrayList class in Delphi for .NET.

Listing 29-6: Using the ArrayList class

image from book
program Project1; {$APPTYPE CONSOLE} uses System.Collections; var   list: ArrayList = ArrayList.Create;   i: Integer; begin   list.Add('Item 1');   list.Add('Item 2');   list.Add('Item 3');   for i := 0 to Pred(list.Count) do     Console.WriteLine(list[i]);   Console.ReadLine(); end.
image from book

Methods

Since C# is entirely object-oriented, it doesn't allow developers to create global methods, that is, methods that don't belong to a class. Delphi for .NET supports global methods, but only syntactically; the Delphi for .NET compiler compiles units as classes and global routines as their methods.

The syntax of a C# method is:

return_type method_name(parameter_list)  { }

Using a C# Class Library in a Delphi for .NET Application

To see how to create and use methods in C# and to illustrate how easy it is to create cross-language applications in .NET, let's create a simple class library (a .NET assembly with the .dll extension) and then use it in a Delphi for .NET console application.

To create a C# class library, double-click the Class Library item in the C# Project category. The only difference between a C# console application project and the class library project is that the compiler will produce a .dll, not an .exe file.

The HelloClass class in the library also has two methods: a normal and a static method. The standard ConsoleHello() method illustrates how to create a method that can only be called when an instance of a class is created. The static method StaticConsoleHello() illustrates how to create a static method that can be called without having to instantiate the class first. For instance, the WriteLine method that we've used constantly is a static method. If it weren't, we would have to create an instance of the Console class first and then call the WriteLine() method through the instance.

Since C# methods are private by default, both methods need to be marked as public in order to use them outside of the class. Listing 29-7 shows the source code of the entire class library.

Listing 29-7: A very simple C# class library

image from book
using System; namespace TestLib {    public class HelloClass    {       private const string MSG = "Hello from C#.";       private const string STATIC = "Static Hello from C#.";       /* constructor currently does nothing */       public HelloClass()       {       }       /* a simple C# method */       public void ConsoleHello()       {         Console.WriteLine(MSG);       }       /* can be called without creating a HelloClass instance */       public static void StaticConsoleHello()       {         Console.WriteLine(STATIC);       }    } }
image from book

After you've compiled the class library, create a new Delphi for .NET console application project. To use the C# TestLib class library in the console application, right-click the References node in the Project Manager window to reference it. To add a reference to a custom assembly, use the Browse button in the lower part of the Add Reference dialog box, as shown in Figure 29-8.

image from book
Figure 29-8: Adding a custom assembly reference

When you add an assembly reference in a Delphi for .NET application, the IDE adds a {%DelphiDotNetAssemblyCompiler} directive that references the assembly to the source code:

{%DelphiDotNetAssemblyCompiler '..\testlib\bin\debug\TestLib.dll'}

When you add a custom assembly to either a C# or a Delphi for .NET project, the IDE checks the Copy Local option. When Copy Local is checked (see Figure 29-9), the assembly is copied to the directory of the executable file when the executable is compiled. Local copies of custom assemblies greatly reduce development and deployment issues because everything (except the standard assemblies from the .NET runtime) is stored in the application directory.

image from book
Figure 29-9: Custom assemblies are copied to the application directory

If you don't know which namespaces, types, or classes are available in the assembly, you don't have to launch ILDASM; you can double-click the assembly (in this case, image from book TestLib.dll) in the Project Manager to display its contents.

image from book
Figure 29-10: Viewing an assembly in the IDE

Finally, to use the HelloClass class, you only have to add the TestLib namespace to the uses list. Listing 29-8 shows the entire console application that illustrates how to call both normal and static methods.

Listing 29-8: Using HelloClass from the C# TestLib class library

image from book
program UseTestLib; {$APPTYPE CONSOLE} {%DelphiDotNetAssemblyCompiler '..\testlib\bin\debug\TestLib.dll'} uses TestLib; var   HC: HelloClass; begin   { static methods can be called without an instance }   TestLib.HelloClass.StaticConsoleHello;   HC := HelloClass.Create;   HC.ConsoleHello;   { no need to free the HC object; it gets     destroyed by the garbage collector }   Console.ReadLine; end.
image from book

Using a Delphi for .NET Package in a C# Application

Now that you know how to build applications using both C# and Delphi for
.NET languages, let's create a Delphi for .NET package (Delphi .NET DLL) with a unit that contains a single global procedure, and then use it in a C# console application. The rationale for building a package with a global procedure is to see Delphi for .NET compiler magic — to see how the compiler constructs namespaces, classes, and methods from units and procedures.

When you create a new Delphi for .NET package, add to it a new unit named image from book About.pas and then create the following procedure:

unit About; interface procedure ShowAbout; implementation procedure ShowAbout; begin   Console.WriteLine('Built with Delphi for .NET.'); end; end. 

After you create the procedure, compile the package to create the necessary DLL file and then open it with ILDASM (see Figure 29-11). The compiler uses the unit name to both create the namespace and name the class. The namespace created by the compiler has the UnitName.Units format, so in this case, it's About.Units. The class name in this case is About. The ShowAbout procedure from the unit is converted into a public static method of the About class.

image from book
Figure 29-11: Delphi for .NET compiles units as classes.

All these internal changes have no effect on Delphi for .NET code that uses the package and the ShowAbout procedure from the unit. To use the Delphi for .NET package in a Delphi for .NET application, you have to reference the package, and then you can use the unit and the procedure as if they are part of the current project:

program Project1; {$APPTYPE CONSOLE} {%DelphiDotNetAssemblyCompiler '..\delphinet_firstlib\DelphiFirstLib.dll'} uses About; begin   ShowAbout;   ReadLn; end.

Although Delphi allows you to use the unit and the procedure as you do in Delphi for Win32, the compiler has to play around with the emitted CIL code and call the appropriate method of the appropriate class (see Figure 29-12).

image from book
Figure 29-12: CIL code emitted for the ShowAbout procedure call

When you have to use a Delphi for .NET package in another language, like C# or VB.NET, you need to reference the package and import the appropriate namespace manually.

Listings 29-9A and 29-9B show how to use the Delphi for .NET package in a C# and a VB.NET console application (you can find the VB.NET console application project item in the Other Files category on the Tool Palette).

VB.NET applications can be built in Delphi because the vbc.exe (VB.NET compiler) is included in the .NET Framework SDK.

Listing 29-9A: Using a Delphi for .NET package in a C# application

image from book
using System; /* About.Units is the namespace,    final About is the class name */ using DelphiClass = About.Units.About; namespace CSharpUsesDelphi {    class UserClass    {        [STAThread]       static void Main(string[] args)       {         DelphiClass.ShowAbout();         Console.ReadLine();       }    } }
image from book

Listing 29-9B: Using a Delphi for .NET package in a VB.NET application

image from book
Imports System Imports DelphiClass = About.Units.About Module VBNETUser     Sub Main()         DelphiClass.ShowAbout()         Console.ReadLine()     End Sub End Module
image from book

Method Parameters

Methods can accept none, one, or many parameters, and they can return none, one, or many values. As in Delphi, methods in C# can be overloaded, can be static, and can accept a variable number of parameters. By default, methods in C# are private (in C++ they are also private, but in Delphi they are public).

When you want to create a method that accepts no parameters, write an empty pair of parentheses as in Listing 29-10.

Listing 29-10: A method without parameters

image from book
public void NoParams() {    return; /* you can but don't have to write return */ }
image from book

When you want to create a method that accepts a single parameter, declare it inside the parameter list's parentheses as you would a variable but without the semicolon:

Listing 29-11: Method that accepts a single parameter by value

image from book
public void OneStringParam(string UserName) {    System.Console.WriteLine(UserName); }
image from book

When you want to create a method that accepts several parameters, separate the parameters with commas:

Listing 29-12: Accepting a larger number of parameters

image from book
public void SeveralParams(int One, string Two, char Three) { }
image from book

To return a single value from the method, use the function syntax:

public int RetIntegerSum(int One, int Two) {    return One + Two; }

To modify values passed as parameters (if they are not constant values), have the method accept parameters by reference using the reserved word ref. C# ref parameters are equivalent to Delphi's var parameters (see Listing 29-13).

Listing 29-13: Passing parameters by reference

image from book
public void PassByReference(ref string Name, ref bool Changed) {       if(Name == "")    {       Name = "The string can't be empty.";       Changed = true;    } }
image from book

You won't be able to test the PassByReference method in the Main method because non-static methods cannot be called in a static method. To test the PassByReference method, either declare the method as static or create an instance of the class inside the Main method and then call the PassByReference method on that instance. When you have a method that accepts ref parameters, you also have to use the reserved word ref when you call the method as shown in Listing 29-14.

Listing 29-14: Calling a method that accepts parameters by reference

image from book
using System; namespace Parameters {    class ParamsClass    {       public void PassByReference(ref string Name, ref bool Changed)       {         if(Name == "")         {            Name = "The string can't be empty.";            Changed = true;         }       }        [STAThread]       static void Main(string[] args)       {         ParamsClass pc = new ParamsClass();         bool b = false;         string s = "";         pc.PassByReference(ref s, ref b);         Console.WriteLine(s); /* writes "The string ... */         Console.ReadLine();       }    } }
image from book

C# also supports out parameters, which, like Delphi out parameters, allow us to pass uninitialized parameters and modify the passed value inside the method. When calling a method that accepts an out parameter, the reserved word out must also be used.

Listing 29-15: C# out parameters

image from book
public void ModifyValues(out string Text) {    Text = "Initializing..."; }   [STAThread] static void Main(string[] args) {    ParamsClass pc = new ParamsClass();    bool b = false;    string s = "";    pc.PassByReference(ref s, ref b);    Console.WriteLine(s); /* writes "The string ... */    /* uninitialized string variable */    string noInit;    pc.ModifyValues(out noInit); /* no error! */    Console.WriteLine(noInit);    Console.ReadLine(); }
image from book

If you've read the Delphi for Win32 portions of this book, you'll know that objects in Delphi don't have to be passed as var parameters in order to change their fields. If you do pass objects as var parameters, nothing bad will happen, but you'll unnecessarily be passing a pointer to pointer. The same is true for C# ref parameters. If you need to pass an object to a method, pass it by value, as shown in Listing 29-16.

Listing 29-16: Passing objects to methods

image from book
using System; namespace Parameters {    class FieldClass {       public string TestField = "";    }    class ParamsClass    {       public void AcceptAnObject(FieldClass fc)       {         fc.TestField = "Changed in the AcceptAnObject method.";       }        [STAThread]       static void Main(string[] args)       {         ParamsClass pc = new ParamsClass();         /* object parameters don't have to be ref */         FieldClass fld = new FieldClass();         Console.WriteLine(fld.TestField);   // empty string         pc.AcceptAnObject(fld);                   // change fld.TestField         Console.WriteLine(fld.TestField);         Console.ReadLine();       }    } }
image from book

There is no special syntax involved in method overloading in C#. To create an overloaded version of a method, you have to create a method with the same name but with a different parameter list.

Listing 29-16: Overloaded methods

image from book
/* overloaded method */ public void AcceptAnObject(FieldClass fc) {    fc.TestField = "Changed in the AcceptAnObject method."; } /* overloaded method */ public void AcceptAnObject(FieldClass fc, string s) {    fc.TestField = s; }
image from book

To have a C# method accept a variable number of parameters, have it accept an array and mark the array parameter with the reserved word params.

Listing 29-17: Passing a variable number of parameters to a method

image from book
public void VariableParamNum(params string[] names) {    foreach(string name in names)       Console.WriteLine(name); } [STAThread] static void Main(string[] args) {    /* passing a variable number of parameters to a method */    pc.VariableParamNum("Anders");    pc.VariableParamNum("Danny", "Allen");    pc.VariableParamNum("Michael", "Lino", "Steve", "David");    Console.ReadLine(); }
image from book

Enumerations

In C#, enumerations are declared with the reserved word enum, which has the following syntax (you can end the declaration with a semicolon, but it isn't required like it is in C++):

enum enumeration_name {enumerator_list}

To use an enumerated value in C#, you have to write the fully qualified name of the value, that is, the enumeration name followed by the dot operator and the enumerated value.

Listing 29-18: C# enumerations

image from book
using System; namespace Enumerations {    enum Days {Monday, Tuesday, Wednesday, Thursday, Friday}    enum Weekend {Saturday = 10, Sunday = 20}    class EnumerationClass    {        [STAThread]       static void Main(string[] args)       {         Console.WriteLine(Days.Monday);         // Monday         Console.WriteLine((int)Days.Monday);    // 0         Console.WriteLine(Weekend.Sunday);      // Sunday         Console.WriteLine((int)Weekend.Sunday); // 20         Console.ReadLine();       }    } }
image from book

Exception Handling

C# exception handling is very similar to Delphi exception handling. C# allows you to catch exceptions in a try-catch block and to protect resource allocations with the try-finally block. Unlike Delphi, C# allows you to catch multiple exceptions after a single try block and lets you add a finally block after one or more exception handling blocks.

All exceptions in Delphi are derived from the Exception class (the SysUtils unit). In C#, all exceptions are derived from the System.Exception class. In Delphi for .NET, the Exception type is mapped to the FCL's System.Exception class. As in Delphi, all exceptions are objects, and you can use the properties of the exception object to find out more about the exception.

To catch all exceptions that a piece of code can throw ("raise" in Delphi parlance), write a "plain" try-catch block:

private void CatchAllExceptions() {    try    {       int i = 2, j = 0;       Console.WriteLine("Result = {0}", i / j);    }    catch   /* all exceptions */    {       Console.WriteLine("I caught an error, but I " +         "have no idea what happened.");       Console.ReadLine();    } }

To catch specific exceptions, use the following syntax:

try { } catch (AnException) { } 

Listing 29-19 shows how to catch specific exceptions in C#. The try block is followed by two catch blocks. The first catch block tries to catch the Divide- ByZeroException, which gets thrown when you try to divide a number by zero. The second catch block tries to catch all other exceptions (except the DivideByZeroException) that might be thrown by code from the try block.

Listing 29-19: Catching several exceptions

image from book
private void CatchSpecificException() {    try    {       int i = 2, j = 0;       Console.WriteLine("Result = {0}", i / j);    }    catch (System.DivideByZeroException)    {       Console.WriteLine("Cannot divide by zero!");       Console.ReadLine();    }    catch(System.Exception)    {       Console.WriteLine("Something wrong happened.");       Console.ReadLine();    } }
image from book

To catch an exception and use the exception object, use the following syntax:

catch (AnException ExceptionInstance)

Use the reserved word throw to throw or rethrow an exception. When you want to rethrow an exception, write the throw reserved word, followed by a semicolon. When you want to throw an exception, use the following syntax:

throw new ExceptionName();

Listing 29-20 shows how to use exception object instances and how to throw and rethrow exceptions in C#. The result of the code displayed in Listing 29-20 is displayed in Figure 29-13.

image from book
Figure 29-13: Throwing and rethrowing exceptions in C#

Listing 29-20: Using exception objects, throwing and rethrowing exceptions

image from book
using System; namespace csharp_exceptions {    class Exceptions    {       private void ThrowOneForFun()       {         // pass the text to the exception's Message property         throw new System.Exception("Catch me if you can!");       }       private void Rethrow()       {         Exceptions exceptions = new Exceptions();         try         {            ThrowOneForFun();         }         catch(Exception e)         {            Console.WriteLine("Exception \"{0}\" " +               "caught and rethrown in Rethrow().", e.Message);            throw; /* rethrow the exception */         }       }        [STAThread]       static void Main(string[] args)       {         Exceptions exc = new Exceptions();         try         {            exc.Rethrow();         }         catch(Exception e)         {            Console.WriteLine("Exception with message \"{0}\" " +               "caught after rethrow in Main().", e.Message);            Console.ReadLine();         }         Console.WriteLine("Press any key to continue...");         Console.ReadLine();       }    } }
image from book



Inside Delphi 2006
Inside Delphi 2006 (Wordware Delphi Developers Library)
ISBN: 1598220039
EAN: 2147483647
Year: 2004
Pages: 212
Authors: Ivan Hladni

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net