/******************************************************************/ /* Document : C# elementary code fragments and basic theory */ /* Doc. Versie : 5 */ /* File : C#.txt */ /* Date : 28-02-2005 */ /* Content : just some OO, C++, and C# theory for a */ /* SQL Server, DB2 and Oracle DBA */ /* Compiled by : Albert */ /******************************************************************/ ==================================================================== 1. First Some stuff About Classes and Objects in other Environments: ==================================================================== This section should explain the basic idea about classes en objects, as it is used in "older" environments. 1.1 Objects in a language that's often considered "Traditional" (pl/sql): ========================================================================= Even in what is considered traditional programming enviroments, "object orienting programming" is now possible. Look for example at the following PL/SQL code in Oracle ( > version Oracle 8): In PL/SQL, object-oriented programming is based on object types. An object type encapsulates a data structure along with the functions and procedures needed to manipulate the data. The variables that form the data structure are called attributes. The functions and procedures that characterize the behavior of the object type are called methods. Object types reduce complexity by breaking down a large system into logical entities. This allows you to create software components that are modular, maintainable, and reusable. When you define an object type using the CREATE TYPE statement (in SQL*Plus for example), you create an abstract template for some real-world object. As the following example of a bank account shows, the template specifies only those attributes and behaviors the object will need in the application environment: Example objecttype: ------------------- CREATE TYPE Bank_Account AS OBJECT ( acct_number INTEGER(5), balance REAL, status VARCHAR2(10), MEMBER PROCEDURE open (amount IN REAL), MEMBER PROCEDURE verify_acct (num IN INTEGER), MEMBER PROCEDURE close (num IN INTEGER, amount OUT REAL), MEMBER PROCEDURE deposit (num IN INTEGER, amount IN REAL), MEMBER PROCEDURE withdraw (num IN INTEGER, amount IN REAL), MEMBER FUNCTION curr_bal (num IN INTEGER) RETURN REAL ); At run time, when the data structure is filled with values, you have created an instance of an abstract bank account. You can create as many instances (called objects) as you need. Each object has the number, balance, and status of an actual bank account. The important thing to notice here, is that Bank_account has internal data, and some type of internal memberfunctions declared. This is quite typical for OO objects. 1.2 Objects in Traditional C++: =============================== In C++, classes and objects have the following meaning. A class is a sort of a template, for creating real instantiated objects. Look at the class "Book": It’s a class for someone who shelves books: but a real copy is an instance It’s an object for the person does order processing: an instance of class book. A class has it's private and public memberfunctions and data. Put in another way: A class is a fundamental building block of OO software. A class defines a data type, much like a struct would be in C. In a computer science sense, a type consists of both a set of states and a set of operations which transition between those states. Thus int is a type because it has both a set of states and it has operations like i + j or i++, etc. In exactly the same way, a class provides a set of (usually public) operations, and a set of (usually non-public) data bits representing the abstract values that instances of the type can have. Its also an important feature of OO, that the "internal data" can only get a value, or the values of the data can only be retrieved, by the member functions of the object. You can imagine that int is a class that has member functions called operator++, etc. (int isn't really a class, but the basic analogy is this: a class is a type, much like int is a type.) After the declaration int i; we say that "i is an object of type int." In OO/C++, "object" usually means "an instance of a class." Thus a class defines the behavior of possibly many objects (instances). -- Example 1. -- ---------- // objpart.cpp // widget part as an object #include class part // specify an object { private: // private data, only visible to the object itself int modelnumber; // ID of widget int partnumber; // ID of widget part float cost; // cost of part public: void setpart(int mn, int pn, float c) // memberfunction to set the data. it's public so anyone can call it. { modelnumber=mn; partnumber=pn; cost=c; } void showpart() // memberfunction to show the data { cout << "\nModel " << modelnumber; // cout is a standard operator which prints to standard output, cout << ", part " << partnumber; // which is your screen. cout << ", costs $" << cost; } }; void main() { part part1; part1.setpart(6244,373,217.55); part1.showpart(); } -- Example 2: -- ---------- class Animal { public: void Eat( Food* ); protected: float m_fWeight; }; class Dog : public Animal { public: void Bark( void ); protected: string m_strName; }; -- instatiate object's animal elephant; animal lion; dog barky; In example 2, you see the effect of "inheritance": the subclass Dog, inherets the member function (or method) "Eat" from the parentclass Animal. Now that an elementary idea of classses and objects is established, We return to C#: ===================================== 2. BASIC ELEMENTS OF C# PROGRAMS: ===================================== 2.1 CLASSES AND METHODS IN C# IN GENERAL: ======================================== 2.1.1. Class Declaration: ========================== As C# is an object-oriented language, C# programs must be placed in classes. To illustrate a Class declaration in general, look at the following example: Example 1: ---------- public class Cat { public Cat(string aName) // memberfunction or method { _name = aName; } public void sleep() // memberfunction or method { _sleeping = true; } public void wake() // memberfunction or method { _sleeping = false; } // Member variables protected bool _sleeping; protected string _name; } To instantiate an object of class Cat: Cat anastasia = new Cat(); To call a method, or memberfunction, of anastasia: anastasia.Sleep(); - "Public" means that an external entity, can call the function. - "Private" means that it's for internal use only. The object does not expose the member. - "Protected" means that it's available for components in the Assembly (see a later section). 2.1.2 METHODS: ============== In C++, you can write functions which have nothing to do with a class. But also in C++, most of the time, memberfunctions of a class, are defined inside the class specifier. But this need not always be the case: it's possible to declare the memberfunction inside the class, but the body of the function is listed elsewhere, In C# all methods exist as class members because C# does not support standalone functions. Methods are always declared as part of the class declaration. Unlike C++, there is no way to declare a C# method implementation seperately from the class declaration. A method declaration consists of a accessibility level, a return type, a name and a list of zero or more parameters Example 1: ---------- public int GetNextRecordId(string TableName) { ... } If the accessibility level is omitted, the level is private by default. Methods can be declared as static, and in this case the method must be called using the class name rather than using an object reference. Example 2: ---------- // define the class Cat class Cat: Animal { public static void ChaseMouse() { ... } } // define the class CatApp class CatApp { static void Main() { Cat c = new Cat(); // This is not OK: c.ChaseMouse(); // This is OK: Cat.ChaseMouse(); } ... } 2.1.3 Inheritance: ================== Class Animal { public void Eat() { .. } public void Sleep() { .. } } class Cat: Animal { public static void ChaseMouse() { .. } } Here, Cat inherits all members from the Animal class. Inheritance enables you to look for similarities between classes and factor those similarities out into base classes that are shared by descendent classes. For example, all of our animal classes like Cat or Dog, share the common characteristics Eat and Sleep. All types in C# are ultimately derived from the object class. Of course, not all classes derive directly from object, but if you follow the inheritance hierarchy for any type, you’ll eventually come to object. In fact, deriving from object is such a fundamental aspect of programming in C# that the compiler will automatically generate the necessary code to inherit from object if you don’t specify any inheritance for your class. This means that the following two class definitions are identical: class Cat { .. } class Cat: object { .. } Declaring a Class as abstract or sealed: ---------------------------------------- When you’re declaring a hierarchy of classes such as the animal classes, the base classes are often incomplete and shouldn’t be directly instantiated. Instead of directly creating an object from the Animal class, it’s more appropriate to create an instance of the Cat or Dog class. In C#, base classes that aren’t to be directly instantiated are tagged with the abstract keyword, as follows: abstract class Animal { .. } Using the abstract keyword with your base classes allows the compiler to enforce proper use of your class hierarchy by preventing base classes from being directly instantiated. If a method signature is defined in an abstract base class but not implemented in the class, it is marked as abstract, as shown here: abstract class Horse { abstract public void ChaseAfterBadGuys(); } Communicating with the base class: ---------------------------------- Like C++, C# allows you to access the current object through the this keyword. In addition, C# allows you to access the members of the immediate base class through the base keyword. The following example calls the PreDraw method of the CommandWindow class: class MyCommandWindow: CommandWindow { public void DrawShape() { base.PreDraw(); Draw(); } } 2.2 Main method: ================ A Console mode application (dos box), has a Main method that serves as the entrypoint for your application. Take a look at the following "HelloWorld" application: using System; namespace HelloWorld { class HelloWorldApp { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } } The first thing to note about C# is that it is case-sensitive. You will therefore get compiler errors if, for instance, you write 'console' rather than 'Console'. The second thing to note is that every statement finishes with a semicolon (;) or else takes a code block within curly braces. Line 1 of the code declares we are using the System namespace (namespaces are also covered later). The point of this declaration is mostly to save ourselves time typing. Because the 'Console' object used in line 10 of the code actually belongs to the 'System' namespace, its fully qualified name is 'System.Console'. However, because in line 1 we declare that the code is using the System namespace, we can then leave off the 'System.' part of its name within the code. The namespace "HelloWorld" helps to insulate any types you create from types that might exist elswhere in the .NET framework. When compiled and run, the program above will automatically run the 'Main' method declared and begun in line 6. Note again C#'s case-sensitivity - the method is 'Main' rather than 'main'. In order to run it, the program above must first be saved in a file. Unlike in Java, the name of the class and the name of the file in which it is saved do not need to match up, although it does make things easier if you use this convention. In addition, you are free to choose any extension for the file, but it is usual to use the extension '.cs'. Suppose that you have saved the file as 'HelloWorld.cs'. Then to compile the program from a command line, you would use the command csc HelloWorld.cs This command would generate the executable HelloWorld.exe, which could be run in the usual way, by entering its name: HelloWorld You can use the following "flavors" which explain the "void" and "args" elements: static void Main(string[] args) { // No return values (void); accepts command-line parameters } static int Main(string[] args) { // Returns integer value; accepts command-line parameters } static void Main() { // No return values (void); no command-line parameters } static int Main() { // Returns integer value; no command-line parameters } Before the word Main is a static modifier. The static modifier explains that this method works in this specific class only, rather than an instance of the class. This is necessary, because when a program begins, no object instances exist. 2.3 Pointers: ============= Maybe it's strange to start with an element that's relatively not much encouraged in C#. But pointers are well known from languages as C,C++ etc.. In C#, also because of the "Garbage collection process", the use of pointers is limited to "unsafe code", that is, code that is "marked" as unsafe. So it's understandable that pointers are avoided in C# code. However, we still need to know what a pointer is, and how it can be used. A pointer is a variable that holds the memory address of another type. Pointers are declared implicitly, using the 'dereferencer' symbol *, as in the following example: int *p; What does it mean? Look at the following examples: Example 1: ---------- int i = 5; int *p; p = &i; Here you should know that the symbol & is the operator which in this context returns the memory address of the variable it prefixes. Given the above, the code *p = 10; changes the value of i to 10, since '*p' can be read as 'the integer located at the memory value held by p'. Example 2: ---------- A pointer can be declared in relation to an array, as in the following: int[] a = {4, 5}; int *b = a; What happens in this case is that the memory location held by b is the location of the first type held by a. This first type must, as before, be a value type. 2.4 Introduction to arrays ========================== An array is a reference type that contains a sequence of variables of a specific type (value types or reference types). An array is declared by including index brackets between the type and the name of the array variable, as shown here: int [] ages; This example declares a variable named ages that’s an array of int, but it doesn’t attach that reference variable to an actual array object. To do so requires that the array be initialized, as shown here: int [] ages = {5, 8, 39}; Arrays are reference types that the Visual C# .NET compiler automatically subclasses from the System.Array class. When an array contains value types, the space for the types is allocated as part of the array. When an array contains ­reference elements, the array contains only references—the objects are allocated elsewhere on the managed heap, The individual elements of an array are accessed through an index, with 0 always referring to the first element in the array, as follows: int currentAge = ages[0]; Some interresting properties: -- You can determine the number of elements in an array by using the Length property: int elements = nameArray.Length; -- An array can be cloned with the Clone method, which returns a new copy of the array. Because Clone is declared as returning an array of object, you must explicitly state the type of the new array, as follows: string [] secondArray = (string[])nameArray.Clone(); -- Clear is a static method in the Array class that removes one or more of the array elements by setting the removed array elements to 0 (for value types) or null (for reference types). The array to be cleared is passed as the first parameter, along with the index of the first element to clear and the number of elements be removed. To eliminate all the elements of the array, pass 0 as the start element and the array length as the third parameter, as shown here: Array.Clear(nameArray, 0, nameArray.Length); -- Reverse is a static method in the Array class that reverses the order of array elements, operating on either the complete array or just a subset of elements. To reverse an entire array, simply pass the array to the static method, as shown here: Array.Reverse(nameArray); To reverse a range within the array, pass the array along with the start element and the number of items to be reversed. Array.Reverse(nameArray, 0, nameArray.Length); -- Sort is a static method that sorts an array. There are several versions of Sort; the simplest version accepts an array as its only parameter and sorts the elements in ascending order. Array.Sort(nameArray); The following example manipulates an array containing the names of the month. The array is examined, reversed, sorted, cloned, and finally cleared. using System; namespace MSPress.CSharpCoreRef.ArrayExample { class ArrayExampleApp { static void Main(string[] args) { string [] months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; Console.WriteLine("The array has a rank of {0}.", months.Rank); int elements = months.Length; Console.WriteLine("There are {0} elements in the array.", elements); Console.WriteLine("Reversing..."); Array.Reverse(months); PrintArray(months); Console.WriteLine("Sorting..."); Array.Sort(months); PrintArray(months); string [] secondArray = (string[])months.Clone(); Console.WriteLine("Cloned Array..."); PrintArray(months); Console.WriteLine("Clearing..."); Array.Clear(months, 0, months.Length); PrintArray(months); } /// /// Print each element in the names array. /// static void PrintArray(string[] names) { foreach(string name in names) { Console.WriteLine(name); } } } } 2.5 Loops and flow control: =========================== C# provides a number of the common loop, and flow statements: - while - do-while - for - foreach - break - continue - goto - return - throw Here we will illustrate them with some simple examples: WHILE LOOPS: ============ syntax: while (expression) statement[s] A 'while' loop executes a statement, or a block of statements wrapped in curly braces, repeatedly until the condition specified by the boolean expression returns false. Example 1: ---------- int a = 0; while (a < 3) { System.Console.WriteLine(a); a++; } It produces the following output: 0 1 2 Example 2: ---------- using System; class WhileLoop { public static void Main() { int myInt = 0; while (myInt < 10) { Console.Write("{0} ", myInt); myInt++; } Console.WriteLine(); } } DO-WHILE LOOPS: =============== syntax: do statement[s] while (expression) do-while' loop is just like a 'while' loop except that the condition is evaluated after the block of code specified in the 'do' clause has been run. So even where the condition is initially false, the block runs once. For instance, the following code outputs '4': Example 1: ---------- int a = 4; do { System.Console.WriteLine(a); a++; } while (a < 3); Example 2: ---------- using System; class DoLoop { public static void Main() { string myChoice; do { // Print A Menu Console.WriteLine("My Address Book\n"); Console.WriteLine("A - Add New Address"); Console.WriteLine("D - Delete Address"); Console.WriteLine("M - Modify Address"); Console.WriteLine("V - View Addresses"); Console.WriteLine("Q - Quit\n"); Console.WriteLine("Choice (A,D,M,V,or Q): "); // Retrieve the user's choice myChoice = Console.ReadLine(); // Make a decision based on the user's choice switch(myChoice) { case "A": case "a": Console.WriteLine("You wish to add an address."); break; case "D": case "d": Console.WriteLine("You wish to delete an address."); break; case "M": case "m": Console.WriteLine("You wish to modify an address."); break; case "V": case "v": Console.WriteLine("You wish to view the address list."); break; case "Q": case "q": Console.WriteLine("Bye."); break; default: Console.WriteLine("{0} is not a valid choice", myChoice); break; } // Pause to allow the user to see the results Console.Write("Press Enter key to continue..."); Console.ReadLine(); Console.WriteLine(); } while (myChoice != "Q" && myChoice != "q"); // Keep going until the user wants to quit } } FOR LOOPS: ========== syntax: for (statement1; expression; statement2) statement[s]3 The 'for' clause contains three parts. Statement1 is executed before the loop is entered. 'For' loops tend to be used when one needs to maintain an iterator value. Usually, as in the following example, the first statement initialises the iterator, the condition evaluates it against an end value, and the second statement changes the iterator value. Example 1: ---------- for (int a =0; a<5; a++) { System.Console.WriteLine(a); } Example 2: ---------- using System; class ForLoop { public static void Main() { for (int i=0; i < 20; i++) { if (i == 10) break; if (i % 2 == 0) continue; Console.Write("{0} ", i); } Console.WriteLine(); } } FOREACH LOOPS: ============== syntax: foreach (variable1 in variable2) statement[s] The 'foreach' loop is used to iterate through the values contained by any object which implements the IEnumerable interface. When a 'foreach' loop runs, the given variable1 is set in turn to each value exposed by the object named by variable2. As we have seen previously, such loops can be used to access array values. So, we could loop through the values of an array in the following way: Example 1: ---------- int[] a = new int[]{1,2,3}; foreach (int b in a) System.Console.WriteLine(b); The main drawback of 'foreach' loops is that each value extracted (held in the given example by the variable 'b') is read-only. Example 2: ---------- using System; class ForEachLoop { public static void Main() { string[] names = {"Cheryl", "Joe", "Matt", "Robert"}; foreach (string person in names) { Console.WriteLine("{0} ", person); } } } The foreach loop allows you to iterate through a collection. An array, is one such collection BREAK: ====== The 'break' statement breaks out of the 'while' and 'for' loops, and the 'switch' statements covered later in this lesson. The following code gives an example - albeit a very inefficient one - of how it could be used. The output of the loop is the numbers from 0 to 4. int a = 0; while (true) { System.Console.WriteLine(a); a++; if (a == 5) break; } CONTINUE: ========= The 'continue' statement can be placed in any loop structure. When it executes, it moves the program counter immediately to the next iteration of the loop. The following code example uses the 'continue' statement to count the number of between 1 and 100 inclusive that are not multiples of seven. At the end of the loop the variable y holds the required value. int y = 0; for (int x=1; x<101; x++) { if ((x % 7) == 0) continue; y++; } GOTO: ===== The 'goto' statement is used to make a jump to a particular labelled part of the program code. It is also used in the 'switch' statement described below. We can use a 'goto' statement to construct a loop, as in the following example (but again, this usage is not recommended): int a = 0; start: System.Console.WriteLine(a); a++; if (a < 5) goto start; IF ELSE: ======== 'If-else' statements are used to run blocks of code conditionally upon a boolean expression evaluating to true. The 'else' clause, present in the following example, is optional. if (a == 5) System.Console.WriteLine("A is 5"); else System.Console.WriteLine("A is not 5"); If statements can also be emulated by using the conditional operator "?". The conditional operator returns one of two values, depending upon the value of a boolean expression. To take a simple example, the line of code int i = (myBoolean) ? 1 : 0 ; sets i to 1 if myBoolean is true, and sets i to 0 if myBoolean is false. The 'if' statement in the previous code example could therefore be written like this: System.Console.WriteLine( a==5 ? "A is 5" : "A is not 5"); SWITCH: ======= This is a sort "case" statement, known from other environments. 'Switch' statements provide a clean way of writing multiple if - else statements. In the following example, the variable whose value is in question, is 'a'. If a equals 1, then the output is 'a>0'; if a equals 2, then the output is 'a>1 and a>0'. Otherwise, it is reported that the variable is not set. switch(a) { case 2: Console.WriteLine("a>1 and "); goto case 1; case 1: Console.WriteLine("a>0"); break; default: Console.WriteLine("a is not set"); break; } Each case (where this is taken to include the 'default' case) will either have code specifying a conditional action, or no such code. Where a case does have such code, the code must (unless the case is the last one in the switch statement) end with one of the following statements: break; goto case k; (where k is one of the cases specified) goto default; From the above it can be seen that C# 'switch' statements lack the default 'fall through' behaviour found in C++ and Java. 2.6 How to write some of those handy shortcuts: =============================================== Some shorthand notations, like for example "a++", needs to be explained: n++ or ++n : ------------ The ++ and -- operators work with a single operand, incrementing or decrementing the value of the operand. You can use either the prefix or postfix versions of these operators. The difference is subtle but important if you’re testing for the resulting value of the expression. A prefix increment or decrement operation increments the value of the operand, and the resulting expression is the changed value of the operand, as shown here: int n = 41; int answer = ++n; // answer = 42, n = 42; Although a postfix increment or decrement operation increments the value of the operand, the resulting expression has the value of the operand before the operator was applied, as shown here: int n = 41; int answer = n++; // answer = 41, n = 42; +=: --- The addition assignment operator (+=) combines addition and assignment, adding the first operand to the second and then storing the result in the first operand, as shown here: y = 40; y += 2; This example is equivalent to the following code: y = 40; y = y + 2; 2.7 Value types and reference types: ==================================== Here's a quick recap of the difference between value types and reference types. - where a variable v contains a value type, it directly contains an object with some value. No other variable v' can directly contain the object contained by v (although v' might contain an object with the same value). Value types in Visual C# include the primitive types such as int, float, and decimal. Value types also include enum and struct types (discussed in Chapter 2). Other types in the .NET Framework are also explicitly declared to be value types, such as the Rectangle, Point, and Size structures found in the System.Drawing namespace When a value type variable is declared in Visual C#, the variable contains the actual type instance. For example, an integer named Age is declared as follows: int Age = 42; The Visual C# .NET compiler will allocate 4 bytes of stack area for the Age variable, making it available for direct access without any indirection to the managed heap. For consistency, value type variables can also be declared using the new syntax, as shown here: int Age = new int(42); In the following lines of code, two variables are declared and set with integer values. int x = 10; int y = x; y = 20; // after this statement x holds value 10 and y holds value 20 - where a variable v contains a reference type, what it directly contains is something which refers to an object. Another variable v' can contain a reference to the same object refered to by v. Reference types actually hold the value of a memory address occupied by the object they reference. Consider the following piece of code, in which two variables are given a reference to the same object (for the sake of the example, this object is taken to contain the numeric property 'myValue'). object x = new object(); x.myValue = 10; object y = x; y.myValue = 20; // after this statement both x.myValue and y.myValue equal 20 If you take a look at the following code: Shape rect=new Shape(); Shape tempRect=rect; Then both variables point to the same object. Boxing: ------- C# allows you convert any value type to a corresponding reference type, and to convert the resultant 'boxed' type back again. The following piece of code demonstrates boxing. When the second line executes, an object is initiated as the value of 'box', and the value held by i is copied across to this object. It is interesting to note that the runtime type of box is returned as the boxed value type; the 'is' operator thus returns the type of box below as 'int'. int i = 123; object box = i; if (box is int) {Console.Write("Box contains an int");} // this line is printed 2.8 Exception handling Try.. Catch.. Finally: ============================================= Exceptions are unforeseen errors that happen in your programs. Most of the time, you can, and should, detect and handle program errors in your code. For example, validating user input, checking for null objects, and verifying the values returned from methods are what you expect, are all examples of good standard error handling that you should be doing all the time. However, there are times when you don't know if an error will occur. For example, you can't predict when you'll receive a file I/O error, run out of system memory, or encounter a database error. When exceptions occur, they are said to be "thrown". What is actually thrown is an object that is derived from the System.Exception class. In the next section, I'll be explaining how thrown exceptions are handled with try/catch blocks. The System.Exception class provides several methods and properties for obtaining information on what went wrong. For example, the Message property provides summary information about what the error was, the StackTrace property provides information from the stack for where the problem occurred, and the ToString() method is overridden to reveal a verbose description of the entire exception. Identifying the exceptions you'll need to handle depends on the routine you're writing. For example, if the routine opened a file with the "System.IO.File.OpenRead()" method, it could throw any of the following exceptions: SecurityException ArgumentException ArgumentNullException PathTooLongException DirectoryNotFoundException UnauthorizedAccessException FileNotFoundException NotSupportedException It's easy to find out what exceptions a method can raise by looking in the .NET Frameworks SDK Documentation. Just go to the Reference/Class Library section and look in the Namespace/Class/Method documentation for the methods you use. The exception in the list above were found by looking at the OpenRead() method definition of the File class in the System.IO namespace. Each exception identified has a hyperlink to its class definition that you can use to find out what that exception is about. Once you've figured out what exceptions can be generated in your code, you need to put the mechanisms in place to handle the exceptions, should they occur. try/catch Blocks ---------------- When exceptions are thrown, you need to be able to handle them. This is done by implementing a try/catch block. Code that could throw an exception is put in the try block an exception handling code goes in the catch block. The following listing shows how to implement a try/catch block. Since an OpenRead() method could throw one of several exceptions, it is placed in the try block. If an exception is thrown, it will be caught in the catch block. The code will print message and stack trace information out to the console if an exception is raised. using System; using System.IO; class TryCatchDemo { static void Main(string[] args) { try { File.OpenRead("NonExistentFile"); // on purpose a non-existent file } catch(Exception ex) { Console.WriteLine(ex.ToString()); } } } Although the code above only has a single catch block, all exceptions will be caught there because the type is of the base exception type "Exception". In exception handling, more specific exceptions will be caught before their more general parent exceptions. For example, the following snippet shows how to place multiple catch blocks: catch(FileNotFoundException fnfex) { Console.WriteLine(fnfex.ToString()); } catch(Exception ex) { Console.WriteLine(ex.ToString()); } If the file doesn't exist, a FileNotFoundException exception will be thrown and caught by the first catch block. However, if a PathTooLongException exception was raised, the second catch part would catch the exception. This is because there isn't a catch block for the PathTooLongException exception and the generic Exception type catch block is the only option available to catch the exception. Exceptions that are not handled will normally bubble up the stack until a calling routine in the call chain handles them. If you forget to include try/catch blocks in a part of your code and there aren't any try/catch blocks earlier in the call chain, your program will abort with a message describing the exception. To your users this would be very cryptic and uncomfortable. It is good practice to provide exception handling in your programs. Finally Blocks: --------------- An exception can leave your program in an inconsistent state by not releasing resources or doing some other type of cleanup. A catch block is a good place to figure out what may have went wrong and try to recover, however it can't account for all scenarios. Sometimes you need to perform clean up actions whether or not your program succeeds. These situations are good candidates for using a finally block. In C#, a finally block is implemented as "garanteed to run" at all times. The following code illustrates the usefulness of a finally block. As you know, a file stream must be closed when your done with it. In this case, the file stream is the resource that needs to be cleaned up. In the below code, outStream is opened successfully, meaning the program now has a handle to an open file resource. When trying to open the inStream, a FileNotFoundException exception is raised, causing control to go immediately to the catch block. It's possible to close the outStream in the catch block, but what if the algorithm executed successfully without an exception? On success, the file would never be closed. Fortunately, we've included a finally block which will always be executed. That's right, regardless of whether the algorithm in the try block raises an exception or not, the code in the finally block will be executed before control leaves the method. using System; using System.IO; class FinallyDemo { static void Main(string[] args) { FileStream outStream = null; FileStream inStream = null; try { outStream = File.OpenWrite("DestinationFile.txt"); inStream = File.OpenRead("BogusInputFile.txt"); } catch(Exception ex) { Console.WriteLine(ex.ToString()); } finally { if (outStream != null) { outStream.Close(); Console.WriteLine("outStream closed."); } if (inStream != null) { inStream.Close(); Console.WriteLine("inStream closed."); } } } } A finally block is not required and you may ask what happens if you just put code after the catch block. True, under normal circumstances, if the exception is caught, all code following the catch will be executed. However, try/catch/finally is for exceptional circumstances and it is better to plan for the worst to make your program more robust. For example, if one of the catch handlers rethrew and exception or caused another exception, the code following the catch block (not in a finally block) would never be executed. Also, if you don't catch the exception at all, program flow would immediately do a stack walk looking for an exception handler that fits and the code following the catch blocks would not be executed. Since there is too much potential for code in an algorithm to not be executed, a finally block is your insurance for executing those critical actions you need. Before we go deeper into C#, we will talk a bit about the .NET architecture and program structures. ================================== 3. About the .NET Architecture: ================================== The .NET platform consists of the CLR (common language runtime) that is responsible for managing and executing code written for the .NET framework, and a large set of Class libraries. The .NET Framework has two main components: The CLR (common language runtime) and the .NET Framework class library. CLR: ==== The common language runtime is the foundation of the .NET Framework. You can think of the runtime as an agent that manages code at execution time, providing core services such as memory management, thread management, and remoting, while also enforcing strict type safety and other forms of code accuracy that ensure security and robustness. In fact, the concept of code management is a fundamental principle of the runtime. Code that targets the runtime is known as managed code, while code that does not target the runtime is known as unmanaged code. The class library, the other main component of the .NET Framework, is a comprehensive, object-oriented collection of reusable types (classes) that you can use to develop applications ranging from traditional command-line or graphical user interface (GUI) applications to applications based on the latest innovations provided by ASP.NET, such as Web Forms and XML Web services. You can make some comparison to a virtual machine, on which the executing code runs, but it's not the same all the way. The runtime is designed to enhance performance. Although the common language runtime provides many standard runtime services, managed code is never interpreted. A feature called just-in-time (JIT) compiling enables all managed code to run in the native machine language of the system on which it is executing. Meanwhile, the memory manager removes the possibilities of fragmented memory and increases memory locality-of-reference to further increase performance. .NET Framework Class Library ============================ The .NET Framework class library is a collection of reusable types that tightly integrate with the common language runtime. The class library is object oriented, providing types from which your own managed code can derive functionality. This not only makes the .NET Framework types easy to use, but also reduces the time associated with learning new features of the .NET Framework. In addition, third-party components can integrate seamlessly with classes in the .NET Framework. For example, the .NET Framework collection classes implement a set of interfaces that you can use to develop your own collection classes. Your collection classes will blend seamlessly with the classes in the .NET Framework. As you would expect from an object-oriented class library, the .NET Framework types enable you to accomplish a range of common programming tasks, including tasks such as string management, data collection, database connectivity, and file access. In addition to these common tasks, the class library includes types that support a variety of specialized development scenarios. For example, you can use the .NET Framework to develop the following types of applications and services: - Console applications. - Scripted or hosted applications. - Windows GUI applications (Windows Forms). - ASP.NET applications (Web forms). - XML Web services. - Windows services. For example, the Windows Forms classes are a comprehensive set of reusable types that vastly simplify Windows GUI development. If you write an ASP.NET Web Form application, you can use the Web Forms classes. .NET program architecture: ========================== The Visual C# compiler (or other tool, such as "csc" of the SDK), does not generate machine code that can be executed directly on your computer. Instead your project's source code is compiled into an ASSEMBLY, as we have tried to visualize in the underneath figure. Assemblies are the building blocks of .NET Framework applications; they form the fundamental unit of deployment, version control, reuse, activation scoping, and security permissions. An assembly is a collection of types and resources that are built to work together and form a logical unit of functionality. An assembly provides the common language runtime with the information it needs to be aware of type implementations. To the runtime, a type does not exist outside the context of an assembly. -------------------- |assembly | | ---------------- | | | METADATA | | | | | | | ---------------- | | | Intermediate | | | | Language IL | | | ---------------- | -------------------- An Assembly has two parts: MSIL and Metadata. The IL contains the executable portion of the program. But it cannot be be executed directly on you computer because it hasn't been translated into the binary format. Instead, it must undergo a final compilation pass by a compiler that's part of the .NET framework. IL: --- When the code in an assembly must be executed, the runtime compiles the assembly into machine code. However, the entire assembly isn't compiled in one step. Instead, each method in the assembly is compiled as it is needed in a process known as "just-in-time compilation" or jitting. As an option, you may choose to compile your assembly into processor specific code. This is not done by the Visual C# .NET compiler. The tool you can use for this purpose is "ngen.exe". Metadata: --------- This describes completely the assembly contents. It makes an assembly completely "self describing" and eliminates the need for component registration. Since metadata is stored in a programming-language-independent fashion with the code, not in a central store such as the Windows Registry, it makes .NET applications self-describing. The metadata can be queried at runtime to get information about the code. Types of Assemblies: -------------------- Private assemblies: Used by a single application, typically located in the same directory as the application that uses them. Because a private assembly isn't shared with other applications, they can be easily updated or replaced, with no impact on other applications. Shared assemblies: Intended for use by multiple applications. A shared assembly has restrictions placed on it by the runtime and must adhere to naming and versioning rules. An aplication that depends on private assemblies can just be copied or moved to another machine (which has the .NET framework also) and it can be run straight away. An assembly in it's most common form "looks" like any other .EXE or .DLL. Other features of assemblies: ----------------------------- It contains code that the common language runtime executes. Microsoft intermediate language (MSIL) code in a portable executable (PE) file will not be executed if it does not have an associated assembly manifest. Note that each assembly can have only one entry point (that is, DllMain, WinMain, or Main). It forms a security boundary. An assembly is the unit at which permissions are requested and granted. It forms a type boundary. Every type's identity includes the name of the assembly in which it resides. A type called MyType loaded in the scope of one assembly is not the same as a type called MyType loaded in the scope of another assembly. It forms a reference scope boundary. The assembly's manifest contains assembly metadata that is used for resolving types and satisfying resource requests. It specifies the types and resources that are exposed outside the assembly. The manifest also enumerates other assemblies on which it depends. It forms a version boundary. The assembly is the smallest versionable unit in the common language runtime; all types and resources in the same assembly are versioned as a unit. The assembly's manifest describes the version dependencies you specify for any dependent assemblies. It forms a deployment unit. When an application starts, only the assemblies that the application initially calls must be present. Other assemblies, such as localization resources or assemblies containing utility classes, can be retrieved on demand. This allows applications to be kept simple and thin when first downloaded. Assemblies can be static or dynamic. Static assemblies can include .NET Framework types (interfaces and classes), as well as resources for the assembly (bitmaps, JPEG files, resource files, and so on). Static assemblies are stored on disk in PE files. You can also use the .NET Framework to create dynamic assemblies, which are run directly from memory and are not saved to disk before execution. You can save dynamic assemblies to disk after they have executed. There are several ways to create assemblies. You can use development tools, such as Visual Studio .NET, that you have used in the past to create .dll or .exe files. You can use tools provided in the .NET Framework SDK to create assemblies with modules created in other development environments. You can also use common language runtime APIs, such as Reflection.Emit, to create dynamic assemblies. ================================== 4. EXAMPLES OF SIMPLE C# PROGRAMS: ================================== In the first sections, we will stick to CONSOLE programs. In section 6, we will turn our attention to Windows applications. Console programs are programs without a GUI. On windows it means running a program from a DOS box. Ofcourse, also with console programs, everything is coded in classes. But console programs always must have a Main() method. You can write the source with any text editor (notepad, textpad etc..). Save the file with an appropriate name. Recommended is to let the name be the same name as you have named the class. From the DOt NET Framework SDK, you have the csc.exe (c sharp compile) utility, with which you can compile your code to an executable. Ofcourse, if you have Visual Studio .NET, that would be mutch better. 4.1 Hello example with argument(s): =================================== // Namespace Declaration using System; // Program start class class NamedWelcome { // Main begins program execution. public static void Main(string[] args) { // Write to console Console.WriteLine("Hello, {0}!", args[0]); Console.WriteLine("Welcome to the C# Station Tutorial!"); } } Note that identifiers are case sensitive. Also note that you do not need to close a block with ";". Compile: -------- E:\data\C#\projects\p1>csc NamedWelcome.cs Microsoft (R) Visual C# .NET Compiler version 7.00.9466 for Microsoft (R) .NET Framework version 1.0.3705 Copyright (C) Microsoft Corporation 2001. All rights reserved. Run: ---- E:\data\C#\projects\p1>NamedWelcome appie Hello, appie! Welcome to the C# Station Tutorial! E:\data\C#\projects\p1>NamedWelcome appie,piet Hello, appie,piet! Welcome to the C# Station Tutorial! E:\data\C#\projects\p1>NamedWelcome appie and piet Hello, appie! Welcome to the C# Station Tutorial! Explanation: ------------ In Listing 5.1, you'll notice an entry in the Main method's parameter list. The parameter name is args. It is what you use to refer to the parameter later in your program. The string[] expression defines the type of parameter that args is. The string type holds characters. These characters could form a single word, or multiple words. The "[]", square brackets denote an Array, which is like a list. Therefore, the type of the args parameter, is a list of words from the command-line. You'll also notice an additional Console.WriteLine(...) statement within the Main method. It has a formatted string with a "{0}" parameter embedded in it. The first parameter in a formatted string begins at number 0, the second is 1, and so on. The "{0}" parameter means that the next argument following the end quote will determine what goes in that position. Hold that thought, and now we'll look at the next argument following the end quote. This is the args[0] argument, which refers to the first string in the args array. The first element of an Array is number 0, the second is number 1, and so on. For example, if I wrote "NamedWelcome Joe" on the command-line, the value of args[0] would be "Joe". Now we'll get back to the embedded "{0}" parameter in the formatted string. Since args[0] is the first argument, after the formatted string, of the Console.WriteLine() statement, its value will be placed into the first embedded parameter of the formatted string. When this command is executed, the value of args[0], which is "Joe" will replace "{0}" in the formatted string. Upon execution of the command-line with "NamedWelcome Joe", the output will be as follows: >Hello, Joe! >Welcome to the C# Station Tutorial! 4.2 Getting Interactive Input: ------------------------------ // Namespace Declaration using System; // Program start class class InteractiveWelcome { // Main begins program execution. public static void Main() { // Write to console/get input Console.Write("What is your name?: "); Console.Write("Hello, {0}! ", Console.ReadLine()); Console.WriteLine("Welcome to the C# Station Tutorial!"); } } Another way to provide input to a program is via the console. Listing 5.2 shows how to obtain interactive input from the user. This time, the Main method doesn't have any parameters. However, there are now three statements and the first two are different from the third. They are Console.Write(...) instead of Console.WriteLine(...). The difference is that the Console.Write(...) statement writes to the console and stops on the same line, but the Console.WriteLine(...) goes to the next line after writing to the console. string name = Console.ReadLine(); Console.Write("Hello, {0}! ", name); 4.3 Variable declaration, 1: ============================ // Listing 4.3. Displaying Boolean Values: Boolean.cs using System; class Booleans { public static void Main() { bool content = true; bool noContent = false; Console.WriteLine("It is {0} that C# Station provides C# programming language content.", content); Console.WriteLine("The statement above is not {0}.", noContent); } } 4.4 Variable declaration, 2: ============================ using System; class Unary { public static void Main() { int unary = 0; int preIncrement; int preDecrement; int postIncrement; int postDecrement; int positive; int negative; sbyte bitNot; bool logNot; preIncrement = ++unary; Console.WriteLine("Pre-Increment: {0}", preIncrement); preDecrement = --unary; Console.WriteLine("Pre-Decrement: {0}", preDecrement); postDecrement = unary--; Console.WriteLine("Post-Decrement: {0}", postDecrement); postIncrement = unary++; Console.WriteLine("Post-Increment: {0}", postIncrement); Console.WriteLine("Final Value of Unary: {0}", unary); positive = -postIncrement; Console.WriteLine("Positive: {0}", positive); negative = +postIncrement; Console.WriteLine("Negative: {0}", negative); bitNot = 0; bitNot = (sbyte)(~bitNot); Console.WriteLine("Bitwise Not: {0}", bitNot); logNot = false; logNot = !logNot; Console.WriteLine("Logical Not: {0}", logNot); } } You can expect the following output from the above program. Pre-Increment: 1 Pre-Decrement 0 Post-Decrement: 0 Post-Increment -1 Final Value of Unary: 0 Positive: 1 Negative: -1 Bitwise Not: -1 Logical Not: True Another example: using System; class Binary { public static void Main() { int x, y, result; float floatResult; x = 7; y = 5; result = x+y; Console.WriteLine("x+y: {0}", result); result = x-y; Console.WriteLine("x-y: {0}", result); result = x*y; Console.WriteLine("x*y: {0}", result); result = x/y; Console.WriteLine("x/y: {0}", result); floatResult = (float)x/(float)y; Console.WriteLine("x/y: {0}", floatResult); result = x%y; Console.WriteLine("x%y: {0}", result); result += x; Console.WriteLine("result+=x: {0}", result); } } And here's the output: x+y: 12 x-y: 2 x*y: 35 x/y: 1 x/y: 1.4 x%y: 2 result+=x: 9 ============================ 5. SOME MORE INFO ON ARRAYS: ============================ Another data type is the Array, which can be thought of as a container that has a list of storage locations for a specified type. When declaring an Array, specify the type, name, dimensions, and size. // Listing 6.1 Array Operations: Array.cs using System; class Array { public static void Main() { int[] myInts = { 5, 10, 15 }; bool[][] myBools = new bool[2][]; myBools[0] = new bool[2]; myBools[1] = new bool[1]; double[,] myDoubles = new double[2, 2]; string[] myStrings = new string[3]; Console.WriteLine("myInts[0]: {0}, myInts[1]: {1}, myInts[2]: {2}", myInts[0], myInts[1], myInts[2]); myBools[0][0] = true; myBools[0][1] = false; myBools[1][0] = true; Console.WriteLine("myBools[0][0]: {0}, myBools[1][0]: {1}", myBools[0][0], myBools[1][0]); myDoubles[0, 0] = 3.147; myDoubles[0, 1] = 7.157; myDoubles[1, 1] = 2.117; myDoubles[1, 0] = 56.00138917; Console.WriteLine("myDoubles[0, 0]: {0}, myDoubles[1, 0]: {1}", myDoubles[0, 0], myDoubles[1, 0]); myStrings[0] = "Joe"; myStrings[1] = "Matt"; myStrings[2] = "Robert"; Console.WriteLine("myStrings[0]: {0}, myStrings[1]: {1}, myStrings[2]: {2}", myStrings[0], myStrings[1], myStrings[2]); } } And here's the output: myInts[0]: 5, myInts[1]: 10, myInts[2]: 15 myBools[0][0]: True, myBools[1][0]: True myDoubles[0, 0]: 3.147, myDoubles[1, 0]: 56.00138917 myStrings[0]: Joe, myStrings[1]: Matt, myStrings[2]: Robert Listing 2-4 shows different implementations of Arrays. The first example is the myInts Array. It is initialized at declaration time with explicit values. Next is a jagged array. It is essentially an array of arrays. We needed to use the new operator to instantiate the size of the primary array and then use the new operator again for each sub-array. The third example is a two dimensional array. Arrays can be multi-dimensional, with each dimension separated by a comma. it must also be instantiated with the new operator. Finally, we have the one dimensional array of string types. In each case, you can see that array elements are accessed by identifying the integer index for the item you wish to refer to. Arrays sizes can be any int type value. Their indexes begin at 0. Single-Dimensional Arrays The type of each array declared is given firstly by the type of basic elements it can hold, and secondly by the number of dimensions it has. Single-dimensional arrays have a single dimension (ie, are of rank 1). They are declared using square brackets, eg: int[] i = new int[100]; This line of code declares variable i to be an integer array of size 100. It contains space for 100 integer elements, ranging from i[0] to i[99]. To populate an array one can simply specify values for each element, as in the following code: int[] i = new int[2]; i[0] = 1; i[1] = 2; One can also run together the array declaration with the assignment of values to elements using int[] i = new int[] {1,2}; or the even shorter version of this: int[] i = {1,2}; By default, as we have seen, all arrays start with their lower bound as 0 (and we would recommend that you stick with this default). However, using the .NET framework's System.Array class it is possible to create and manipulate arrays with an alternative initial lower bound. The (read-only) Length property of an array holds the total number of its elements across all of its dimensions. As single-dimensional arrays have just one dimension, this property will hold the length of the single dimension. For instance, given the definition of array i above, i.Length is 2. Rectangular Arrays C# supports two types of multidimensional arrays: rectangular and jagged. A rectangular array is a single array with more than one dimension, with the dimensions' sizes fixed in the array's declaration. The following code creates a 2 by 3 multi-dimensional array: int[,] squareArray = new int[2,3]; As with single-dimensional arrays, rectangular arrays can be filled at the time they are declared. For instance, the code int[,] squareArray = {{1, 2, 3}, {4, 5, 6}}; creates a 2 by 3 array with the given values. It is, of course, important that the given values do fill out exactly a rectangular array. The System.Array class includes a number of methods for determining the size and bounds of arrays. These include the methods GetUpperBound(int i) and GetLowerBound(int i), which return, respectively, the upper and lower subscripts of dimension i of the array (note that i is zero based, so the first array is actually array 0). For instance, since the length of the second dimension of squareArray is 3, the expression "squareArray.GetLowerBound(1)" returns 0, and the expression "squareArray.GetUpperBound(1)" returns 2. System.Array also includes the method GetLength(int i), which returns the number of elements in the ith dimension (again, zero based). The following piece of code loops through squareArray and writes out the value of its elements for(int i = 0; i < squareArray.GetLength(0); i++) for (int j = 0; j < squareArray.GetLength(1); j++) Console.WriteLine(squareArray[i,j]); A foreach loop can also be used to access each of the elements of an array in turn, but using this construction one doesn't have the same control over the order in which the elements are accessed. ================================= 6. WINDOWS FORMS OR .NET WINAPPS: ================================= The key class in the System.Windows.Forms namespace is "Form", which is the base class for all top-level windows, including the application's main windows, view windows and any dialog boxes that you create. Example 1: ========== Suppose we create a windows application, which has a mainform, which shows a number of buttons which can open other forms. You actually do not need to "program" the majority of the code, as shown in this example. The "Forms Designer" inject these lines into your source code, as you add controls and set properties. // start code of the form using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace MSPress.CSharpCoreRef.Controls { /// /// Summary description for Form1. /// public class mainForm : System.Windows.Forms.Form { private System.Windows.Forms.Button buttonsButton; private System.Windows.Forms.Button listBoxesButton; private System.Windows.Forms.Button textBoxesButton; private System.Windows.Forms.Button combosButton; private System.Windows.Forms.Button scrollbarsButton; private System.Windows.Forms.Button containersButton; private System.Windows.Forms.Button webLookButton; /// /// Required designer variable. /// private System.ComponentModel.Container components = null; public mainForm() { // // Required for Windows Form Designer support // InitializeComponent(); // // TODO: Add any constructor code after InitializeComponent call // } /// /// Clean up any resources being used. /// protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.buttonsButton = new System.Windows.Forms.Button(); this.listBoxesButton = new System.Windows.Forms.Button(); this.textBoxesButton = new System.Windows.Forms.Button(); this.combosButton = new System.Windows.Forms.Button(); this.scrollbarsButton = new System.Windows.Forms.Button(); this.containersButton = new System.Windows.Forms.Button(); this.webLookButton = new System.Windows.Forms.Button(); this.SuspendLayout(); // // buttonsButton // this.buttonsButton.Location = new System.Drawing.Point(64, 32); this.buttonsButton.Name = "buttonsButton"; this.buttonsButton.TabIndex = 0; this.buttonsButton.Text = "Buttons"; this.buttonsButton.Click += new System.EventHandler(this.buttonsButton_Click); // // listBoxesButton // this.listBoxesButton.Location = new System.Drawing.Point(152, 32); this.listBoxesButton.Name = "listBoxesButton"; this.listBoxesButton.TabIndex = 1; this.listBoxesButton.Text = "List Boxes"; this.listBoxesButton.Click += new System.EventHandler(this.ListBoxesButton_Click); // // textBoxesButton // this.textBoxesButton.Location = new System.Drawing.Point(64, 64); this.textBoxesButton.Name = "textBoxesButton"; this.textBoxesButton.TabIndex = 2; this.textBoxesButton.Text = "Text Boxes"; this.textBoxesButton.Click += new System.EventHandler(this.TextBoxesButton_Click); // // combosButton // this.combosButton.Location = new System.Drawing.Point(64, 96); this.combosButton.Name = "combosButton"; this.combosButton.TabIndex = 4; this.combosButton.Text = "Combos"; this.combosButton.Click += new System.EventHandler(this.CombosButton_Click); // // scrollbarsButton // this.scrollbarsButton.Location = new System.Drawing.Point(152, 96); this.scrollbarsButton.Name = "scrollbarsButton"; this.scrollbarsButton.TabIndex = 5; this.scrollbarsButton.Text = "Scroll Bars"; this.scrollbarsButton.Click += new System.EventHandler(this.scrollbarsButton_Click); // // containersButton // this.containersButton.Location = new System.Drawing.Point(152, 64); this.containersButton.Name = "containersButton"; this.containersButton.TabIndex = 6; this.containersButton.Text = "Containers"; this.containersButton.Click += new System.EventHandler(this.containersButton_Click); // // webLookButton // this.webLookButton.Location = new System.Drawing.Point(104, 136); this.webLookButton.Name = "webLookButton"; this.webLookButton.TabIndex = 7; this.webLookButton.Text = "Web Look"; this.webLookButton.Click += new System.EventHandler(this.webLookButton_Click); // // mainForm // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(292, 198); this.Controls.AddRange(new System.Windows.Forms.Control[] { this.webLookButton, this.containersButton, this.scrollbarsButton, this.combosButton, this.textBoxesButton, this.listBoxesButton, this.buttonsButton}); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.Name = "mainForm"; this.Text = "Basic Controls Demo"; this.ResumeLayout(false); } #endregion /// /// The main entry point for the application. /// [STAThread] static void Main() { Application.Run(new mainForm()); } private void ListBoxesButton_Click(object sender, System.EventArgs e) { Form f = new ListBoxForm(); f.Show(); } private void CombosButton_Click(object sender, System.EventArgs e) { Form f = new ComobBoxForm(); f.ShowDialog(); } private void TextBoxesButton_Click(object sender, System.EventArgs e) { Form f = new TextBoxForm(); f.ShowDialog(); } private void scrollbarsButton_Click(object sender, System.EventArgs e) { Form f = new ScrollbarsForm(); f.ShowDialog(); } private void containersButton_Click(object sender, System.EventArgs e) { Form f = new ContainersForm(); f.ShowDialog(); } private void buttonsButton_Click(object sender, System.EventArgs e) { Form f = new ButtonForm(); f.Show(); } private void webLookButton_Click(object sender, System.EventArgs e) { Form f = new WebLookForm(); f.Show(); } } } // end code Let's take a look at some noticable points: - The Main() method in a Windows Forms application includes a call to Application.Run static void Main() { Application.Run(new mainForm()); } - Display a form: A modal form prevents access to any other portions of the application while the form is displayed. A modeless form allows other parts of the application be used. - To display a modeless form, us the Show method: Form addressForm=new AddressForm(); addressForm.Show(); To destroy the form, use the Close() method: addressForm.Close(); - To display a modal form, use the ShowDialog() method, as shown here: addressForm.ShowDialog(); ======================== CALL A STORED PROCEDURE: ======================== Example 1. ---------- using(MyDatabase db = new MyDatabase()) { // Call the GetUsersByZip stored procedure and return data in a DataTable object DataTable users = db.StoredProcedures.GetUsersByZipDataTable(77057); // You do not need to close the DB connection manually. // The using statement do it automatically. } or MyDatabase db = new MyDatabase(); try { // Call the GetUsersByZip stored procedure and return data in a DataTable object DataTable users = db.StoredProcedures.GetUsersByZipDataTable(77057); } finally { // Do not forget to close the DB connection db.Close(); } Example 2. ---------- Oracle is one of the most popular databases around. So, there is a good enough chance that you will end up working with Oracle and .NET. Stored Procedures is one of the ways that you can shift some of the business logic to the database server. Here , we will see how to execute a stored procedure which takes some input parameters. Here is the code for the Stored Procedure: CREATE OR REPLACE PROCEDURE updateBalance(accNo number, amount number) IS BEGIN UPDATE customer_account SET balance = balance + amount WHERE accountNo = accNo ; END; / Here our procedure is a very simple one which has only one update statement and which takes two input parameters 1. Account Number (AccNo) and 2. Amount (amount) You can run the above code in SQL*PLUS and the procedure is created. Of course, you should have the table Customer_Account with at least the required fields i.e 1. AccountNo which is a number and 2. Balance also a number. Let's see the how can we execute it using .NET. Here, we will be a using a simple console application to execute the stored procedure. The language used is C#. Here is the C# code (console.cs) // import the required assemblies using System; using System.Data; using System.Data.OleDb; class StoredProc { public static void Main() { string DBstr = "" ; // your Database connection string // create an instance of the connection object OleDbConnection oCn = new OleDbConnection(DBstr) ; //create an instance of the command object giving the procedure name OleDbCommand oCm = new OleDbCommand("updateBalance",oCn) ; // Define the command type u r executing as a Stored Procedure. oCm.CommandType = CommandType.StoredProcedure ; //Add the parameter "accNo" giving it's value and defining it as a Input parameter oCm.Parameters.Add("accNo",OleDbType.Integer,16); oCm.Parameters["accNo"].Value = 1 ; oCm.Parameters["accNo"].Direction = ParameterDirection.Input ; //Add the parameter "amount" giving it's value & defining it as Input parameter oCm.Parameters.Add("amount",OleDbType.Integer,16); oCm.Parameters["amount"].Value = 200 ; oCm.Parameters["amount"].Direction = ParameterDirection.Input ; // using the Try Catch Finally Block. try { // Open the connection oCn.Open(); // giving screen output Console.WriteLine("created connection") ; // execute the stored procedure oCm.ExecuteNonQuery() ; // get the confirmation on the screen Console.WriteLine("Procedure Completed") ; } catch(Exception ex) { // catch the error message and put it in the string "msg" string msg = ex.Message ; //show the error message on the screen Console.WriteLine(msg) ; } finally { // Destroy the command object oCm.Dispose() ; // Destroy the connection object oCn.Dispose() ; } } } You will have to compile this separately with the following command csc /r:System.Data.dll console.cs This creates console.exe which you can run at command prompt. Example 3: ---------- ==================== 7. ASP.NET PROGRAMS: ==================== -- END OF FILE