You are on page 1of 118

PROGRAMMING WITH C#

Disclaimer
The copyright of the content used in the courseware will remain with Principle Company.

TABLE OF CONTENTS
Sr.No. Chapter Name Page No.
3 15 38 52 68 90 92 104 113

1. .NET Framework Fundamentals 2. C# Language Fundamentals 3. Array , String and DateTime Structure 4. Class, Objects and Structure 5. OOPS Concepts ( Inheritance,Polymorphism,Interface) 6. Namespace 7. Collection Framework 8. Exception Handling 9. Assemblies

CHAPTER : 1

.NET FRAMEWORK FUNDAMENTALS


The .NET Framework is an integral Windows component that supports building and running the next generation of applications and XML Web services. The .NET Framework is designed to fulfil the following objectives: To provide a consistent object-oriented programming environment whether object code is stored and executed locally, executed locally but Internet-distributed, or executed remotely. To provide a code-execution environment that minimizes software deployment and versioning conflicts. To provide a code-execution environment that promotes safe execution of code, including code created by an unknown or semi-trusted third party. To provide a code-execution environment that eliminates the performance problems of scripted or interpreted environments. To make the developer experience consistent across widely varying types of applications, such as Windows-based applications and Web-based applications. To build all communication on industry standards to ensure that code based on the .NET Framework can integrate with any other code.

The .NET Framework has two main components: the common language runtime and the .NET Framework class library. 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 promote 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 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. The .NET Framework can be hosted by unmanaged components that load the common language runtime into their processes and initiate the execution of managed code, thereby creating a software environment that can exploit both managed and unmanaged features. The .NET Framework not only provides several runtime hosts, but also supports the development of third-party runtime hosts. For example, ASP.NET hosts the runtime to provide a scalable, server-side environment for managed code. ASP.NET works directly with the runtime to enable ASP.NET applications and XML Web services, both of which are discussed later in this topic.

Internet Explorer is an example of an unmanaged application that hosts the runtime (in the form of a MIME type extension). Using Internet Explorer to host the runtime enables you to embed managed components or Windows Forms controls in HTML documents. Hosting the runtime in this way makes managed mobile code (similar to Microsoft ActiveX controls) possible, but with significant improvements that only managed code can offer, such as semi-trusted execution and isolated file storage. The following illustration shows the relationship of the common language runtime and the class library to your applications and to the overall system. The illustration also shows how managed code operates within a larger architecture.

Components of .NET Framework


The following sections describe the main components and features of the .NET Framework in greater detail.

COMMON LANGUAGE RUNTIME (CLR)


The common language runtime manages memory, thread execution, code execution, code safety verification, compilation, and other system services. These features are intrinsic to the managed code that runs on the common language runtime. With regards to security, managed components are awarded varying degrees of trust, depending on a number of factors that include their origin (such as the Internet, enterprise network, or local computer). This means that a managed component might or might not be able to perform file-access operations, registry-access operations, or other sensitive functions, even if it is being used in the same active application. The runtime enforces code access security. For example, users can trust that an executable embedded in a Web page can play an animation on screen or sing a song, but cannot access their personal data, file system, or network. The security features of the runtime thus enable legitimate Internet-deployed software to be exceptionally featuring rich. The runtime also enforces code robustness by implementing a strict type-and-codeverification infrastructure called the common type system (CTS). The CTS ensures that all managed code is self-describing. The various Microsoft and third-party language compilers generate managed code that conforms to the CTS. This means that managed code can consume other managed types and instances, while strictly enforcing type fidelity and type safety. In addition, the managed environment of the runtime eliminates many common software issues. For example, the runtime automatically handles object layout and manages references to objects, releasing them when they are no longer being used. This automatic memory management resolves the two most common application errors, memory leaks and invalid memory references. The runtime also accelerates developer productivity. For example, programmers can write applications in their development language of choice, yet take full advantage of the runtime, the class library, and components written in other languages by other developers. Any compiler vendor who chooses to target the runtime can do so. Language compilers that target the .NET Framework make the features of the .NET Framework available to existing code written in that language, greatly easing the migration process for existing applications.
4

While the runtime is designed for the software of the future, it also supports software of today and yesterday. Interoperability between managed and unmanaged code enables developers to continue to use necessary COM components and DLLs. 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. Finally, the runtime can be hosted by high-performance, server-side applications, such as Microsoft SQL Server and Internet Information Services (IIS). This infrastructure enables you to use managed code to write your business logic, while still enjoying the superior performance of the industry's best enterprise servers that support runtime hosting.

MSIL
When compiling to managed code, the compiler translates your source code into Microsoft intermediate language (MSIL), which is a CPU-independent set of instructions that can be efficiently converted to native code. MSIL includes instructions for loading, storing, initializing, and calling methods on objects, as well as instructions for arithmetic and logical operations, control flow, direct memory access, exception handling, and other operations. Before code can be run, MSIL must be converted to CPU-specific code, usually by a just-intime (JIT) compiler. Because the common language runtime supplies one or more JIT compilers for each computer architecture it supports, the same set of MSIL can be JITcompiled and run on any supported architecture. When a compiler produces MSIL, it also produces metadata. Metadata describes the types in your code, including the definition of each type, the signatures of each type's members, the members that your code references, and other data that the runtime uses at execution time. The MSIL and metadata are contained in a portable executable (PE) file that is based on and extends the published Microsoft PE and common object file format (COFF) used historically for executable content. This file format, which accommodates MSIL or native code as well as metadata, enables the operating system to recognize common language runtime images. The presence of metadata in the file along with the MSIL enables your code to describe itself, which means that there is no need for type libraries or Interface Definition Language (IDL). The runtime locates and extracts the metadata from the file as needed during execution.

Just In Time Compilers (JIT Compiler)


When our IL compiled code needs to be executed, the CLR invokes the JIT compiler, which compile the IL code to native executable code (.exe or .dll) that is designed for the specific machine and OS. JITers in many ways are different from traditional compilers as they compile the IL to native code only when desired; e.g., when a function is called, the IL of the function's body is converted to native code just in time. So, the part of code that is not used by that particular run is never converted to native code. If some IL code is converted to native code, then the next time it's needed, the CLR reuses the same (already compiled) copy without re-compiling. So, if a program runs for some time (assuming that all or most of the functions get called), then it won't have any just-in-time performance penalty.
5

As JITers are aware of the specific processor and OS at runtime, they can optimize the code extremely efficiently resulting in very robust applications. Also, since a JIT compiler knows the exact current state of executable code, they can also optimize the code by in-lining small function calls (like replacing body of small function when its called in a loop, saving the function call time). Although Microsoft stated that C# and .Net are not competing with languages like C++ in efficiency and speed of execution, JITers can make your code even faster than C++ code in some cases when the program is run over an extended period of time (like web-servers).

.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. Windows GUI applications (Windows Forms). Windows Presentation Foundation (WPF) applications. ASP.NET applications. Web services. Windows services. Service-oriented applications using Windows Communication Foundation (WCF). Workflow-enabled applications using Windows Workflow Foundation (WF).

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.

CLS
Earlier, we used the term '.Net Compliant Language' and stated that all the .Net compliant languages can make use of CLR and FCL. But what makes a language a '.Net compliant' language? The answer is the Common Language Specification (CLS). Microsoft has released a small set of specifications that each language should meet to qualify as a .Net Compliant Language. As IL is a very rich language, it is not necessary for a language to implement all the IL functionality; rather, it merely needs to meet a small subset of CLS to qualify as a .Net compliant language. This is the reason why so many languages (procedural
6

and OO) are now running under the .Net umbrella. CLS basically addresses language design issues and lays down certain standards. For instance, there shouldn't be any global function declarations, no pointers, no multiple inheritance and things like that. The important point to note here is that if you keep your code within the CLS boundary, your code is guaranteed to be usable in any other .Net language. CTS The common type system defines how types are declared, used, and managed in the runtime, and is also an important part of the runtime's support for cross-language integration. The common type system performs the following functions: Establishes a framework that helps enable cross-language integration, type safety, and high performance code execution. Provides an object-oriented model that supports the complete implementation of many programming languages. Defines rules that languages must follow, which helps ensure that objects written in different languages can interact with each other.

Garbage Collection (GC) CLR also contains the Garbage Collector (GC), which runs in a low-priority thread and checks for un-referenced, dynamically allocated memory space. If it finds some data that is no longer referenced by any variable/reference, it re-claims it and returns it to the OS. The presence of a standard Garbage Collector frees the programmer from keeping track of dangling data. Ask any C++ programmer how big a relief it is! The Garbage collection is very important technique in the .Net framework to free the unused managed code objects in the memory and free the space to the process. I will explain about the basics of the Garbage collection in this article.

Garbage Collection in .Net framework The garbage collection (GC) is new feature in Microsoft .net framework. When we have a class that represents an object in the runtime that allocates a memory space in the heap memory. All the behavior of that objects can be done in the allotted memory in the heap. Once the activities related to that object is get finished then it will be there as unused space in the memory. The earlier releases of Microsoft products have used a method like once the process of that object get finished then it will be cleared from the memory. For instance Visual Basic, An object get finishes that work then there we have to define a "nothing" to that object. So, it clears the memory space to the processors.Microsoft was planning to introduce a method that should automate the cleaning of unused memory space in the heap after the life time of that object. Eventually they have introduced a new technique "Garbage collection". It is very important part in the .Net framework. Now it handles this object clear in the memory implicitly. It overcomes the existing explicit unused memory space clearance.
7

Garbage Collection
The heap memory is divided into number of generations. Normally it is three generations. The Generation 0 is for short live objects, Generation 1 is for medium live objects which are moved from Generation 0. Generation 3 is mostly stable objects. When an object is created then it will allocate the memory space which will be higher. It will be in the Generation 0 and the memory allocation will be continuous without any space between the generations of garbage collectors.

How it works Implicit Garbage Collection should be handled by the .Net framework. When object is created then it will be placed in the Generation 0. The garbage collection uses an algorithm which checks the objects in the generation, the objects life time get over then it will be removed from the memory. The two kinds of objects. One is Live Objects and Dead Objects. The Garbage collection algorithm collects all unused objects that are dead objects in the generation. If the live objects running for long time then based on that life time it will be moved to next generation.The object cleaning in the generation will not take place exactly after the life time over of the particular objects. It takes own time to implement the sweeping algorithm to free the spaces to the process.

ASSEMBLY 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. CLI CONCEPT Common Language Infrastructure From Wikipedia, the free encyclopediaJump to: navigation, search Not to be confused with Command-line Interface.The Common Language Infrastructure (CLI) is an open specification developed by Microsoft and standardized by ISO and ECMA[2] that describes the executable code and runtime environment that form the core of the Microsoft .NET Framework and the free and open source implementations Mono and Portable.NET. The specification defines an environment that allows multiple high-level languages to be used on different computer platforms without being rewritten for specific architectures. Overview Visual overview of the Common Language Infrastructure (CLI)Among other things, the CLI specification describes the following four aspects: The Common Type System (CTS) A set of data types and operations that are shared by all CTS-compliant programming languages. Metadata

Information about program structure is language-agnostic, so that it can be referenced between languages and tools, making it easy to work with code written in a language you are not using. Common Language Specification (CLS) A set of base rules to which any language targeting the CLI should conform in order to interoperate with other CLS-compliant languages. The CLS rules define a subset of the Common Type System. Virtual Execution System (VES) The VES loads and executes CLI-compatible programs, using the metadata to combine separately generated pieces of code at runtime. All compatible languages compile to Common Intermediate Language (CIL), which is an intermediate language that is abstracted from the platform hardware. When the code is executed, the platform-specific VES will compile the CIL to the machine language according to the specific hardware and operating system. Standardization and licensingIn August 2000, Microsoft, Hewlett-Packard, Intel, and others worked to standardize CLI. By December 2001, it was ratified by the ECMA, with ISO standardization following in April 2003. Microsoft and its partners hold patents for CLI. ECMA and ISO require that all patents essential to implementation be made available under "reasonable and non-discriminatory (RAND) terms", but interpretation[who?] of this has led to much controversy, particularly in the case of Mono[citation needed]. As of July 2009.Microsoft applied C# and CLI under the Community Promise, so anyone can safely implement those standards without fearing a patent lawsuit. The Common Language Infrastructure currently has no built-in support for Dynamically typed languages because the existing Common Intermediate Language is statically typed The Dynamic Language Runtime is an ongoing effort to bring this support to the CLR. Implementations of CLI concept The .NET Framework is built on the Common Language Runtime, Microsoft's commercial implementation of the CLI for desktop and server systems, and also encompasses a large collection of programming frameworks and libraries. Shared Source Common Language Infrastructure is a reference implementation of the CLI available from Microsoft, under the Shared source licensing program. .NET Compact Framework is Microsoft's commercial implementation of the CLI for portable devices and Xbox 360. Microsoft Silverlight, an implementation for use in web browsers - for Microsoft Windows and Mac OS X.
9

Mono development platform is an open source implementation of CLI and accompanying technologies, sponsored by Xamarin. Portable.NET, part of the dotGNU project, is a Free Software implementation of ECMA-335 by Free Software Foundation. VMKit part of Low Level Virtual Machine toolkit as of version 2.3. Implements very incomplete and alpha stage support of a Virtual Execution System. It is based on DotGNU, Portable.NET corelib and class libraries.

ADVANTAGE OF .NET FRAMEWORK .NET Framework Advantages The .NET Framework offers a number of advantages to developers. The following paragraphs describe them in detail. Consistent Programming Model Different programming languages have different approaches for doing a task. For example, accessing data with a VB 6.0 application and a VC++ application is totally different. When using different programming languages to do a task, a disparity exists among the approach developers use to perform the task. The difference in techniques comes from how different languages interact with the underlying system that applications rely on. With .NET, for example, accessing data with a VB .NET and a C# .NET looks very similar apart from slight syntactical differences. Both the programs need to import the System.Data namespace, both the programs establish a connection with the database and both the programs run a query and display the data on a data grid. The VB 6.0 and VC++ example mentioned in the first paragraph explains that there is more than one way to do a particular task within the same language. The .NET example explains that there's a unified means of accomplishing the same task by using the .NET Class Library, a key component of the .NET Framework. The functionality that the .NET Class Library provides is available to all .NET languages resulting in a consistent object model regardless of the programming language the developer uses. Direct Support for Security Developing an application that resides on a local machine and uses local resources is easy. In this scenario, security isn't an issue as all the resources are available and accessed locally. Consider an application that accesses data on a remote machine or has to perform a privileged task on behalf of a nonprivileged user. In this scenario security is much more important as the application is accessing data from a remote machine. With .NET, the Framework enables the developer and the system administrator to specify method level security. It uses industry-standard protocols such as TCP/IP, XML, SOAP and
10

HTTP to facilitate distributed application communications. This makes distributed computing more secure because .NET developers cooperate with network security devices instead of working around their security limitations. Simplified Development Efforts Let's take a look at this with Web applications. With classic ASP, when a developer needs to present data from a database in a Web page, he is required to write the application logic (code) and presentation logic (design) in the same file. He was required to mix the ASP code with the HTML code to get the desired result. ASP.NET and the .NET Framework simplify development by separating the application logic and presentation logic making it easier to maintain the code. You write the design code (presentation logic) and the actual code (application logic) separately eliminating the need to mix HTML code with ASP code. ASP.NET can also handle the details of maintaining the state of the controls, such as contents in a textbox, between calls to the same ASP.NET page. Another advantage of creating applications is debugging. Visual Studio .NET and other third party providers provide several debugging tools that simplify application development. The .NET Framework simplifies debugging with support for Runtime diagnostics. Runtime diagnostics helps you to track down bugs and also helps you to determine how well an application performs. The .NET Framework provides three types of Runtime diagnostics: Event Logging, Performance Counters and Tracing. Easy Application Deployment and Maintenance The .NET Framework makes it easy to deploy applications. In the most common form, to install an application, all you need to do is copy the application along with the components it requires into a directory on the target computer. The .NET Framework handles the details of locating and loading the components an application needs, even if several versions of the same application exist on the target computer. The .NET Framework ensures that all the components the application depends on are available on the computer before the application begins to execute. The following console program is the C# version of the traditional "Hello World!" program, which displays the string Hello World!. using System; // A "Hello World!" program in C# namespace HelloWorld { class Hello { static void Main() { System.Console.WriteLine("Hello World!"); } }
11

} it requires into a directory on the target computer. The .NET Framework handles the details of locating and loading the components an application needs, even if several versions of the same application exist on the target computer. The .NET Framework ensures that all the components the application depends on are available on the computer before the application begins to execute. Let us now look at the important parts of this program in turn. Comments The first line contains a comment: // A "Hello World!" program in C# The characters // convert the rest of the line to a comment. You can also comment a block of text by placing it between the characters /* and */, for example: /* A "Hello World!" program in C#. This program displays the string "Hello World!" on the screen. */ The Main Method The C# program must contain a Main method, in which control starts and ends. The Main method is where you create objects and execute other methods.The Main method is a static method that resides inside a class or a struct. In the previous "Hello World!" example, it resides in a class called Hello. Declare the Main method in one of the following ways: It can return void: static void Main() { //... } It can also return an int: static int Main() { //... return 0; } With both of the return types, it can take arguments: static void Main(string[] args) { //... } -orstatic int Main(string[] args) { //...
12

return 0; } The parameter of the Main method is a string array that represents the command-line arguments used to invoke the program. Notice that, unlike C++, this array does not include the name of the executable (exe) file. Input and Output C# programs generally use the input/output services provided by the run-time library of the .NET Framework. The statement, System.Console.WriteLine("Hello World!"); uses the WriteLine method, one of the output methods of the Console class in the run-time library. It displays its string parameter on the standard output stream followed by a new line. Other Console methods are used for different input and output operations. If you include the using System; directive at the beginning of the program, you can directly use the System classes and methods without fully qualifying them. For example, you can call Console.WriteLine instead, without specifying System.Console.Writeline: using System; Console.WriteLine("Hello World!"); Compilation and Execution You can compile the "Hello World!" program either by creating a project in the Visual Studio IDE, or by using the command line. Use the Visual Studio Command Prompt or invoke vsvars32.bat to put the Visual C# tool set on the path in your command prompt. To compile the program from the command line: Create the source file using any text editor and save it using a name such as Hello.cs. C# source code files use the extension .cs. To invoke the compiler, enter the command: csc Hello.cs If your program does not contain any compilation errors, a Hello.exe file will be created. To run the program, enter the command: Hello

13

CHAPTER : 2

THE C# LANGUAGE FUNDAMENTALS


C# (pronounced C-Sharp) is no doubt the language of choice in the .Net environment. It is a whole new language free of the backward compatibility curse with a whole bunch of new, exciting and promising features. It is an Object Oriented Programming language and has at its core, many similarities to Java, C++ and VB. In fact, C# combines the power and efficiency of C++, the simple and clean OO design of Java and the language simplification of Visual Basic. Like Java, C# also does not allow multiple inheritance or the use of pointers (in safe/managed code), but does provide garbage memory collection at runtime, type and memory access checking. However, contrary to JAVA, C# maintains the unique useful operations of C++ like operator overloading, enumerations, pre-processor directives, pointers (in unmanaged/un-safe code), function pointers (in the form of delegates) and promises to have template support in the next versions. Like VB, it also supports the concepts of properties (context sensitive fields). In addition to this, C# comes up with some new and exciting features such as reflections, attributes, marshalling, remoting, threads, streams, data access with ADO.Net and more The class Keyword All of our C# programs contain at least one class. The Main() method resides in one of these classes. Classes are a combination of data (fields) and functions (methods) that can be performed on this data in order to achieve the solution to our problem. We will see the concept of class in more detail in the coming days. Classes in C# are defined using the class keyword followed by the name of class. The Main() Method In the next line we defined the Main() method of our program: static void Main(string[] args) This is the standard signature of the Main method in C#. The Main method is the entry point of our program, i.e., our C# program starts its execution from the first line of Main method and terminates with the termination of Main method. The Main method is designated as static as it will be called by the Common Language Runtime (CLR)without making any object of our HelloWorld Class (which is the definition of static methods, fields and properties). The method is also declared void as it does not return anything. Main is the (standard) name of this method, while string [] args is the list of parameters that can be passed to main while executing the program from command line. We will see this later. One interesting point here is that it is legitimate to have multiple Main() methods in C# program. But, you have to explicitly identify which Main method is the entry point at the runtime. C++ and Java Programmers, take note that Main starts with capital 'M' and the return type is void. Printing on the Console Our next line prints Hello World on the Console screen: Console.WriteLine("Hello World");

14

Here we called WriteLine(), a static method of the Console class defined in the System namespace. This method takes a string (enclosed in double quotes) as its parameter and prints it on the Console window. C#, like other Object Oriented languages, uses the dot (.) operator to access the member variables (fields) and methods of a class. Also, braces () are used to identify methods in the code and string literals are enclosed in double quotation marks ("). Lastly, each statement in C# (like C, C++ and Java) ends with a semicolon (;), also called the statement terminator. Comments Comments are the programmer's text to explain the code, are ignored by the compiler and are not included in the final executable code. C# uses syntax for comments that is similar to Java and C++. The text following double slash marks (// any comment) are line comments. The comment ends with the end of the line: // This is my main method of program static void Main() { ... } C# also supports the comment block. In this case, the whole block is ignored by the compiler. The start of the block is declared by slash-asterisk (/*) and ends with asterisk-slash mark (*/): static void Main() { /* These lines of text will be ignored by the compiler */ ........... ........ }

C# introduces another kind of comment called 'documentation comments'. C# can use these to generate the documentation for your classes and program. These are line comments and start with triple slash mark (///): /// These are documentation comments We will discuss these in detail in coming issues. Important points to remember Your C# executable program resides in some class. The entry point to program is the static method Main() with void return type C# is a case sensitive language so void and Void are different Whitespaces (enter, tab, space) are ignored by the compiler between the code. Hence, the following is also a valid declaration of the Main() method although it is not recommended:

15

static void Main ( ) { ... } You DON'T need to save your program with same file name as of your class containing Main() method. There can be multiple Main() methods in your program. The boundaries of namespace, class and method are defined by opening and closing curly brackets { }. A namespace is only logical collection of classes with no physical mapping on disk (unlike Java). The using keyword is used to inform compiler where to search for the definition of classes (namespaces) that you are about to use in your C# program. The three types of comments exist in C#; line, block and documentation. These are ignored by thecompiler and are used only to enhance the readability and understandability of program for the developers. Enclosing your class in some namespace is optional. You can write program where your class is not enclosed by any namespace. It is not mandatory that Main Method of program takes 'string [] args' as parameter. It is perfectly valid to write Main method as: static void Main() { ... ... } A more interactive Hello World Application Up. Until now, we have seen a very static hello world application that greets the whole world when it is executed. Lets now make a more interactive hello world that greets the current user of it. This program will ask the user their name and will greet using his/her name, like 'Hello Chintu' when a user named 'Chintu' runs it. Lets see the code first: static void Main(string[] args) { Console.Write("Please enter your name: "); string name = Console.ReadLine(); Console.WriteLine ("Hello {0}, Good Luck in C#", name); } Author's Note: In the above code, we haven't shown the complete program but only the Main Method to save space. We will follow this strategy in the rest of the course when appropriate. Discussing a more interactive Hello World Application In the first line, we have used another method, Write(), of the Console class. This is similar to the WriteLine() method, discussed in the previous program, but does not change the line after printing the string on the console.
16

In the second line, we declared a variable of the type string and called it 'name'. Then, we took a line of input from the user through the ReadLine() method of the Console class and stored the result in the 'name' variable. The variables are placeholders in memory for storing data temporarily during the execution of program. Variables can hold different types of data depending on their data-type, e.g., int variables can store integers while string variables can store a string (series) of characters. The ReadLine() method of the Console class (contrary to WriteLine()) reads a line of input given at the Console Window. It returns this input as string data, which we stored in our string variable 'name'.

A string is implicit data-type in C# contrary to other languages. It starts with small 's'. In the third line, we printed the name given by user in line 2, along with some greeting text using the WriteLine() method of the Console class. Here we used the substitution parameter {0} to state where in the line the data in the variable 'name' should be written when the WriteLine() method is called. Console.WriteLine("Hello {0}, Good Luck in C#", name); When the compiler finds a substitution parameter, {n}, it replaces it with the (n+1)th variable following the string. The string is delimited with double quotation marks and each parameter is separated by a comma. Hence, in our case when the compiler finds {0}, it replaces it with (0+1)th, i.e., the 1st variable ('name') following the string. So at run-time, the CLR will read it as: Console.WriteLine("Hello Chintu, Good Luck in C#"); if the value of 'name' is Chintu at run-time. Alternatively, it can also be written as: Console.WriteLine("Hello " + name + ", Good Luck in C#"); removing the substitution parameter. Here we concatenate (add) the strings together to form a message. (The first approach is similar to C's printf() function while the second is similar to Java's System.out.println() method) When we compile and run this program the output will be: Please enter your name: Chintu Hello Chintu, Good Luck in C#

C# programs can consist of one or more files. Each file can contain zero or more namespaces. A namespace can contain types such as classes, structs, interfaces, enumerations, and delegates, in addition to other namespaces. The following is the skeleton of a C# program that contains all of these elements. GENERAL STRUCTURE OF C# PROGRAM // A skeleton of a C# program using System; namespace YourNamespace
17

{ class YourClass { } struct YourStruct { } interface IYourInterface { } delegate int YourDelegate(); enum YourEnum { } namespace YourNestedNamespace { struct YourStruct { } } class YourMainClass { static void Main(string[] args) { //Your program starts here... } } }

Data Types The C# typing system contains the following categories: Value Types Reference Types Nullable Types Pointer Types

Variables of the value types store data, while those of the reference types store references to the actual data. Reference types are also referred to as objects. Pointer types can be used only in unsafe mode.

18

It is possible to convert a value type to a reference type, and back again to value types, by using boxing and unboxing. With the exception of a boxed value types, you cannot convert a reference type to a value type. Value types are also nullable, which means they can store an addition non-value state. VALUE TYPES Variable that are based on value types directly contain a values. Assigning one value type variable to another copies the contained value. This differs from the assignment of reference type variables, which copies a reference to the object but not the object itself. All value types are derived implicitly from the System.ValueType. Unlike reference types, it is not possible to derive a new type from a value type. However, like reference types, structs can implement interfaces. Unlike reference types, it is not possible for a value type to contain the null value. However, the nullable types feature does allow values types to be assigned to null. Each value type has an implicit default constructor that initializes the default value of that type. For information on default values of value types, see Default Values Table.

Value types are passed to methods by passing an exact copy while Reference types are passed to methods by passing only their reference (handle). Implicit data types are defined in the language core by the language vendor, while explicit data types are types that are made by using or composing implicit data types. As we saw in the first issue, implicit data types in .Net compliant languages are mapped to types in the Common Type System (CTS) and CLS (Common Language Specification). Hence, each implicit data type in C# has its corresponding .Net type.

The implicit data types in C# are: The value types consist of two main categories: Structs Enumerations Structs fall into these categories: Numeric types Integral types Floating-point types decimal bool User defined structs. Integral types The following table shows the sizes and ranges of the integral types, which constitute a subset of simple types.
19

Type sbyte byte char

Range -128 to 127 0 to 255 U+0000 to U+ffff

Size Signed 8-bit integer Unsigned 8-bit integer Unicode character 16-bit

short

-32,768 to 32,767

Signed 16-bit integer Unsigned 16-bit integer Signed 32-bit integer Unsigned 32-bit integer to Signed 64-bit integer

ushort 0 to 65,535 int uint long -2,147,483,648 to 2,147,483,647 0 to 4,294,967,295 -9,223,372,036,854,775,808 9,223,372,036,854,775,807

ulong 0 to 18,446,744,073,709,551,615

Unsigned 64-bit integer

Floating Point Types If the value represented by an integer literal exceeds the range of ulong, a compilation error will occur. The following table shows the precision and approximate ranges for the floating-point types. Type float double Approximate range 1.5e45 to 3.4e38 5.0e324 to 1.7e308 Precision 7 digits 15-16 digits

Decimal Types The decimal keyword denotes a 128-bit data type. Compared to floating-point types, the decimal type has a greater precision and a smaller range, which makes it suitable for

20

financial and monetary calculations. The approximate range and precision for the decimal type are shown in the following table. Type Approximate Range Precision .NET Framework type

Decimal 1.0 10e28 to 7.9 10e28

28-29 significant digits System.Decimal

REFERENCE TYPES Variables of reference types, referred to as objects, store references to the actual data. This section introduces the following keywords used to declare reference types: class interface delegate

This section also introduces the following built-in reference types: object string

NULLABLE TYPES Nullable types are instances of the System..::.Nullable<(Of <(T>)>) struct. A nullable type can represent the correct range of values for its underlying value type, plus an additional null value. For example, a Nullable<Int32>, pronounced "Nullable of Int32," can be assigned any value from -2147483648 to 2147483647, or it can be assigned the null value. A Nullable<bool> can be assigned the values truefalse, or null. The ability to assign null to numeric and Boolean types is especially useful when you are dealing with databases and other data types that contain elements that may not be assigned a value. For example, a Boolean field in a database can store the values true or false, or it may be undefined. class NullableExample { static void Main() { int? num = null; if (num.HasValue == true) { System.Console.WriteLine("num = " + num.Value); } else { System.Console.WriteLine("num = Null"); } //y is set to zero int y = num.GetValueOrDefault(); // num.Value throws an InvalidOperationException if num.HasValue is false try
21

{ y = num.Value; } catch (System.InvalidOperationException e) { System.Console.WriteLine(e.Message); } } } The example will display the output: num = Null Nullable object must have a value. BOXING and UNBOXING Boxing and unboxing enable value types to be treated as objects. Boxing a value type packages it inside an instance of the Object reference type. This allows the value type to be stored on the garbage collected heap. Unboxing extracts the value type from the object. In this example, the integer variable i is boxed and assigned to object o. int i = 123; object o = (object) i; // boxing The object o can then be unboxed and assigned to integer variable i: o = 123; i = (int) o; // unboxing

Naming Conventions for variables and methods Microsoft suggests using Camel Notation (first letter in lowercase) for variables and Pascal Notation (first letter in uppercase) for methods. Each word after the first word in the name of both variables and methods should start with a capital letter. For example, variable names following Camel notation could be: salary totalSalary myMathsMarks isPaid Some typical names of method following Pascal Notation are GetTotal() WriteLine() Start() LastIndexOf()

Although it is not mandatory to follow this convention, it is highly recommended that you strictly follow the convention. Microsoft no longer supports Hungarian notation, like using iMarks for integer variable. Also, using the underscore _ in identifiers is not encouraged.

22

OPERATORS
In C#, an operator is a term or a symbol that takes one or more expressions, called operands, as input and returns a value. Operators that take one operand, such as the increment operator (++) or new, are called unary operators. Operators that take two operands, such as arithmetic operators (+,-,*,/) are called binary operators. One operator, the conditional operator (?:), takes three operands and is the sole tertiary operator in C#. Types of Operators Arithmetic Relational Logical Assignment Increment / decrement Conditional Bitwise Special An operand can be a valid expression of any size, composed of any number of other operations. Operators in an expression are evaluated in a specific order known as operator precedence. The following table divides the operators into categories based on the type of operation they perform. The categories are listed in order of precedence. Primary Unary Arithmetic Multiplicative Arithmetic Additive Shift Relational and type testing Equality Logical, in order of precedence Conditional, precedence Assignment in order x.y, f(x), a[x], x++, x--, new, typeof, checked, unchecked +, -, !, ~, ++x, --x, (T)x *, / , % +, <<, >> <, >, <=, >=, is, as ==, != &, ^, | of &&, ||, ?: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, =>

When two operators with the same precedence are present in an expression, they are evaluated based on associativity. Left-associative operators are evaluated in order from left to right. For example, x * y / z is evaluated as (x * y) / z. Right-associative operators are evaluated in order from right to left. The assignment operators and the tertiary operator (?:) are right-associative. All other binary operators are left-associative. However, C# standard
23

does not specify when, in an expression, the "set" portion of an increment instruction is executed. For example, The output of the following example code is 6: int num1 = 5; num1++; System.Console.WriteLine(num1);

However, the output of the following example code is undefined: int num2 = 5; num2 = num2++; //not recommended System.Console.WriteLine(num2);

Therefore, the latter example is not recommended. Parentheses can be used to surround an expression and force that expression to be evaluated before any others. For example, 2 + 3 * 2 would normally become 8. This is because multiplicative operators take precedence over additive operators. Writing the expression as (2 + 3 ) * 2 results in 10, because it indicates to the C# compiler that the addition operator (+) must be evaluated before the multiplication operator (*). The equals (=) operator is used to assign a value to an object. Like we have seen bool isPaid = false;

assigns the value 'false' to the isPaid variable of Boolean type. The left hand and right hand side of the equal or any other assignment operator must be compatible, otherwise the compiler will complain about a syntax error. Sometimes casting is used for type conversion, e.g., to convert and store a value in a variable of type double to a variable of type int, we need to apply an integer cast.
double doubleValue = 4.67; // intValue will be equal to 4 int intValue = (int) doubleValue;

Of course, when casting there is always a danger of some loss of precision; in the case above, we only got the 4 ofthe original 4.67. Sometimes, the casting may result in strange values:
int intValue = 32800; short shortValue = (short) intValue; 24

// shortValue would be equal to -32736 Variables of type short can only take values ranging from -32768 to 32767, so the cast above can not assign 32800 to shortValue. Hence shortValue took the last 16 bits (as a short consists of 16 bits) of the integer 32800, which gives the value -32736 (since bit 16, which represents the value 32768 in an int, now represents -32768). If you try to cast incompatible types like:
bool isPaid = false; int intValue = (int) isPaid;

It won't get compiled and the compiler will generate a syntax error.

PROGRAMMING CONSTRUCTS Statement A statement is a procedural building-block from which all C# programs are constructed. A statement can declare a local variable or constant, call a method, create an object, or assign a value to a variable, property, or field. A control statement can create a loop, such as a for loop, or make a decision and branch to a new block of code, such as an if or switch statement. Statements are usually terminated by a semicolon. Statement Types (C# Reference). Statements are program instructions. Except as described, statements are executed in sequence. C# has the following categories of statements. Category Selection statements Iteration statements Jump statements Exception handling statements Checked and unchecked fixed Statement lock Statement C# keywords if, else, switch, case do, for, foreach, in, while break, continue, default, goto, return, yield throw, try-catch, try-finally, try-catch-finally checked, unchecked Fixed Lock

A series of statements surrounded by curly braces form a block of code. A method body is one example of a code block. Code blocks often follow a control statement. Variables or constants declared within a code block are only available to statements within the same code
25

block. For example, the following code shows a method block and a code block following a control statement:

The operators &, | and ^ are rarely used in usual programming practice. The NOT operator is used to negate a Boolean or bitwise expression like: bool b = false; bool bb = !b; // bb would be true Logical Operators && and || are used to combine comparisons like int i=6, j=12; bool firstVar = i>3 && j<10; // firstVar would be false bool secondVar = i>3 || j<10; // secondVar would be true In the first comparison: i>3 && j<10 will result in true only if both the conditions i>3 and j<10 result in true. In the second comparison: i>3 || j<10 will result in true if any of the conditions i>3 and j<10 result in true. You can, of course, use both && and || in single statement like: bool firstVar = (i>3 && j<10) || (i<7 && j>10) // firstVar would be true In the above statement we used parenthesis to group our conditional expressions and to avoid any ambiguity.

You can use & and | operators in place of && and || but for combining conditional expressions, && and || are more efficient because they use "short circuit evaluation". For example, if in the expression (i>3 && j<10), i>3 evaluates to false, the second expression j<10 won't be checked and false will be returned (when using AND, if one of the participant operands is false, the whole operation will result in false). Hence, one should be very careful when using assignment expressions with && and || operators. The & and | operators don't do short circuit evaluation and do execute all the comparisons before returning the result
26

An expression is a fragment of code that can be evaluated to a single value, object, method, or namespace. Expressions can contain a literal value, a method invocation, an operator and its operands, or a simple name. Simple names can be the name of a variable, type member, method parameter, namespace or type. Expressions can use operators that in turn use other expressions as parameters, or method calls whose parameters are in turn other method calls, so expressions can range from simple to very complex. Literals and Simple Names The two simplest types of expressions are literals and simple names. A literal is a constant value that has no name. For example, in the following code example, both 5 and "Hello World" are literal values: int i = 5; string s = "Hello World"; In the example above, both i and s are simple names identifying local variables. When those variables are used in an expression, the value of the variable is retrieved and used for the expression. For example, in the following code example, when DoWork is called, the method receives the value 5 by default and is not able to access the variable var: int var = 5; DoWork(var); Invocation Expressions In the following code example, the call to DoWork is another kind of expression, called an invocation expression. DoWork(var); Specifically, calling a method is a method invocation expression. A method invocation requires the name of the method, either as a name as in the previous example, or as the result of another expression, followed by parenthesis and any method parameters. A delegate invocation uses the name of a delegate and method parameters in parenthesis. Method invocations and delegate invocations evaluate to the return value of the method, if the method returns a value. Methods that return void cannot be used in place of a value in an expression. Query Expressions The same rules for expressions in general apply to query expressions. Lambda Expressions Lambda expressions represent "inline methods" that have no name but can have input parameters and multiple statements. They are used extensively in LINQ to pass arguments to methods. Lambda expressions are compiled to either delegates or expression trees depending on the context in which they are used. For more information, Expression Trees
27

Expression trees enable expressions to be represented as data structures. They are used extensively by LINQ providers to translate query expressions into code that is meaningful in some other context, such as a SQL database. For more information, see Expression Trees. Remarks Whenever a variable, object property, or object indexer access is identified from an expression, the value of that item is used as the value of the expression. An expression can be placed anywhere in C# where a value or object is required, as long as the expression ultimately evaluates to the required type.

LOOPS Another essential technique when writing software is looping - the ability to repeat a block of code X times. In C#, they come in 4 different variants, and we will have a look at each one of them. The while loop The while loop is probably the most simple one, so we will start with that. The while loop simply executes a block of code as long as the condition you give it is true. A small example, and then some more explanation: using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { int number = 0; while(number < 5) { Console.WriteLine(number); number = number + 1; } Console.ReadLine(); } } } Try running the code. You will get a nice listing of numbers, from 0 to 4. The number is first defined as 0, and each time the code in the loop is executed, it's incremented by one. But why does it only get to 4, when the code says 5? For the condition to return true, the number has to be less than 5, which in this case means that the code which outputs the number is not reached once the number is equal to 5. This is because the condition of the while loop is evaluated before it enters the code block. The do loop The opposite is true for the do loop, which works like the while loop in other aspects through. The do loop evaluates the condition after the loop has executed, which makes sure that the code block is always executed at least once.
28

do { Console.WriteLine(number); number = number + 1; } while(number < 5); The output is the same though - once the number is more than 5, the loop is exited. The for loop The for loop is a bit different. It's preferred when you know how many iterations you want, either because you know the exact amount of iterations, or because you have a variable containing the amount. Here is an example on the for loop. using System;
namespace ConsoleApplication1 { class Program { static void Main(string[] args) { int number = 5; for(int i = 0; i < number; i++) Console.WriteLine(i); Console.ReadLine(); } } }

This produces the exact same output, but as you can see, the for loop is a bit more compact. It consists of 3 parts - we initialize a variable for counting, set up a conditional statement to test it, and increment the counter (++ means the same as "variable = variable + 1"). The first part, where we define the i variable and set it to 0, is only executed once, before the loop starts. The last 2 parts are executed for each iteration of the loop. Each time, i is compared to our number variable - if i is smaller than number, the loop runs one more time. After that, i is increased by one. Try running the program, and afterwards, try changing the number variable to something bigger or smaller than 5. You will see the loop respond to the change. The foreach loop The last loop we will look at, is the foreach loop. It operates on collections of items, for instance arrays or other built-in list types. In our example we will use one of the simple lists, called an ArrayList. It works much like an array, but don't worry, we will look into it in a later chapter. using System; using System.Collections; namespace ConsoleApplication1 { class Program { static void Main(string[] args) {
29

ArrayList list = new ArrayList(); list.Add("John Doe"); list.Add("Jane Doe"); list.Add("Someone Else"); foreach(string name in list) Console.WriteLine(name); Console.ReadLine(); } } } Okay, so we create an instance of an ArrayList, and then we add some string items to it. We use the foreach loop to run through each item, setting the name variable to the item we have reached each time. That way, we have a named variable to output. As you can see, we declare the name variable to be of the string type you always need to tell the foreach loop which datatype you are expecting to pull out of the collection. In case you have a list of various types, you may use the object class instead of a specific class, to pull out each item as an object. When working with collections, you are very likely to be using the foreach loop most of the time, mainly because its simpler than any of the other loops for these kind of operations.

Control statements Control Statements - Loops In the last lesson, you learned how to create a simple loop by using the goto statement. I advised you that this is not the best way to perform loops in C#. The information in this lesson will teach you the proper way to execute iterative logic with the various C# looping statements. Its goal is to meet the following objectives: Learn the while loop. Learn the do loop. Learn the for loop. Learn the foreach loop. Complete your knowledge of the break statement. Teach you how to use the continue statement. The while Loop A while loop will check a condition and then continues to execute a block of code as long as the condition evaluates to a boolean value of true. Its syntax is as follows: while (<boolean expression>) { <statements> }. The statements can be any valid C# statements. The boolean expression is evaluated before any code in the following block has executed. When the boolean expression evaluates to true, the statements will execute. Once the statements have executed, control returns to the beginning of the while loop to check the boolean expression again. When the boolean expression evaluates to false, the while loop statements are skipped and execution begins after the closing brace of that block of code. Before entering the loop, ensure that variables evaluated in the loop condition are set to an initial state. During

30

execution, make sure you update variables associated with the boolean expression so that the loop will end when you want it to. Listing 4-1 shows how to implement a while loop. Listing 4-1. The While Loop: WhileLoop.cs using System; class WhileLoop { public static void Main() { int myInt = 0; while (myInt < 10) { Console.Write("{0} ", myInt); myInt++; } Console.WriteLine(); } } Listing 4-1 shows a simple while loop. It begins with the keyword while, followed by a boolean expression. All control statements use boolean expressions as their condition for entering/continuing the loop. This means that the expression must evaluate to either a true or false value. In this case we are checking the myInt variable to see if it is less than (<) 10. Since myInt was initialized to 0, the boolean expression will return true the first time it is evaluated. When the boolean expression evaluates to true, the block immediately following the boolean expression will be executed. Within the while block we print the number and a space to the console. Then we increment (++) myInt to the next integer. Once the statements in the while block have executed, the boolean expression is evaluated again. This sequence will continue until the boolean expression evaluates to false. Once the boolean expression is evaluated as false, program control will jump to the first statement following the while block. In this case, we will write the numbers 0 through 9 to the console, exit the while block, and print a new line to the console. The do Loop A do loop is similar to the while loop, except that it checks its condition at the end of the loop. This means that the do loop is guaranteed to execute at least one time. On the other hand, a while loop evaluates its boolean expression at the beginning and there is generally no guarantee that the statements inside the loop will be executed, unless you program the code to explicitly do so. One reason you may want to use a do loop instead of a while loop is to present a message or menu such as the one in Listing 4-2 and then retrieve input from a user. Listing 4-2. The Do Loop: DoLoop.cs using System; class DoLoop {
31

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();
32

Console.WriteLine(); } while (myChoice != "Q" && myChoice != "q"); // Keep going until the user wants to quit } } Listing 4-2 shows a do loop in action. The syntax of the do loop is do { <statements> } while (<boolean expression>);. The statements can be any valid C# programming statements you like. The boolean expression is the same as all others we've encountered so far. It returns either true or false. In the Main method, we declare the variable myChoice of type string. Then we print a series of statements to the console. This is a menu of choices for the user. We must get input from the user, which is in the form of a Console.ReadLine method which returns the user's value into the myChoice variable. We must take the user's input and process it. A very efficient way to do this is with a switch statement. Notice that we've placed matching upper and lower case letters together to obtain the same functionality. This is the only legal way to have automatic fall through between cases. If you were to place any statements between two cases, you would not be able to fall through. Another point is that we used the default: case, which is a very good habit for the reasons stated in Lesson 3: Control Statements - Selection. The for Loop A for loop works like a while loop, except that the syntax of the for loop includes initialization and condition modification. for loops are appropriate when you know exactly how many times you want to perform the statements within the loop. The contents within the for loop parentheses hold three sections separated by semicolons (<initializer list>; <boolean expression>; <iterator list>) { <statements> }. The initializer list is a comma separated list of expressions. These expressions are evaluated only once during the lifetime of the for loop. This is a one-time operation, before loop execution. This section is commonly used to initialize an integer to be used as a counter. Once the initializer list has been evaluated, the for loop gives control to its second section, the boolean expression. There is only one boolean expression, but it can be as complicated as you like as long as the result evaluates to true or false. The boolean expression is commonly used to verify the status of a counter variable. When the boolean expression evaluates to true, the statements within the curly braces of the for loop are executed. After executing for loop statements, control moves to the top of loop and executes the iterator list, which is normally used to increment or decrement a counter. The iterator list can contain a comma separated list of statements, but is generally only one statement. Listing 4-3 shows how to implement a for loop. The purpose of the program is to print only odd numbers less than 10. Listing 4-3. The For Loop: ForLoop.cs using System; class ForLoop { public static void Main() {
33

for (int i=0; i < 20; i++) { if (i == 10) break; if (i % 2 == 0) continue; Console.Write("{0} ", i); } Console.WriteLine(); } } Normally, for loop statements execute from the opening curly brace to the closing curly brace without interruption. However, in Listing 4-3, we've made a couple exceptions. There are a couple if statements disrupting the flow of control within the for block. The first if statement checks to see if i is equal to 10. Now you see another use of the break statement. Its behavior is similar to the selection statements, as discussed in Lesson 3: Control Statements - Selection. It simply breaks out of the loop at that point and transfers control to the first statement following the end of the for block. The second if statement uses the remainder operator to see if i is a multiple of 2. This will evaluate to true when i is divided by 2 with a remainder equal to zero, (0). When true, the continue statement is executed, causing control to skip over the remaining statements in the loop and transfer back to the iterator list. By arranging the statements within a block properly, you can conditionally execute them based upon whatever condition you need. When program control reaches either a continue statement or end of block, it transfers to the third section within the for loop parentheses, the iterator list. This is a comma separated list of actions that are executed after the statements in the for block have been executed. Listing 4-3 is a typical action, incrementing the counter. Once this is complete, control transfers to the boolean expression for evaluation. Similar to the while loop, a for loop will continue as long as the boolean expression is true. When the boolean expression becomes false, control is transferred to the first statement following the for block. For this tutorial, I chose to implement break and continue statements in Listing 4-3 only. However, they may be used in any of the loop statements. The foreach Loop A foreach loop is used to iterate through the items in a list. It operates on arrays or collections such as ArrayList, which can be found in the System.Collections namespace. The syntax of a foreach loop is foreach (<type> <iteration variable> in <list>) { <statements> }. The type is the type of item contained in the list. For example, if the type of the list was int[] then the type would be int.

34

The iteration variable is an identifier that you choose, which could be anything but should be meaningful. For example, if the list contained an array of people's ages, then a meaningful name for item name would be age. The in keyword is required. As mentioned earlier, the list could be either an array or a collection. You learned about arrays.Operators, Types, and Variables. You can also iterate over C# generic collections also, described in Lesson 20: Introduction to Generic Collections. While iterating through the items of a list with a foreach loop, the list is read-only. This means that you can't modify the iteration variable within a foreach loop. There is a subtlety here; Later, you'll learn how to create custom types, called class and struct, that can contain multiple fields. You can change the fields of the class or struct, but not the iteration variable for the class or struct itself in a foreach loop. On each iteration through a foreach loop the list is queried for a new value. As long as the list can return a value, this value will be put into the read-only iteration variable, causing the statements in the foreach block to be executed. When the collection has been fully traversed, control will transfer to the first executable statement following the end of the foreach block. Listing 4-4 demonstrates how to use a foreach loop. Listing 4-4. The ForEach Loop: ForEachLoop.cs using System; class ForEachLoop { public static void Main() { string[] names = {"Cheryl", "Joe", "Matt", "Robert"}; foreach (string person in names) { Console.WriteLine("{0} ", person); } } } In Listing 4-4, the first thing we've done inside the Main method is declare and initialize the names array with 4 strings. This is the list used in the foreach loop. In the foreach loop, we've used a string variable, person, as the item name, to hold each element of the names array. As long as there are names in the array that have not been returned, the Console.WriteLine method will print each value of the person variable to the screen.

35

CHAPTER : 3

ARRAY, STRING AND DATETIME STRUCTURE


Array
An array is a data structure that contains several variables of the same type. Arrays are declared with a type: type[] arrayName; The following examples create single-dimensional, multidimensional, and jagged arrays: class TestArraysClass { static void Main() { // Declare a single-dimensional array int[] array1 = new int[5]; // Declare and set array element values int[] array2 = new int[] { 1, 3, 5, 7, 9 }; // Alternative syntax int[] array3 = { 1, 2, 3, 4, 5, 6 }; // Declare a two dimensional array int[,] multiDimensionalArray1 = new int[2, 3]; // Declare and set array element values int[,] multiDimensionalArray2 = { { 1, 2, 3 }, { 4, 5, 6 } }; } } Array Overview An array has the following properties: An array can be Single-Dimensional, Multidimensional or Jagged. The default value of numeric array elements are set to zero, and reference elements are set to null. A jagged array is an array of arrays, and therefore its elements are reference types and are initialized to null. Arrays are zero indexed: an array with n elements is indexed from 0 to n-1. Array elements can be of any type, including an array type. Array types are reference types derived from the abstract base type Array. Since this type implements IEnumerable and IEnumerable(T), you can use foreach iteration on all arrays in C#. Array as objects In C#, arrays are actually objects, and not just addressable regions of contiguous memory as in C and C++. Array is the abstract base type of all array types. You can use the properties, and other class members, that Array has. An example of this would be using the Length property to get the length of an array. The following code assigns the length of the numbers array, which is 5, to a variable called lengthOfNumbers:
36

int[] numbers = { 1, 2, 3, 4, 5 }; int lengthOfNumbers = numbers.Length; The System.Array class provides many other useful methods and properties for sorting, searching, and copying arrays. Example This example uses the Rank property to display the number of dimensions of an array. class TestArraysClass { static void Main() { // Declare and initialize an array: int[,] theArray = new int[5, 10]; System.Console.WriteLine("The array has {0} dimensions.", theArray.Rank); } }

Output The array has 2 dimensions. Single-Dimensional Arrays You can declare an array of five integers as in the following example: int[] array = new int[5]; This array contains the elements from array[0] to array[4]. The new operator is used to create the array and initialize the array elements to their default values. In this example, all the array elements are initialized to zero. An array that stores string elements can be declared in the same way. For example: string[] stringArray = new string[6]; Array Initialization It is possible to initialize an array upon declaration, in which case, the rank specifier is not needed because it is already supplied by the number of elements in the initialization list. For example: int[] array1 = new int[] { 1, 3, 5, 7, 9 }; A string array can be initialized in the same way. The following is a declaration of a string array where each array element is initialized by a name of a day:
37

string[] weekDays = new string[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; When you initialize an array upon declaration, you can use the following shortcuts: int[] array2 = { 1, 3, 5, 7, 9 }; string[] weekDays2 = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; It is possible to declare an array variable without initialization, but you must use the new operator when you assign an array to this variable. For example: int[] array3; array3 = new int[] { 1, 3, 5, 7, 9 }; // OK //array3 = {1, 3, 5, 7, 9}; // Error Value Type and Reference Type Arrays Consider the following array declaration: SomeType[] array4 = new SomeType[10]; The result of this statement depends on whether SomeType is a value type or a reference type. If it is a value type, the statement creates an array of 10 instances of the type SomeType. If SomeType is a reference type, the statement creates an array of 10 elements, each of which is initialized to a null reference.

Multidimensional Arrays Arrays can have more than one dimension. For example, the following declaration creates a two-dimensional array of four rows and two columns: int[,] array = new int[4, 2]; Also, the following declaration creates an array of three dimensions, 4, 2, and 3: int[, ,] array1 = new int[4, 2, 3]; Array Initialization You can initialize the array upon declaration as shown in the following example: int[,] array2D = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } }; int[, ,] array3D = new int[,,] { { { 1, 2, 3 } }, { { 4, 5, 6 } } }; You can also initialize the array without specifying the rank: int[,] array4 = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
38

If you choose to declare an array variable without initialization, you must use the new operator to assign an array to the variable. For example: int[,] array5; array5 = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } }; // OK //array5 = {{1,2}, {3,4}, {5,6}, {7,8}}; // Error You can also assign a value to an array element, for example: array5[2, 1] = 25; The following code example initializes the array variables to default (except for jagged arrays): int[,] array6 = new int[10, 10]; Jagged Array A jagged array is an array whose elements are arrays. The elements of a jagged array can be of different dimensions and sizes. A jagged array is sometimes called an "array of arrays." The following examples show how to declare, initialize, and access jagged arrays. The following is a declaration of a single-dimensional array that has three elements, each of which is a single-dimensional array of integers: int[][] jaggedArray = new int[3][]; Before you can use jaggedArray, its elements must be initialized. You can initialize the elements like this: jaggedArray[0] = new int[5]; jaggedArray = new int[4]; jaggedArray[2] = new int[2]; Each of the elements is a single-dimensional array of integers. The first element is an array of 5 integers, the second is an array of 4 integers, and the third is an array of 2 integers. It is also possible to use initializers to fill the array elements with values, in which case you do not need the array size. For example: jaggedArray[0] = new int[] { 1, 3, 5, 7, 9 }; jaggedArray = new int[] { 0, 2, 4, 6 }; jaggedArray[2] = new int[] { 11, 22 }; You can also initialize the array upon declaration like this: int[][] jaggedArray2 = new int[][] { new int[] {1,3,5,7,9},
39

new int[] {0,2,4,6}, new int[] {11,22} }; You can use the following shorthand form. Notice that you cannot omit the new operator from the elements initialization because there is no default initialization for the elements: int[][] jaggedArray3 = { new int[] {1,3,5,7,9}, new int[] {0,2,4,6}, new int[] {11,22} }; A jagged array is an array of arrays, and therefore its elements are reference types and are initialized to null. You can access individual array elements like these examples: // Assign 77 to the second element () of the first array ([0]): jaggedArray3[0] = 77; // Assign 88 to the second element () of the third array ([2]): jaggedArray3[2] = 88; It is possible to mix jagged and multidimensional arrays. The following is a declaration and initialization of a single-dimensional jagged array that contains two-dimensional array elements of different sizes: int[][,] jaggedArray4 = new int[3][,] { new int[,] { {1,3}, {5,7} }, new int[,] { {0,2}, {4,6}, {8,10} }, new int[,] { {11,22}, {99,88}, {0,9} } }; You can access individual elements as shown in this example, which displays the value of the element [1,0] of the first array (value 5): System.Console.Write("{0}", jaggedArray4[0][1, 0]); The method Length returns the number of arrays contained in the jagged array. For example, assuming you have declared the previous array, this line: System.Console.WriteLine(jaggedArray4.Length); will return a value of 3. Example

40

This example builds an array whose elements are themselves arrays. Each one of the array elements has a different size. class ArrayTest { static void Main() { // Declare the array of two elements: int[][] arr = new int[2][]; // Initialize the elements: arr[0] = new int[5] { 1, 3, 5, 7, 9 }; arr = new int{ 2, 4, 6, 8 }; // Display the array elements: for (int i = 0; i < arr.Length; i++) { System.Console.Write("Element({0}): ", i); for (int j = 0; j < arr[i].Length; j++) { System.Console.Write("{0}{1}", arr[i][j], j == (arr[i].Length - 1) ? "" : " "); } System.Console.WriteLine(); } } } Output Element(0): 1 3 5 7 9 Element(1): 2 4 6 8

Using foreach with Arrays C# also provides the foreach statement. This statement provides a simple, clean way to iterate through the elements of an array. For example, the following code creates an array called numbers and iterates through it with the foreach statement: int[] numbers = { 4, 5, 6, 1, 2, 3, -2, -1, 0 }; foreach (int i in numbers) { System.Console.WriteLine(i); }
41

With multidimensional arrays, you can use the same method to iterate through the elements, for example: int[,] numbers2D = new int[3, 2] { { 9, 99 }, { 3, 33 }, { 5, 55 } }; foreach (int i in numbers2D) { System.Console.Write("{0} ", i); }

The output of this example is: 9 99 3 33 5 55 However, with multidimensional arrays, using a nested for loop gives you more control over the array elements. String The following sections discuss the string data type, which is an alias for the String class. A C# string is an array of characters that is declared by using the string keyword. A string literal is declared by using quotation marks, as shown in the following example: string s = "Hello, World!"; You can extract substrings, and concatenate strings as in the following example: string s1 = "orange"; string s2 = "red"; s1 += s2; System.Console.WriteLine(s1); // outputs "orangered" s1 = s1.Substring(2, 5); System.Console.WriteLine(s1); // outputs "anger" String objects are immutable: they cannot be changed after they have been created. Methods that act on strings actually return new string objects. In the previous example, when the contents of s1 and s2 are concatenated to form a single string, the two strings that contain "orange" and "red" are both unmodified. The += operator creates a new string that contains the combined contents. The result is that s1 now refers to a different string completely. A string that contains just "orange" still exists, but is no longer referenced when s1 is concatenated. Note:

42

Use caution when you create references to strings. If you create a reference to a string, and then "modify" the string, the reference will continue to point to the original object, not the new object that was created when the string was modified. The following code illustrates the danger: string s1 = "Hello"; string s2 = s1; s1 += " and goodbye."; Console.WriteLine(s2); //outputs "Hello" Because modifications to strings involve creating new string objects, for performance reasons, large amounts of concatenation or other involved string manipulation should be performed with the StringBuilder class, as in the following example: System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append("one "); sb.Append("two "); sb.Append("three"); string str = sb.ToString(); System.Console.WriteLine(str); // Outputs: one two three The StringBuilder class is discussed in the "Using Stringbuilder" section. Working with Strings Escape Characters Escape characters such as "\n" (new line) and "\t" (tab) can be included in strings. The line: string hello = "Hello\nWorld!"; is the same as: Hello World! If you want to include a backward slash, it must be preceded with another backward slash. The following string: string filePath = "\\\\My Documents\\"; is actually the same as: \\My Documents\

43

Verbatim Strings: The @ Symbol The @ symbol tells the string constructor to ignore escape characters and line breaks. The following two strings are therefore identical: string p1 = "\\\\My Documents\\My Files\\"; string p2 = @"\\My Documents\My Files\"; In a verbatim string, you escape the double quotation mark character with a second double quotation mark character, as in the following example: string s = @"You say ""goodbye"" and I say ""hello"""; Accessing Individual Characters Individual characters that are contained in a string can be accessed by using methods such as SubString() and Replace(). string s3 = "Visual C# Express"; System.Console.WriteLine(s3.Substring(7, 2)); // outputs "C#" System.Console.WriteLine(s3.Replace("C#", "Basic")); // outputs "Visual Basic Express" It is also possible to copy the characters into a character array, as in the following example: string s4 = "Hello, World"; char[] arr = s4.ToCharArray(0, s4.Length); foreach (char c in arr) { System.Console.Write(c); // outputs "Hello, World" } Individual characters from a string can be accessed with an index, as in the following example: string s5 = "Printing backwards"; for (int i = 0; i < s5.Length; i++) { System.Console.Write(s5[s5.Length - i - 1]); // outputs "sdrawkcab gnitnirP" } Changing Case To change the letters in a string to uppercase or lowercase, use ToUpper() or ToLower(), as in the following example: string s6 = "Battle of Hastings, 1066";
44

System.Console.WriteLine(s6.ToUpper()); // outputs "BATTLE OF HASTINGS 1066" System.Console.WriteLine(s6.ToLower()); // outputs "battle of hastings 1066" Comparisons The simplest way to compare two strings is to use the == and != operators, which perform a case-sensitive comparison. string color1 = "red"; string color2 = "green"; string color3 = "red"; if (color1 == color3) { System.Console.WriteLine("Equal"); } if (color1 != color2) { System.Console.WriteLine("Not equal"); } String objects also have a CompareTo() method that returns an integer value that is based on whether one string is less-than (<), equal to (==) or greater-than (>) another. When comparing strings, the Unicode value is used, and lowercase has a smaller value than uppercase. For more information about the rules for comparing strings, see CompareTo()()(). // Enter different values for string1 and string2 to // experiement with behavior of CompareTo string string1 = "ABC"; string string2 = "abc"; int result = string1.CompareTo(string2); if (result > 0) { System.Console.WriteLine("{0} is greater than {1}", string1, string2); } else if (result == 0) { System.Console.WriteLine("{0} is equal to {1}", string1, string2); } else if (result < 0) { System.Console.WriteLine("{0} is less than {1}", string1, string2); } // Outputs: ABC is less than abc To search for a string inside another string, use IndexOf(). IndexOf() returns -1 if the search string is not found; otherwise, it returns the zero-based index of the first location at which it occurs.
45

string s9 = "Battle of Hastings, 1066"; System.Console.WriteLine(s9.IndexOf("Hastings")); // outputs 10 System.Console.WriteLine(s9.IndexOf("1967")); // outputs -1 Splitting a String into Substrings Splitting a string into substrings, such as splitting a sentence into individual words, is a common programming task. The Split() method takes a char array of delimiters, for example, a space character, and returns an array of substrings. You can access this array with foreach, as in the following example: char[] delimit = new char[] { ' ' }; string s10 = "The cat sat on the mat."; foreach (string substr in s10.Split(delimit)) { System.Console.WriteLine(substr); } This code outputs each word on a separate line, as in the following example: The cat sat on the mat. Null Strings and Empty Strings An empty string is an instance of a System..::.String object that contains zero characters. Empty strings are used often in various programming scenarios to represent a blank text field. You can call methods on empty strings because they are valid System..::.String objects. Empty strings are initialized as follows: string s = ""; By contrast, a null string does not refer to an instance of a System..::.String object and any attempt to call a method on a null string causes a NullReferenceException. However, you can use null strings in concatenation and comparison operations with other strings. The following examples illustrate some cases in which a reference to a null string does and does not cause an exception to be thrown: string str = "hello"; string nullStr = null; string emptyStr = ""; string tempStr = str + nullStr; // tempStr = "hello" bool b = (emptyStr == nullStr);// b = false;
46

emptyStr + nullStr = ""; // creates a new empty string int len = nullStr.Length; // throws NullReferenceException

Using StringBuilder The StringBuilder class creates a string buffer that offers better performance if your program performs many string manipulations. The StringBuilder string also enables you to reassign individual characters, something the built-in string data type does not support. This code, for example, changes the content of a string without creating a new string: System.Text.StringBuilder sb = new System.Text.StringBuilder("Rat: the ideal pet"); sb[0] = 'C'; System.Console.WriteLine(sb.ToString()); System.Console.ReadLine(); //Outputs Cat: the ideal pet In this example, a StringBuilder object is used to create a string from a set of numeric types: class TestStringBuilder { static void Main() { System.Text.StringBuilder sb = new System.Text.StringBuilder(); // Create a string composed of numbers 0 - 9 for (int i = 0; i < 10; i++) { sb.Append(i.ToString()); } System.Console.WriteLine(sb); // displays 0123456789 // Copy one character of the string (not possible with a System.String) sb[0] = sb[9]; System.Console.WriteLine(sb); // displays 9123456789 } } Parse Strings Using the Split Method The following code example demonstrates how a string can be parsed using the String..::.Split method. This method works by returning an array of strings, where each element is a word. As input, Split takes an array of chars that indicate which characters are to be used as delimiters. In this example, spaces, commas, periods, colons, and tabs are used. An array containing these delimiters is passed to Split, and each word in the sentence is displayed separately using the resulting array of strings.
47

Example class TestStringSplit { static void Main() { char[] delimiterChars = { ' ', ',', '.', ':', '\t' }; string text = "one\ttwo three:four,five six seven"; System.Console.WriteLine("Original text: '{0}'", text); string[] words = text.Split(delimiterChars); System.Console.WriteLine("{0} words in text:", words.Length); foreach (string s in words) { System.Console.WriteLine(s); } } }

Original text: 'one 7 words in text: one two three four five six seven

two three:four,five six seven'

Choosing Between DateTime, DateTimeOffset, and TimeZoneInfo .NET Framework applications that use date and time information are very diverse and can use that information in several ways. The more common uses of date and time information include one or more of the following: To reflect a date only, so that time information is not important. To reflect a time only, so that date information is not important. To reflect an abstract date and time that is not tied to a specific time and place (for example, most stores in an international chain open on weekdays at 9:00 A.M.). To retrieve date and time information from sources outside the .NET Framework, typically where date and time information is stored in a simple data type.
48

To uniquely and unambiguously identify a single point in time. Some applications require that a date and time be unambiguous only on the host system; others require that it be unambiguous across systems (that is, a date serialized on one system can be meaningfully deserialized and used on another system anywhere in the world). To preserve multiple related times (such as the requestor's local time and the server's time of receipt for a Web request). To perform date and time arithmetic, possibly with a result that uniquely and unambiguously identifies a single point in time.

The .NET Framework includes the DateTime, DateTimeOffset, and TimeZoneInfo types, all of which can be used to build applications that work with dates and times. The DateTime Structure A DateTime value defines a particular date and time. Starting with version 2.0 of the .NET Framework, it includes a Kind property that provides limited information about the time zone to which that date and time belongs. The DateTimeKind value returned by the Kind property indicates whether the DateTime value represents the local time (DateTimeKind.Local), Coordinated Universal Time (UTC) (DateTimeKind.Utc), or an unspecified time (DateTimeKind.Unspecified). The DateTime structure is suitable for applications that do the following: Work with dates only. Work with times only. Work with abstract dates and times. Retrieve date and time information from sources outside the .NET Framework, such as SQL databases. Typically, these sources store date and time information in a simple format that is compatible with the DateTime structure. Perform date and time arithmetic, but are concerned with general results. For example, in an addition operation that adds six months to a particular date and time, it is often not important whether the result is adjusted for daylight saving time.

Unless a particular DateTime value represents UTC, that date and time value is often ambiguous or limited in its portability. For example, if a DateTime value represents the local time, it is portable within that local time zone (that is, if the value is deserialized on another system in the same time zone, that value still unambiguously identifies a single point in time). Outside the local time zone, that DateTime value can have multiple interpretations. If the value's Kind property is DateTimeKind.Unspecified, it is even less portable: it is now ambiguous within the same time zone and possibly even on the same system on which it was first serialized. Only if a DateTime value represents UTC does that value unambiguously identify a single point in time regardless of the system or time zone in which the value is used.

49

CHAPTER : 4

CLASS, OBJECT AND STRUCTURE


C# is an object-oriented programming language and uses classes and structs to implement types such as Windows Forms, user interface controls, and data structures. A typical C# application consists of classes defined by the programmer, combined with classes from the .NET Framework. C# provides many powerful ways of defining classes, such as providing different access levels, inheriting features from other classes, and enabling the programmer to specify what occurs when types are instantiated or destroyed.

Overview Objects, classes, and structs have the following properties: Objects are instances of a given data type. The data type provides a blueprint for the object that is created, or instantiated, when the application is executed. New data types are defined by using classes and structs. Classes and structs form the building blocks of C# applications that contain code and data. A C# application will always contain of at least one class. A struct can be considered a lightweight class, ideal for creating data types that store small amounts of data, and does not represent a type that might later be extended through inheritance. C# classes support inheritance; classes can derive from a previously defined class.

A class is the most powerful data type in C#. Like structures, a class defines the data and behavior of the data type. Programmers can then create objects that are instances of this class. Unlike structures, classes support inheritance, a fundamental part of object-oriented programming. Declaring Classes Classes are defined by using the class keyword, as shown in the following example: public class Customer { //Fields, properties, methods and events go here... } The class keyword is preceded by the access level. Because public is used in this case, anyone can create objects from this class. The name of the class follows the class keyword. The remainder of the definition is the class body, where the behavior and data are defined. Fields, properties, methods, and events on a class are collectively referred to as class members. Creating Objects

50

Although they are sometimes used interchangeably, a class and an object are different things. A class defines a type of object, but it is not an object itself. An object is a concrete entity based on a class, and is sometimes referred to as an instance of a class. Objects can be created by using the new keyword followed by the name of the class that the object will be based on, like this: Customer object1 = new Customer(); When an instance of a class is created, a reference to the object is passed back to the programmer. In the previous example, object1 is a reference to an object that is based on Customer. This reference refers to the new object but does not contain the object data itself. In fact, you can create an object reference without creating an object at all: Customer object2; We do not recommend creating object references such as this one that does not refer to an object because trying to access an object through such a reference will fail at run time. However, such a reference can be made to refer to an object, either by creating a new object, or by assigning it to an existing object, such as this: Customer object3 = new Customer(); Customer object4 = object3; This code creates two object references that both refer to the same object. Therefore, any changes to the object made through object3 will be reflected in subsequent uses of object4. Because objects that are based on classes are referred to by reference, classes are known as reference types. Class Inheritance Inheritance is accomplished by using a derivation, which means a class is declared by using a base class from which it inherits data and behavior. A base class is specified by appending a colon and the name of the base class following the derived class name, like this: public class Manager : Employee { // Employee fields, properties, methods and events are inherited // New Manager fields, properties, methods and events go here... } When a class declares a base class, all the members of the class that are defined for the base class become part of the new class also. Because a base class may itself inherit from another class, which inherited from another class, and so on, a class may have many base classes. Description In the following example, a public class that contains a single field, a method, and a special method called a constructor is defined.
51

The class is then instantiated with the new keyword. Example public class Person { // Field public string name; // Constructor public Person() { name = "unknown"; } // Method public void SetName(string newName) { name = newName; } } class TestPerson { static void Main() { Person person1 = new Person(); System.Console.WriteLine(person1.name); person1.SetName("John Smith"); System.Console.WriteLine(person1.name); } }

Output unknown John Smith

Nested Types A type defined within a class or struct is called a nested type. For example: class Container { class Nested {
52

Nested() { } } } Regardless of whether the outer types is a class or a struct, nested types default to private, but can be made public, protected internal, protected, internal, or private. In the previous example, Nested is inaccessible to external types, but can be made public like this: class Container { public class Nested { Nested() { } } }

The nested, or inner type can access the containing, or outer type. To access the containing type, pass it as a constructor to the nested type. For example: public class Container { public class Nested { private Container m_parent; public Nested() { } public Nested(Container parent) { m_parent = parent; } } } Nested types can access private and protected members of the containing type, including any inherited private or protected members. In the previous declaration, the full name of class Nested is Container.Nested. This is the name used to create a new instance of the nested class, as follows: Container.Nested nest = new Container.Nested(); Access Modifier

53

Classes and structs can be restricted so that only the program or namespace they are declared in may use them. Class members can be restricted so that only derived classes can use them, or restricted so that only classes in the current namespace or program can use them. Access modifiers are keywords added to the class, struct, or member declaration to specify these restrictions. Those keywords are public, private, protected, and internal. For example: public class Bicycle { public void Pedal() { } } Class and Struct Accessibility Classes and structs that are not nested within other classes or structs can be either public or internal. A type declared as public is accessible by any other type. A type declared as internal is only accessible by types in the same assembly. By default, classes and structs are declared as internal unless the public keyword is added to the class definition, as in the previous example. Class or struct definitions can add the internal keyword to make their access level explicit. Access modifiers do not affect the class or struct itself; it always has access to itself and all its own members. Class and Struct Member Accessibility Class or struct members can be declared with one of five types of access. They can be public or internal, just like the classes and structs themselves. When a class member is declared as protected by using the protected keyword, only derived types that use the class as a base can access the member. By combining the protected and internal keywords, a class member can be marked protectedinternal; only derived types or types in the same assembly can access that member. Finally, a class or struct member can be declared as private with the private keyword, which indicates that only the class or struct declaring the member can access that member. To set the access level for a class or struct member, add the appropriate keyword to the member declaration.

Fields
Fields in a class are used to hold data. Fields can be marked as public, private, protected, internal, or protected internal. A field can optionally be declared static. A field can be declared readonly. A read-only field can only be assigned a value during initialization or in a constructor. A field can be given an initial value by using the assignment operator when the field is declared. public class Car { public string make = "Ford"; }
54

Fields are initialized immediately before the constructor for the object instance is called, so if the constructor assigns the value of a field, it will overwrite any value given during field declaration. public class Car { public string make = "Ford"; public Car() { make = "Alfa"; } } These examples use fields that are public, but this is not recommended in practice. Fields should generally be private with access to fields given by using properties.

Constructors Whenever a class or struct is created, its constructor is called. A class or struct may have multiple constructors that take different arguments. Constructors enable the programmer to set default values, limit instantiation, and write code that is flexible and easy to read. If you do not provide a constructor for your object, C# will create one by default that instantiates the object and sets member variables to the default values as listed in Default Values Table (C# Reference). Static classes and structs can also have constructors. Constructors are class methods that are executed when an object of a given type is created. Constructors have the same name as the class, and usually initialize the data members of the new object. In the following example, a class named Taxi is defined by using a simple constructor. This class is then instantiated with the new operator. The Taxi constructor is invoked by the new operator immediately after memory is allocated for the new object. public class Taxi { public bool isInitialized; public Taxi() { isInitialized = true; } } class TestTaxi { static void Main()
55

{ Taxi t = new Taxi(); System.Console.WriteLine(t.isInitialized); } } Static Constructors A static constructor is used to initialize any static data, or to perform a particular action that needs performed once only. It is called automatically before the first instance is created or any static members are referenced. class SimpleClass { // Static constructor static SimpleClass() { //... } }

Copy Constructor Unlike some languages, C# does not provide a copy constructor. If you create a new object and want to copy the values from an existing object, you have to write the appropriate method yourself. Example In this example, the Personclass contains a constructor that takes as the argument another object of type Person. The contents of the fields in this object are then assigned to the fields in the new object. class Person { private string name; private int age; // Copy constructor. public Person(Person previousPerson) { name = previousPerson.name; age = previousPerson.age; } // Instance constructor. public Person(string name, int age) {
56

this.name = name; this.age = age; } // Get accessor. public string Details { get { return name + " is " + age.ToString(); } } } class TestPerson { static void Main() { // Create a new person object. Person person1 = new Person("Hanu", 5); // Create another new object, copying person1. Person person2 = new Person(person1); System.Console.WriteLine(person2.Details); } }

Hanu is 5

Properties
Properties allow users to access class variables as if they were accessing member fields directly, while actually implementing that access through a class method. The user wants direct access to the variables of the object and does not want to work with methods. The class designer, however, wants to hide the internal variables of his class in class members, and provide indirect access through a method. By decoupling the class variables from the methods that access those variables, the designer is free to change the internal state of the object as needed. Coding properties The code below shows how to create a private variable with its associated properties. // private member variables private int hour;
57

// create a property public int Hour { get { return hour; } set { hour = value; } }You then access the properties in the following manner: // Get the current value of hour to local variable iHour int iHour = aClass.Hour; // Increment iHour iHour++; // Write iHour back to hour aClass.Hour = iHour; Properties you can set the accessibility of get and set. The code below shows how to create a private variable with an internal set and public get. The Hour property can now only be set from code in the same module (dll), but can be accessed by all code that uses the module (dll) that contains the class.
// private member variables private int hour; // create a property public int Hour { get { return hour; } internal set { hour = value; } }

Indexer
In c# introduce new concept is Indexer. This is very useful for some situation. Let as discuss something about Indexer. Indexer Concept is object act as an array. Indexer an object to be indexed in the same way as an array. Indexer modifier can be private, public, protected or internal. The return type can be any valid C# types. Indexers in C# must have at least one parameter. Else the compiler will generate a compilation error.
58

this [Parameter] { get { // Get codes goes here } set { // Set codes goes here } } For Example: using System; using System.Collections.Generic; using System.Text; namespace Indexers { class ParentClass { private string[] range = new string[5]; public string this[int indexrange] { get { return range[indexrange]; } set { range[indexrange] = value; } } } /* The Above Class just act as array declaration using this pointer */ class childclass { public static void Main() { ParentClass obj = new ParentClass(); /* The Above Class ParentClass create one object name is obj */ obj[0] = "ONE"; obj = "TWO"; obj[2] = "THREE"; obj= "FOUR "; obj= "FIVE"; Console.WriteLine("WELCOME TO C# CORNER HOME PAGE\n");
59

Console.WriteLine("\n"); Console.WriteLine("{0}\n,{1}\n,{2}\n,{3}\n,{4}\n", obj[0], obj[4]); Console.WriteLine("\n"); Console.WriteLine("ALS.Senthur Ganesh Ram Kumar\n"); Console.WriteLine("\n"); Console.ReadLine(); } } }

obj,

obj[2],

obj[3],

const, static and readonly Within a class, const, static and readonly members are special in comparison to the other modifiers. const vs. readonly const and readonly perform a similar function on data members, but they have a few important differences. const A constant member is defined at compile time and cannot be changed at runtime. Constants are declared as a field, using the const keyword and must be initialized as they are declared. For example; public class MyClass { public const double PI = 3.14159; } PI cannot be changed in the application anywhere else in the code as this will cause a compiler error. Constants must be a value type (sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool), an enumeration, a string literal, or a reference to null. Since classes or structures are initialized at run time with the new keyword, and not at compile time, you can't set a constant to a class or structure. Constants can be marked as public, private, protected, internal, or protected internal. Constants are accessed as if they were static fields, although they cannot use the static keyword. To use a constant outside of the class that it is declared in, you must fully qualify it using the class name.

readonly

60

A read only member is like a constant in that it represents an unchanging value. The difference is that a readonly member can be initialized at runtime, in a constructor as well being able to be initialized as they are declared. For example: public class MyClass { public readonly double PI = 3.14159; } or public class MyClass { public readonly double PI; public MyClass() { PI = 3.14159; } } Because a readonly field can be initialized either at the declaration or in a constructor, readonly fields can have different values depending on the constructor used. A readonly field can also be used for runtime constants as in the following example: public static readonly uint l1 = (uint)DateTime.Now.Ticks; Notes Readonly members are not implicitly static, and therefore the static keyword can be applied to a readonly field explicitly if required. A readonly member can hold a complex object by using the new keyword at initialization. static Use of the static modifier to declare a static member, means that the member is no longer tied to a specific object. This means that the member can be accessed without creating an instance of the class. Only one copy of static fields and events exists, and static methods and properties can only access static fields and static events. For example: public class Car { public static int NumberOfWheels = 4; } The static modifier can be used with classes, fields, methods, properties, operators, events and constructors, but cannot be used with indexers, destructors, or types other than classes. static members are initialized before the static member is accessed for the first time, and before the static constructor, if any is called. To access a static class member, use the name of the class instead of a variable name to specify the location of the member. For example: int i = Car.NumberOfWheels;

61

Enumerators ENUMERATORS ARE USEFUL WHEN a value in the program can only have a specific set of values. A control that could only be one of four colors, or a network package that supports only two protocols, are situations where an enumeration can improve code. A Line Style Enumeration In the following example, a line drawing class uses an enumeration to declare the styles of lines it can draw: using System; public class Draw { public enum LineStyle { Solid, Dotted, DotDash, }

public void DrawLine(int x1, int y1, int x2, int y2, LineStyle lineStyle) { switch (lineStyle) { case LineStyle.Solid: // draw solid break; case LineStyle.Dotted: // draw dotted break; case LineStyle.DotDash: // draw dotdash break; default: throw(new ArgumentException("Invalid line style")); } } } class Test { public static void Main() { Draw draw = new Draw(); draw.DrawLine(0, 0, 10, 10, Draw.LineStyle.Solid); draw.DrawLine(5, 6, 23, 3, (Draw.LineStyle) 35); } }
62

The LineStyle enum defines the values that can be specified for the enum, and then that same enum is used in the function call to specify the type of line to draw. While enums do prevent the accidental specification of values outside of the enum range, the values that can be specified for an enum are not limited to the identifiers specified in the enum declaration. The second call to DrawLine() is legal, so an enum value passed into a function must still be validated to ensure that it is in the range of valid values. The Draw class throws an invalid argument exception if the argument is invalid. Enumerator Each enumerator has an underlying type that specifies how much storage is allocated for that enumerator. The valid base types for enumerators are byte, sbyte, short, ushort, int, uint, long, and ulong. If the base type is not specified, the base type defaults to int. The base type is specified by listing the base type after the enum name: enum SmallEnum : byte { A, B, C, D } Specifying the base type can be useful if size is a concern, or if the number of entries would exceed the number of possible values for int. Initialization By default, the value of the first enum member is set to 0, and incremented for each subsequent member. Specific values may be specified along with the member name: enum Values { A = 1, B = 5, C = 3, D = 42 } Computed values can also be used, as long as they only depend on values already defined in the enum: enum Values { A = 1, B = 2, C = A + B, D = A * C + 33 } If an enum is declared without a 0 value, this can lead to problems, since 0 is the default initialized value for the enum:
63

enum Values { A = 1, B = 2, C = A + B, D = A * C + 33 } class Test { public static void Member(Values value) { // do some processing here } public static void Main() { Values value = 0; Member(value); } } A member with the value 0 should always be defined as part of an enum. Bit Flag Enums Enums may also be used as bit flags by specifying a different bit value for each bit. Heres a typical definition: Enum BitValues { NoBits = 0, Bit1 = 0x00000001, Bit2 = 0x00000002, Bit3 = 0x00000004, Bit4 = 0x00000008, Bit5 = 0x00000010, AllBits = 0xFFFFFFFF } class Test { public static void Member(BitValues value) { // do some processing here } public static void Main() { Member(BitValues.Bit1 | BitValues.Bit2); }
64

} The [Flags] attribute before the enum definition is used so that designers and browsers can present a different interface for enums that are flag enums. In such enums, it would make sense to allow the user to OR several bits together, which wouldnt make sense for non-flag enums. The Main() function OR s two bit values together, and then passes the value to the member function. Conversions Enum types can be converted to their underlying type and back again with an explicit conversion: enum Values { A = 1, B = 5, C = 3, D = 42 } class Test { public static void Main() { Values v = (Values) 2; int ival = (int) v; } } The sole exception to this is that the literal 0 can be converted to an enum type without a cast. This is allowed so that the following code can be written: public void DoSomething(BitValues bv) { if (bv == 0) { } } The if statement would have to be written as if (bv == (BitValues) 0) if this exception wasnt present. Thats not bad for this example, but it could be quite cumbersome in actual use if the enum is nested deeply in the hierarchy: if (bv == (CornSoft.PlotLibrary.Drawing.LineStyle.BitValues) 0) Thats a lot of typing.

65

CHAPTER : 5

INHERITANCE, POLYMORPHISM, INTERFACE


Inheritance This little article is intended for rank .NET newbies who are making their first attempts at C# programming. I assume that they have done some elementary C++ programming and know what classes and member functions are. Using a few simple code snippets we'll see how C# supports inheritance and polymorphism. Inheritance & Polymorphism When you derive a class from a base class, the derived class will inherit all members of the base class except constructors, though whether the derived class would be able to access those members would depend upon the accessibility of those members in the base class. C# gives us polymorphism through inheritance. Inheritance-based polymorphism allows us to define methods in a base class and override them with derived class implementations. Thus if you have a base class object that might be holding one of several derived class objects, polymorphism when properly used allows you to call a method that will work differently according to the type of derived class the object belongs to. Consider the following class which we'll use as a base class. class Animal { public Animal() { Console.WriteLine("Animal constructor"); } public void Greet() { Console.WriteLine("Animal says Hello"); } public void Talk() { Console.WriteLine("Animal talk"); } public virtual void Sing() { Console.WriteLine("Animal song"); } };Now see how we derive another class from this base class.

class Dog : Animal { public Dog() { Console.WriteLine("Dog constructor"); }


66

public new void Talk() { Console.WriteLine("Dog talk"); } public override void Sing() { Console.WriteLine("Dog song"); } };Now try this code out.

Animal a1 = new Animal(); a1.Talk(); a1.Sing(); a1.Greet(); //Output Animal constructor Animal talk Animal song Animal says HelloOkay, that came out just as expected. Now try this code out.

Animal a2 = new Dog(); a2.Talk(); a2.Sing(); a2.Greet(); //Output Animal constructor Dog constructor Animal talk Dog song Animal says HelloWe have an object of type Animal, but it references an object of type Dog. Thus you can see the base class constructor getting called first followed by the derived class constructor. Now we call Talk() and find that the method that's executed is the base class method. That's not surprising when you consider that the object was declared to be of the base type which in our case is Animal. Now when we call Sing(), we find that the derived class method has got called. This is because in the base class the method is prototyped as public virtual void Sing() and in the derived class we have overridden it by using public override void Sing(). In C#, we need to explicitly use the override keyword as opposed to C++ where we didn't have to do that. And finally when we call Greet() the base class method gets called and this is not confusing at all specially since the derived class has not even implemented the method. Now try the following code out. Dog d1 = new Dog(); d1.Talk(); d1.Sing();
67

d1.Greet(); //Output Animal constructor Dog constructor Dog talk Dog song Animal says HelloOkay, here everything came out as expected. No rude surprises there. The fact that we could invoke the Greet() method is proof of inheritance in C#, not that anyone had any doubts to begin with I guess. Now take a look at this new class we'll be using as a base class for some other classes.

class Color { public virtual void Fill() { Console.WriteLine("Fill me up with color"); } public void Fill(string s) { Console.WriteLine("Fill me up with {0}",s); } };Now run this code out.

Color c1 = new Color(); c1.Fill(); c1.Fill("red"); //Output Fill me up with color Fill me up with redOkay, that went fine, I'd say. Now let's derive a class from this class. class Green : Color { public override void Fill() { Console.WriteLine("Fill me up with green"); } };Now let's try this code out.

Green g1 = new Green(); g1.Fill(); g1.Fill("violet"); //Output Fill me up with green

68

Fill me up with violetWell, that went fine too. Thus if you have overloaded methods, you can mark some of them as virtual and override them in the derived class. It's not required that you have to override all the overloads. Now I want to demonstrate some stuff on overloaded constructors. For that we'll use the following base class.

class Software { public Software() { m_x = 100; } public Software(int y) { m_x = y; } protected int m_x; };Now we'll derive a class from the above class.

class MicrosoftSoftware : Software { public MicrosoftSoftware() { Console.WriteLine(m_x); } };Now try this code out MicrosoftSoftware m1 = new MicrosoftSoftware(); //MicrosoftSoftware m2 = new MicrosoftSoftware(300); //won't compile //Output 100The base class had two overloaded constructors. One that took zero arguments and one that took an int. In the derived class we only have the zero argument constructor. Constructors are not inherited by derived classes. Thus we cannot instantiate a derived class object using the constructor that takes an int as parameter. As you will deduce from the output we got, the base class constructor that called was the default parameter-less constructor. Now take a look at this second derived class.

class DundasSoftware : Software { //Here I am telling the compiler which //overload of the base constructor to call public DundasSoftware(int y) : base(y) { Console.WriteLine(m_x); } //Here we are telling the compiler to first
69

//call the other overload of the constructor public DundasSoftware(string s, int f) : this(f) { Console.WriteLine(s); } };Here we have two constructors, one that takes an int and one that takes a string and an int. Now lets try some code out.

DundasSoftware du1 = new DundasSoftware(50); //Output 50 DundasSoftware du2 = new DundasSoftware("test",75); //Output 75 testThere, now that you've seen how it came out, things are a lot clearer I bet. You can use the this and base access keywords on other methods too, and not just on constructors. Multiple Inheritance: C# does not support multiple implementation inheritance A class cannot be derived from more than one class However, a class can be derived from multiple interfaces Inheritance Usage Example: Here is a syntax example for using Implementation Inheritance Class derivedClass:baseClass { } derivedClass is derived from baseClass Interface Inheritance example: private Class derivedClass:baseClass , InterfaceX , InterfaceY { } derivedClass is now derived from interfaces InterfaceX, InterfaceY Similarly a struct can be derived from any number of interfaces private struct childStruct:InterfaceX, InterfaceY { } Virtual Methods If a function or a property in the base class is declared as virtual it can be overridden in any derived classes Usage Example: class baseClass
70

{ public virtual int fnCount() { return 10; } } class derivedClass :baseClass { public override int fnCount() { return 100; } } This is useful because the compiler verifies that the override function has the same signature as the virtual function Hiding Methods: Similar to the above scenario if the methods are declared in a child and base class with the same signature but without the key words virtual and override, the child class function is said to hide the base class function class someBaseClass { } class abcClass:someBaseClass { public int fnAge() { return 99; } } class grandchildClass: abcClass { public int fnAge() { return 10; } } In the example above the function fnAge in grandChildClass hides the function fnAge in its parent class ie abcClass The C# compiler will generate a warning in this case The new keyword should be used when we intend to hide a method Example: class grandchildClass: abcClass { public new int fnAge() {
71

return 10; } }

Interface
Interfaces are closely related to abstract classes; they resemble an abstract class that has all members abstract. Example:The following code defines the interface IScalable and the class TextObject, which implements the interface, meaning that it contains versions of all the functions defined in the interface. public class DiagramObject { public DiagramObject() {} } interface IScalable { void ScaleX(float factor); void ScaleY(float factor); } // A diagram object that also implements IScalable public class TextObject: DiagramObject, IScalable { public TextObject(string text) { this.text = text; } // implementing ISclalable.ScaleX() public void ScaleX(float factor) { // scale the object here. } // implementing ISclalable.ScaleY() public void ScaleY(float factor) { // scale the object here. } private string text; } class Test { public static void Main() { TextObject text = new TextObject("Hello"); IScalable scalable = (IScalable) text;
72

scalable.ScaleX(0.5F); scalable.ScaleY(0.5F); } } This code implements a system for drawing diagrams. All of the objects derive from DiagramObject, so that they can implement common virtual functions (not shown in this example). Some of the objects can be scaled, and this is expressed by the presence of an implementation of the IScalable interface. Listing the interface name with the base class name for TextObject indicates that TextObject implements the interface. This means that TextObject must have functions that match every function in the interface. Interface members have no access modifiers; the class that implements the interface sets the visibility of the interface member. When an object implements an interface, a reference to the interface can be obtained by casting to the interface. This can then be used to call the functions on the interface. This example could have been done with abstract methods, by moving the ScaleX() and ScaleY() methods to DiagramObject and making them virtual. The "Design Guidelines" section later in this chapter will discuss when to use an abstract method and when to use an interface. Working with Interfaces Typically, code doesnt know whether an object supports an interface, so it needs to check whether the object implements the interface before doing the cast. using System; interface IScalable { void ScaleX(float factor); void ScaleY(float factor); } public class DiagramObject { public DiagramObject() {} } public class TextObject: DiagramObject, IScalable { public TextObject(string text) { this.text = text; } // implementing ISclalable.ScaleX() public void ScaleX(float factor) { Console.WriteLine("ScaleX: {0} {1}", text, factor); } // implementing ISclalable.ScaleY() public void ScaleY(float factor) { Console.WriteLine("ScaleY: {0} {1}", text, factor); }

// scale the object here.

// scale the object here.


73

private string text; } class Test { public static void Main() { DiagramObject[] dArray = new DiagramObject[100]; dArray[0] = new DiagramObject(); dArray = new TextObject("Text Dude"); dArray[2] = new TextObject("Text Backup"); // array gets initialized here, with classes that // derive from DiagramObject. Some of them implement foreach (DiagramObject d in dArray) { if (d is IScalable) { IScalable scalable = (IScalable) d; scalable.ScaleX(0.1F); scalable.ScaleY(10.0F); } }

// IScalable.

} } Before the cast is done, the type is checked to make sure that the cast will succeed. If it will succeed, the object is cast to the interface, and the scale functions are called. This construct unfortunately checks the type of the object twice; once as part of the is operator, and once as part of the cast. This is wasteful, since the cast can never fail. One way around this would be to restructure the code with exception handling, but thats not a great idea, because it would make the code more complex, and exception handling should generally be - 69 reserved for exceptional conditions. Its also not clear whether it would be faster, since exception handling has some overhead. The as Operator C# provides a special operator for this situation, the as operator. Using the as operator, the loop can be rewritten as follows: using System; interface IScalable { void ScaleX(float factor); void ScaleY(float factor); } public class DiagramObject { public DiagramObject() {} }
74

public class TextObject: DiagramObject, IScalable { public TextObject(string text) { this.text = text; } // implementing ISclalable.ScaleX() public void ScaleX(float factor) { Console.WriteLine("ScaleX: {0} {1}", text, factor); } // implementing ISclalable.ScaleY() public void ScaleY(float factor) { Console.WriteLine("ScaleY: {0} {1}", text, factor); } private string text; } class Test { public static void Main() { DiagramObject[] dArray = new DiagramObject[100]; dArray[0] = new DiagramObject(); dArray = new TextObject("Text Dude"); dArray[2] = new TextObject("Text Backup"); // array gets initialized here, with classes that // derive from DiagramObject. Some of them implement foreach (DiagramObject d in dArray) { IScalable scalable = d as IScalable; if (scalable != null) { scalable.ScaleX(0.1F); scalable.ScaleY(10.0F); } }

// scale the object here.

// scale the object here.

// IScalable.

} } The as operator checks the type of the left operand, and if it can be converted explicitly to the right operand, the result of the operator is the object converted to the right operand. If the conversion would fail, the operator returns null. Both the is and as operators can also be used with classes.

75

Interfaces and Inheritance When converting from an object to an interface, the inheritance hierarchy is searched until it finds a class that lists the interface on its base list. Having the right functions alone is not enough: using System; interface IHelper { void HelpMeNow(); } public class Base: IHelper { public void HelpMeNow() { Console.WriteLine("Base.HelpMeNow()"); } } // Does not implement IHelper, though it has the right public class Derived: Base { public new void HelpMeNow() { Console.WriteLine("Derived.HelpMeNow()"); } } class Test { public static void Main() { Derived der = new Derived(); der.HelpMeNow(); IHelper helper = (IHelper) der; helper.HelpMeNow(); } } This code gives the following output: Derived.HelpMeNow() Base.HelpMeNow() It doesnt call the Derived version of HelpMeNow() when calling through the interface, even though Derived does have a function of the correct form, because Derived doesnt implement the interface. Design Guidelines Both interfaces and abstract classes have similar behaviors and can be used in similar situations. Because of how they work, however, interfaces make sense in some situations, and abstract classes in others. Here are a few guidelines to determine whether a capability should be expressed as an interface or an abstract class.
76

// form.

The first thing to check is whether the object would be properly expressed using the is-a relationship. In other words, is the capability an object, and would the derived classes be examples of that object? Another way of looking at this is to list what kind of objects would want to use this capability. If the capability would be useful across a range of different objects that arent really related to each other, an interface is the proper choice. Caution Because there can only be one base class in the .NET Runtime world, this decision is pretty important. If a base class is required, users will be very disappointed if they already have a base class and are unable to use the feature. When using interfaces, remember that there is no versioning support for an interface. If a function is added to an interface after users are already using it, their code will break at runtime and their classes will not properly implement the interface until the appropriate modifications are made. Multiple Implementation Unlike object inheritance, a class can implement more than one interface. interface IFoo { void ExecuteFoo(); } interface IBar { void ExecuteBar(); } class Tester: IFoo, IBar { public void ExecuteFoo() {} public void ExecuteBar() {} } That works fine if there are no name collisions between the functions in the interfaces. But if the example was just a bit different, there might be a problem: // error interface IFoo { void Execute(); } interface IBar { void Execute(); } class Tester: IFoo, IBar { // IFoo or IBar implementation? public void Execute() {}
77

} Does Tester.Execute() implement IFoo.Execute(), or IBar.Execute()? Its ambiguous, so the compiler reports an error. If the user controlled either of the interfaces, the name in one of them could be changed, but thats not a great solution; why should IFoo have to change the name of its function just because IBar has the same name? More seriously, if IFoo and IBar came from different vendors, they couldnt be changed. The .NET Runtime and C# support a technique known as explicit interface implementation, which allows a function to specify which interface member its implementing. Explicit Interface Implementation To specify which interface a member function is implementing, qualify the member function by putting the interface name in front of the member name. Heres the previous example, revised to use explicit interface implementation: using System; interface IFoo { void Execute(); } interface IBar { void Execute(); } class Tester: IFoo, IBar { void IFoo.Execute() { Console.WriteLine("IFoo.Execute implementation"); void IBar.Execute() { Console.WriteLine("IBar.Execute implementation"); } class Test { public static void Main() { Tester tester = new Tester(); IFoo iFoo = (IFoo) tester; iFoo.Execute(); IBar iBar = (IBar) tester; iBar.Execute(); } } This prints: IFoo.Execute implementation
78

IBar.Execute implementation This is what we expected. But what does the following test class do? // error using System; interface IFoo { void Execute(); } interface IBar { void Execute(); } class Tester: IFoo, IBar { void IFoo.Execute() { Console.WriteLine("IFoo.Execute implementation"); void IBar.Execute() { Console.WriteLine("IBar.Execute implementation"); } class Test { public static void Main() { Tester tester = new Tester(); tester.Execute(); } } Is IFoo.Execute() called, or is IBar.Execute() called? The answer is that neither is called. There is no access modifier on the implementations of IFoo.Execute() and IBar.Execute() in the Tester class, and therefore the functions are private and cant be called. In this case, this behavior isnt because the public modifier wasnt used on the function, its because access modifiers are prohibited on explicit interface implementations, so that the only way the interface can be accessed is by casting the object to the appropriate interface. To expose one of the functions, a forwarding function is added to Tester: using System; interface IFoo { void Execute(); } interface IBar { void Execute();
79

} class Tester: IFoo, IBar { void IFoo.Execute() { Console.WriteLine("IFoo.Execute implementation"); void IBar.Execute() { Console.WriteLine("IBar.Execute implementation"); public void Execute() { ((IFoo)this).Execute(); } } class Test { public static void Main() { Tester tester = new Tester(); tester.Execute(); }

} Now, calling the Execute() function on an instance of Tester will forward to Tester.IFoo.Execute(). Implementation Hiding There may be cases where it makes sense to hide the implementation of an interface from the users of a class, either because its not generally useful, or just to reduce the member clutter. Doing so can make an object much easier to use. For example:
using System; class DrawingSurface { } interface IRenderIcon { void DrawIcon(DrawingSurface surface, int x, int y); void DragIcon(DrawingSurface surface, int x, int y, int x2, int y2); void ResizeIcon(DrawingSurface surface, int xsize, int ysize); } class Employee: IRenderIcon { public Employee(int id, string name) { this.id = id; this.name = name; } void IRenderIcon.DrawIcon(DrawingSurface surface, int x, int y) { } void IRenderIcon.DragIcon(DrawingSurface surface, int x, int y, int x2, int y2) 80

{ } void IRenderIcon.ResizeIcon(DrawingSurface surface, int xsize, int ysize) } int id; string name; }

If the interface had been implemented normally, the DrawIcon(), DragIcon(), and ResizeIcon() member functions would be visible as part of Employee, which might be confusing to users of the class. By implementing them through explicit implementation, they can only be accessed through the interface. Interfaces Based on Interfaces Interfaces can also be combined together to form new interfaces. The ISortable and ISerializable interfaces can be combined together, and new interface members can be added. using System.Runtime.Serialization; using System; interface IComparableSerializable : IComparable, ISerializable { string GetStatusString(); } A class that implements IComparableSerializable would need to implement all the members in IComparable, ISerializable, and the GetStatusString() function introduced in IComparableSerializable. Versioning Using new and override Software projects rarely exist as a single version of code that is never revised, unless the software never sees the light of day. In most cases, the software library writer is going to want to change some things, and the client will need to adapt to such changes. Dealing with such issues is known as versioning, and its one of the harder things to do in software. One reason why its tough is that it requires a bit of planning and foresight; the areas that might change have to be determined, and the design must be modified to allow change. Another reason why versioning is tough is that most execution environments dont provide much help to the programmer. In C++, compiled code has internal knowledge of the size and layout of all classes burned into it. With care, some revisions can be made to the class without forcing all users to recompile, but the restrictions are fairly severe. When compatibility is broken, all users need to recompile to use the new version. This may not be that bad, though installing a new version of a library may cause other applications that use an older version of the library to cease functioning. Managed environments that dont expose class member or layout information in the metadata fare better at versioning but its still possible to write code that versions poorly. A Versioning Example The following code presents a simple versioning scenario. The program uses a class named Control, which is provided by another company.

81

public class Control { } public class MyControl: Control { } During implementation of MyControl, the virtual function Foo() is added: public class Control { } public class MyControl: Control { public virtual void Foo() {} } This works well, until an upgrade notice arrives from the suppliers of the Control object. The new library includes a virtual Foo() function on the Control object. public class Control { // newly added virtual public virtual void Foo() {} } public class MyControl: Control { public virtual void Foo() {} } That Control uses Foo() as the name of the function is only a coincidence. In the C++ world, the compiler will assume that the version of Foo() in MyControl does what a virtual override of the Foo() in Control should do, and will blindly call the version in MyControl. Which is bad. In the Java world, this will also happen, but things can be a fair bit worse; if the virtual function doesnt have the same parameters and "return type, the class loader will consider the Foo() in MyControl to be an invalid override of the Foo() in Control, and the class will fail to load at runtime. In C# and the .NET Runtime, a function defined with virtual is always considered to be the root of a virtual dispatch. If a function is introduced into a base class that could be considered a base virtual function of an existing function, the run- time behavior is unchanged. When the class is next compiled, however, the compiler will generate a warning, requesting that the programmer specify their versioning intent. Returning to the example, to specify that the default behavior of not considering the function an override continue, the new modifier is added in front of the function: class Control { public virtual void Foo() {} } class MyControl: Control {
82

// not an override public new virtual void Foo() {} } The presence of new will suppress the warning. If, on the other hand, the derived version is an override of the function in the base class, the override modifier is used. class Control { public virtual void Foo() {} } class MyControl: Control { // an override for Control.Foo() public override void Foo() {} } This tells the compiler that the function really is an override. Caution About this time, theres somebody in the back of the room whos thinking, Ill just put new on all of my virtual functions, and then Ill never have to deal with the situation again." Doing so is discouraged because it reduces the value that the new annotation has to somebody reading the code. If new is only used when it is required, the reader can find the base class and understand what function isnt being overridden. If new is used indiscriminately, the user will have to refer to the base class every time to see if the new has meaning. Properties and Inheritance Like member functions, properties can also be declared using the virtual, override, or abstract modifiers. These modifiers are placed on the property and affect both accessors. When a derived class declares a property with the same name as in the base class, it hides the entire property; it is not possible to hide only a getter or setter. Use of Properties Properties separate the interface of a class from the implementation of a class. This is useful in cases where the property is derived from other fields, and also to do lazy initialization and only fetch a value if the user really needs it. Suppose that a car maker wanted to be able to produce a report that listed some current information about the production of cars. using System; class Auto { public Auto(int id, string name) { this.id = id; this.name = name; } // query to find # produced
83

public int ProductionCount { get { if (productionCount == -1) { // fetch count from database here. } return(productionCount); } } public int SalesCount { get { if (salesCount == -1) { // query each dealership for data } return(salesCount); } } string name; int id; int productionCount = -1; int salesCount = -1; } Both the ProductionCount and SalesCount properties are initialized to , and the expensive operation of calculating them is deferred until it is actually needed. Side Effects When Setting Values Properties are also very useful to do something beyond merely setting a value when the setter is called. A shopping basket could update the total when the user changed an item count, for example: using System; using System.Collections; class Basket { internal void UpdateTotal() { total = 0; foreach (BasketItem item in items) { total += item.Total; } } ArrayList items = new ArrayList(); Decimal total;
84

} class BasketItem { BasketItem(Basket basket) { this.basket = basket; } public int Quantity { get { return(quantity); } set { quantity = value; basket.UpdateTotal(); } } public Decimal Price { get { return(price); } set { price = value; basket.UpdateTotal(); } } public Decimal Total { get { // volume discount; 10% if 10 or more are purchased if (quantity >= 10) return(quantity * price * 0.90m); else return(quantity * price); } } int quantity; // count of the item Decimal price; // price of the item Basket basket; // reference back to the basket } In this example, the Basket class contains an array of BasketItem. When the price or quantity of an item is updated, an update is fired back to the Basket class, and the basket walks through all the items to update the total for the basket.
85

Static Properties In addition to member properties, C# also allows the definition of static properties, which belong to the whole class rather than to a specific instance of the class. Like static member functions, static properties cannot be declared with the virtual, abstract, or override modifiers. When readonly fields were discussed Other Class Stuff," there was a case that initialized some static readonly fields. The same thing can be done with static properties without having to initialize the fields until necessary. The value can also be fabricated when needed, and not stored. If creating the field is costly and it will likely be used again, then the value should be cached in a private field. If it is cheap to create or it is unlikely to be used again, it can be created as needed. class Color { public Color(int red, int green, int blue) { this.red = red; this.green = green; this.blue = blue; } int red; int green; int blue; public static Color Red { get { return(new Color(255, 0, 0)); } } public static Color Green { get { return(new Color(0, 255, 0)); } } public static Color Blue { get { return(new Color(0, 0, 255)); } } } class Test { static void Main()
86

{ Color background = Color.Red; } } When the user wants one of the predefined color values, the getter in the property creates an instance with the proper color on the fly, and returns that instance. Property Efficiency Returning to the first example in this chapter, lets consider the efficiency of the code when executed: class Test { private string name; public string Name { get { return name; } set { name = value; } } } This may seem to be an inefficient design, because a member function call is added where there would normally be a field access. However, there is no reason that the underlying runtime environment cant inline the accessors as it would any other simple function, so there is often no performance penalty in choosing a property instead of a simple field. The opportunity to be able to revise the implementation at a later time without changing the interface can be invaluable, so properties are usually a better choice than fields for public members. There does remain a small downside of using properties; they arent supported natively by all .NET languages, so other languages may have to call the accessor functions directly, which is a bit more complicated than using fields. Win32 version of the .NET Runtime does perform the inlining of trivial accessors, though other environments wouldnt have to.

87

CHAPTER : 6

NAMESPACE
Microsofts Visual Studio .NET has introduced many new concepts to the Visual Studio developer, including the Microsoft Intermediate Language (MSIL) with runtime compilation, garbage collection, Common Language Runtime (CLR), and, perhaps most misunderstood of all: namespaces and assemblies. To help you develop a better understanding of the .NET environment, this article will explore namespaces and assemblies and clarify the relationship between them.

The namespace
At first glance, it appears as if namespaces represent little more than the C++ include directive or the addition of a VB module to a project. But the concept of namespaces and assemblies with the C# using directive and the VB Imports statement in Visual Studio .NET extends beyond the inclusion of predefined header files. They represent a method of interacting with external code libraries that may be new to the Microsoft developer. Put simply, a namespace is just a grouping of related classes. It's a method of putting classes inside a container so that they can be clearly distinguished from other classes with the same name. Programmers skilled in the Java language will recognize namespaces as packages. A namespace is a logical grouping rather than a physical grouping. The physical grouping is accomplished by an assembly, which equates most directly to a dynamic link library (DLL), COM object, or OCX module.

Namespaces in C# A Namespace is simply a logical collection of related classes in C#. We bundle our related classes (like those related with database activity) in some named collection calling it a namespace (e.g., DataActivity). As C# does not allow two classes with the same name to be used in a program, the sole purpose of using namespaces is to prevent name conflicts. This may happen when you have a large number of classes, as is the case in the Framework Class Library (FCL). It is very much possible that our Connection Class in DataActivity conflicts with the Connection Class of InternetActivity. To avoid this, these classes are made part of their respective namespace. So the fully qualified name of these classes will be DataActivity.Connection and InternetActivity.Connection, hence resolving any ambiguity for the compiler. So, in the second line of our program we are declaring that the following classes (within { } block) are part of MyHelloWorldApplication namespace. namespace MyHelloWorldApplication { ... }

88

The C# namespaces have NO physical mapping as is the case in Java. Classes with same namespace can be in different folders. The C# concept of mapping is similar to packages in Java and namespaces in standard C++. The namespace may contain classes, events, exceptions, delegates and even other namespaces called 'Internal namespace'.

These internal namespaces can be defined like this: namespace Parent { namespace Child { ... } } The using Keyword The first line of our program was: using System; The using keyword above allows us to use the classes in the following 'System' namespace. By doing this, we can now access all the classes defined in the System namespace like we are able to access the Console class in our Main method later. One point to remember here is using allows you to access the classes in the referenced namespace only and not in its internal/child namespaces. Hence we might need to write using System.Collections; in order to access the classes defined in Collection namespace which is a sub/internal namespace of System namespace.

89

CHAPTER : 7

COLLECTION FRAMEWORK

Selecting a collection
All the collections implement IEnumerable interface that's extended by ICollection. There are two interfaces for collections derived from ICollection: IDictionary and IList.

IList interface is for collections of values. Here is the list of collections that implement it:

System.Array System.Collections.ArrayList System.Collections.Specialized.StringCollection

The interface IDictionary is for collections of (Key, Value) pairs. Here is the list of collections that implement it:

System.Collections.Hashtable System.Collections.Specialized.ListDictionary System.Collections.SortedList System.Collections.Specialized.HybridDictionary

Other collections derived from ICollection are:


System.Collections.BitArray System.Collections.Stack System.Collections.Queue System.Collections.Specialized.NameValueCollection

90

Then there is System.Collections.Specialized.StringDictionary that implements just IEnumerable and there is also System.Collections.Specialized.BitVector32 tha's a collection of bit/Boolean. This article focuses on managing collections in .NET Framework 2.0. Collection represents a set of objects that you can access by stepping through each element in turn. The .NET Framework provides specialized classes for managing collection and these classes have rich capability for enhancing your programming experience through better performance and easy maintenance. Object class is the base class of every type in .NET. All the collections implement IEnumerable interface that is extended by ICollection interface. IDictionary and IList are also interfaces for collection which are derived from ICollection as shown in the diagram. System.Object Object class is the base class of every type. All other types directly or indirectly derive from object class. Because of its lofty position, a .NET developer should have a good knowledge of the object class and its members. 1.Static Methods object.Equals(object objA, object objB) This method does some testing for null on objA and objB and calls objA.Equals(objB). It returns true if objA and objB are null references or both are the same instance, or if objA.Equals(objB) returns true, otherwise it returns false. int n1 = 2; int n2 = 3; bool result1 = object.Equals(n1, n2); // returns false. // because n1 & n2 are value type // and it will be compared by value. string s1 = "test"; string s2 = "test"; bool result2 = object.Equals(s1, s2); // returns true. s1 & s2 are // reference type, // but Equals(object obj) method of // object class is overridden by // string class. // that's why it returns true because // s1 and s2 are comparing // by their values. object obj1 = new Person(1, "Test1"); object obj2 = new Person(1, "Test1"); bool result3 = object.Equals(obj1, obj2); // returns false. obj1 & obj2 // are reference type,
91

// both are comparing by // reference and both are // different instances but // having same values. object.ReferenceEquals(object objA, object objB) This method returns true if objA is the same instance as objB or both have null reference, otherwise return false.

int n1 = 2; int n2 = 2; bool result1 = object.ReferenceEquals(n1, n2); // returns false. // because n1 & n2 are // different instances. object obj1 = new Person(1, "Test1"); object obj2 = new Person(1, "Test1"); object obj3 = obj1; bool result2 = object.ReferenceEquals(obj1, obj2); // returns false because // obj1 & obj2 are different instances. bool result3 = object.ReferenceEquals(obj1, obj3); // returns true because // obj1 & obj2 are same instances. 2.Methods Equals(object obj) The default implementation of Equals() supports reference equality only, but derived class can override this method to support value equality. An example is string class, which overrides Equals() to ensure that the two strings are compared by the value of their strings. A common operation, especially for searching or sorting in collections is testing two objects for equality.

string s1 = "Test"; string s2 = "Test"; bool result1 = s1.Equals(s2); // returns true. // because s1 & s2 has same value. object obj1 = new Person(1, "Test1"); object obj2 = new Person(1, "Test1"); object obj3 = obj1; bool result2 = obj1.Equals(obj2); // returns false. // because obj1 & obj2 are different // instances. bool result3 = obj1.Equals(obj3); // returns true. // because obj1 & obj3 are same instances. GetHashCode() It returns the hash code for the current object. This method also serves as a hash function for a particular type. It is suitable for use in hashing algorithms and data
92

structures like a hash table. This method can be overridden by the derive class. Object.GetHashCode() returns the same hash code for the same instance, but it is not necessary that it will return a different hash code for two different instances or the same hash code for two instances which have the same values. Different versions of the .NET Framework might also generate different hash codes for the same instance. Default hash code returned by the GetHashCode() method has no guarantee to be unique, you should override GetHashCode in your custom types.

object obj1 = 4; object obj2 = "Test"; object obj3 = new Person(1, "Test1"); int result1 = obj1.GetHashCode(); // returns 4. int result2 = obj2.GetHashCode(); // returns -354185577. int result3 = obj3.GetHashCode(); // returns 2418355. GetType() It returns the Type object of current instance. Type is the basis for using reflection in .NET. Use the members of Type to get information about a type declaration, such as the constructors, methods, fields, properties, and events of a class, as well as the module and the assembly in which the class is deployed.

object obj1 = 4; object obj2 = "Test"; object obj3 = new Person(1, "Test1"); string type1 = obj1.GetType().ToString(); // returns System.Int32 string type2 = obj2.GetType().ToString(); // returns System.String. string type3 = obj3.GetType().ToString(); // returns DotNetCollections. // CollectionExp.Person. ToString() It returns the human readable string of the object that is culture specific. The default implementation returns the runtime type of the object. The derive class can override this method for returning meaningful value of the type. For example, the ToString() method of Double returns the string representation of the value that the object has.

object obj1 = 4; object obj2 = "Test"; object obj3 = new Person(1, "Test1"); string s1 = obj1.ToString(); // returns 4 string s2 = obj2.ToString(); // returns Test string s3 = obj3.ToString(); // returns DotNetCollections. // CollectionExp.Person.System.Collections.IEnumerable It exposes the enumerator, which provides a collection like behavior to user defined classes.
93

1.Methods GetEnumerator() It returns the enumerator object that can be used to iterate through the collection. It allows using the foreach statement. Enumerators only allow reading the data in the collection.

Array array = new int[] { 12, 24, 26, 35, 40, 59 }; IEnumerator iEnum = array.GetEnumerator(); string msg = ""; while (iEnum.MoveNext()) { int n = (int)iEnum.Current; msg += n.ToString() + "\n"; } MessageBox.Show(msg); System.Collections.ICollection ICollection interface specifies a method for getting the size of collection, creating enumerators on a collection and managing synchronized access to all non-generic collections. It is a base interface for classes in the System.Collections namespace. 1.Properties Count It returns the number of elements contain by ICollection.

// Array List ArrayList sourceList = new ArrayList(); sourceList.Add(10); sourceList.Add(20); sourceList.Add(30); int count = sourceList.Count; // count = 3. IsSynchronized It returns true if access to the ICollection is synchronized. SyncRoot It returns an object that can be used to synchronize access to the ICollection.

ArrayList sourceList = new ArrayList(); sourceList.Add(10); sourceList.Add(20); sourceList.Add(30);


94

lock (sourceList.SyncRoot) { string list = string.Empty; foreach (object value in sourceList) { if (list.Length > 0) list += ", "; list += value.ToString(); } MessageBox.Show(list); }2.Methods CopyTo(Array array, int index) CopyTo() method copies the elements of the ICollection object to any array, starting at a particular Array index. If .NET is unable to cast source type to destination, then it throws ArrayTypeMismatchException exception.

// Copy int array to other int array int[] sourceIDs = new int[] { 1, 2, 3, 4, 5 }; int[] destinationIDs = new int[sourceIDs.Length]; sourceIDs.CopyTo(destinationIDs, 0); // destinationIDs = 1, 2, 3, 4, 5

// Copy array list items to int array. // But each item in array list has int type ArrayList sourceList = new ArrayList(); sourceList.Add(10); sourceList.Add(20); sourceList.Add(30); int[] destinationList = new int[5]; destinationList[0] = 1; destinationList = 5; sourceList.CopyTo(destinationList, 2); // start copy on index 2. // destinationList = 1, 5, 10, 20, 30System.Collections.IList IList interface represents the collection of objects that can be individually accessed by index. IList interface represents the collection of objects that can be individually accessed by index. The implementation of IList falls into three categories: read-only, fixed-size, and variablesize. A read only IList cannot be modified. A fixed size IList does not allow the addition or removal of elements, but it allows the modification of the existing elements. A variables size IList allows the addition, removal, and modification of elements. 1.Properties
95

IsFixedSize It returns true if IList has fixed size.

ArrayList arrayList = new ArrayList(); bool isFixedSize = arrayList.IsFixedSize; // false, because ArrayList // is not fixed size listIsReadOnly It returns true if IList is read only.

ArrayList arrayList = new ArrayList(); arrayList.Add(1); arrayList.Add(2); arrayList.Add(3); bool readOnly = arrayList.IsReadOnly; // false, because default array // list is not readonly. // create readonly list from existing list ArrayList readOnlyList = ArrayList.ReadOnly(arrayList); bool isNewListReadOnly = readOnlyList.IsReadOnly; // true. now user can't // modify this list2.Methods Add(object value) It adds the item into the IList.

ArrayList arrayList = new ArrayList(); arrayList.Add(1); // Add First Item arrayList.Add(2); // Add Second Item arrayList.Add(3); // Add Third Item Clear() It removes the all items from the IList.

ArrayList arrayList = new ArrayList(); arrayList.Add(1); arrayList.Add(2); arrayList.Add(3); int itemsCount = arrayList.Count; // 3 arrayList.Clear(); itemsCount = arrayList.Count; // 0 Contains(object value) It returns true if IList contain a specific value. This method uses the Equals and CompareTo methods to determine whether an item exists.
96

ArrayList arrayList = new ArrayList(); arrayList.Add(new Person(1,"test")); Person person1 = new Person(1, "test"); Person person2 = new Person(2, "test2"); bool result1 = arrayList.Contains(person1); // true bool result2 = arrayList.Contains(person2); // false IndexOf(object value) It returns the index of a specific item in the IList. This method also uses the Equals and CompareTo methods to determine whether an item exists.

// Populate Array list ArrayList arrayList = new ArrayList(); arrayList.Add(new Person(1, "test1")); arrayList.Add(new Person(2, "test2")); arrayList.Add(new Person(3, "test3")); // create new object Person person3 = new Person(3, "test3"); Person person4 = new Person(4, "test4"); int result1 = arrayList.IndexOf(person3); // 2, int result2 = arrayList.IndexOf(person4); // -1. because it does not exist // in list Insert(int index, object value) It inserts an item to the IList at specific index. If index equals the number of items in the IList, then value is appended to the end, but if index greater then the number of items in the IList or less then zero, then it throws ArgumentOutOfRangeException exception. If you try to insert item in the read-only or fixed size IList then it throws NotSupportedException exception.

ArrayList arrayList = new ArrayList(); arrayList.Add(new Person(1, "test1")); arrayList.Add(new Person(2, "test2")); arrayList.Add(new Person(3, "test3")); // create new object Person person =new Person(4, "test4"); // insert item at index 2. arrayList.Insert(2, person);

97

Remove(object value) It removes the first occurrence of a specific object from the IList. If you try to remove value from read only or fixed size IList, then it throws NotSupportedException.

ArrayList arrayList = new ArrayList(); arrayList.Add(new Person(1, "test1")); arrayList.Add(new Person(2, "test2")); arrayList.Add(new Person(3, "test3")); // Create person Person person = new Person(2, "test2"); arrayList.Remove(person); // it will remove 2nd item. it will call // Equals method to object to find in list. RemoveAt(int index) It removes an item at the specified index. It throws ArgumentOutOfRangeException exception for invalid index in list and throws NotSupportedException exception for read only and fixed size IList.

ArrayList arrayList = new ArrayList(); arrayList.Add(new Person(1, "test1")); arrayList.Add(new Person(2, "test2")); arrayList.Add(new Person(3, "test3")); arrayList.RemoveAt(1); // remove item at index 1System.Collections.IDictionary It represents a collection of key/value pairs. IDictionary interface is implemented by classes that supports collections of associated keys and values. Each element in a key/value pair is stored in a DictionaryEntry object. It allows the contained keys and values to be enumerated, but it does not imply any particular sort order. 1.Properties IsFixedSize It returns true if IDictionary object has a fixed size.

Hashtable hashList = new Hashtable(); hashList.Add(1, "item#1"); hashList.Add(2, "item#2"); hashList.Add(3, "item#3"); bool result = hashList.IsFixedSize; // falseIsReadOnly It returns true if IDictionary object is read only.

Hashtable hashList = new Hashtable(); hashList.Add(1, "item#1");


98

hashList.Add(2, "item#2"); hashList.Add(3, "item#3"); bool result = hashList.IsReadOnly;Keys It returns ICollection object containing keys of the IDictionary object.

Hashtable hashList = new Hashtable(); hashList.Add(1, "item#1"); hashList.Add(2, "item#2"); hashList.Add(3, "item#3"); ICollection keys = hashList.Keys; string[] strKeys = new string[keys.Count]; int index =0; foreach (int key in keys) { strKeys[index++] = key.ToString(); } string keysList = string.Join(", ",strKeys); // 3, 2, 1Values It returns ICollection object containing values of the IDictionary object.

Hashtable hashList = new Hashtable(); hashList.Add(1, "item#1"); hashList.Add(2, "item#2"); hashList.Add(3, "item#3"); ICollection values = hashList.Values; string[] strValues = new string[values.Count]; int index = 0; foreach (string value in values) { strValues[index++] = value; } string valueList = string.Join(", ", strValues); //item#1, item#2, item#32.Methods Add(object key, object value) Adds an element with the specified key and value into the IDictionary object.

99

Hashtable hashList = new Hashtable(); hashList.Add(1, "item#1"); hashList.Add(2, "item#2"); hashList.Add(3, "item#3"); Clear() It removes all elements from the IDictionary object.

Hashtable hashList = new Hashtable(); hashList.Add(1, "item#1"); hashList.Add(2, "item#2"); hashList.Add(3, "item#3"); hashList.Clear(); // it removes all item from the list. Contains(object key) It returns true if IDictionary object contains an element with the specified key.

Hashtable hashList = new Hashtable(); hashList.Add(1, "item#1"); hashList.Add(2, "item#2"); hashList.Add(3, "item#3"); bool result = hashList.Contains(1); // true GetEnumerator() It returns an IDictionaryEnumerator object for the IDictionary object.

Hashtable hashList = new Hashtable(); hashList.Add(1, "item#1"); hashList.Add(2, "item#2"); hashList.Add(3, "item#3"); IDictionaryEnumerator dicEnum = hashList.GetEnumerator(); string items = string.Empty; while (dicEnum.MoveNext()) { items += string.Format("{0} : {1}\n", dicEnum.Key, dicEnum.Value); } MessageBox.Show(items); Remove(object key) It removes the element with the specified key from the IDictionary object.

100

Hashtable hashList = new Hashtable(); hashList.Add(1, "item#1"); hashList.Add(2, "item#2"); hashList.Add(3, "item#3"); hashList.Remove(2); // remove item which has 2 key

101

CHAPTER : 8

EXCEPTION HANDLING
Exception Handling In many programming books, exception handling warrants a chapter somewhat late in the book. In this book, however, its very near the front, for a couple of reasons. The first reason is that exception handling is deeply ingrained in the .NET Runtime, and is therefore very common in C# code. C++ code can be written without using exception handling, but thats not an option in C#. The second reason is that it allows the code examples to be better. If exception handling is late in the book, early code samples cant use it, and that means the examples cant be written using good programming practices. Unfortunately, this means that classes must be used without really introducing them. Read the following section for flavour. What's Wrong with Return Codes? Most programmers have probably written code that looked like this: bool success = CallFunction(); if (!success) { // process the error } This works okay, but every return value has to be checked for an error. If the above was written as CallFunction(); any error return would be thrown away. Thats where bugs come from. There are many different models for communicating status; some functions may return an HRESULT, some may return a Boolean value, and others may use some other mechanism. In the .NET Runtime world, exceptions are the fundamental method of handling error conditions. Exceptions are nicer than return codes because they cant be silently ignored. Trying and Catching To deal with exceptions, code needs to be organized a bit differently. The sections of code that might throw exceptions are placed in a try block, and the code to handle exceptions in the try block is placed in a catch block. Heres an example: using System; class Test { static int Zero = 0; public static void Main() { // watch for exceptions here try
102

{ int j = 22 / Zero; } // exceptions that occur in try are transferred here { Console.WriteLine("Exception " + e.Message); } Console.WriteLine("After catch"); } } The try block encloses an expression that will generate an exception. In this case, it will generate an exception known as DivideByZeroException. When the division takes place, the .NET Runtime stops executing code and searches for a try block surrounding the code in which the exception took place. When it finds a try block, it then looks for associated catch blocks. If it finds catch blocks, it picks the best one (more on how it determines which one is best in a minute), and executes the code within the catch block. The code in the catch block may process the event or rethrow it. The example code catches the exception and writes out the message that is contained within the exception object. The Exception Hierarchy All C# exceptions derive from the class named Exception, which is part of the Common Language Runtime. When an exception occurs, the proper catch block is determined by matching the type of the exception to the name of the exception mentioned. A catch block with an exact match wins out over a more general exception. Returning to the example: using System; class Test { static int Zero = 0; public static void Main() { try { int j = 22 / Zero; } // catch a specific exception catch (DivideByZeroException e) { Console.WriteLine("DivideByZero {0}", e); } // catch any remaining exceptions catch (Exception e) { Console.WriteLine("Exception {0}", e); } }
103

catch (Exception e)

} The catch block that catches the DivideByZeroException is the more specific match, and is therefore the one that is executed. This example is a bit more complex: using System; class Test { static int Zero = 0; static void AFunction() { int j = 22 / Zero; // the following line is never executed. Console.WriteLine("In AFunction()"); } public static void Main() { try { AFunction(); } catch (DivideByZeroException e) { Console.WriteLine("DivideByZero {0}", e); } } What happens here? When the division is executed, an exception is generated. The runtime starts searching for a try block in AFunction(), but it doesnt find one, so it jumps out of AFunction(), and checks for a try in Main(). It finds one, and then looks for a catch that matches. The catch block then executes. Sometimes, there wont be any catch clauses that match. using System; class Test { static int Zero = 0; static void AFunction() { try { int j = 22 / Zero; } // this exception doesn't match catch (ArgumentOutOfRangeException e) { Console.WriteLine("OutOfRangeException: {0}", e); Console.WriteLine("In AFunction()"); }
104

public static void Main() { try { AFunction(); } // this exception doesn't match catch (ArgumentException e) { Console.WriteLine("ArgumentException {0}", e); } } } Neither the catch block in AFunction() nor the catch block in Main() matches the exception thats thrown. When this happens, the exception is caught by the "last chance" exception handler. The action taken by this handler depends on how the runtime is configured, but it will usually bring up a dialog box containing the exception information and halt the program. This is true of .NET classes in general, but there are some cases where this might not hold true. Passing Exceptions on to the Caller Its sometimes the case that theres not much that can be done when an exception occurs; it really has to be handled by the calling function. There are three basic ways to deal with this, which are named based on their result in the caller: Caller Beware, Caller Confuse, and Caller Inform. Caller Beware The first way is to merely not catch the exception. This is sometimes the right design decision, but it could leave the object in an incorrect state, causing problems when the caller tries to use it later. It may also give insufficient information to the caller. Caller Confuse The second way is to catch the exception, do some cleanup, and then rethrow the exception: using System; public class Summer { int sum = 0; int count = 0; float average; public void DoAverage() { try { average = sum / count; } catch (DivideByZeroException e) { // do some cleanup here throw e; }
105

} } class Test { public static void Main() { Summer summer = new Summer(); try { summer.DoAverage(); } catch (Exception e) { Console.WriteLine("Exception {0}", e); } } } This is usually the minimal bar for handling exceptions; an object should always maintain a valid state after an exception. This is called Caller Confuse because while the object is in a valid state after the exception occurs, the caller often has little information to go on. In this case, the exception information says that a DivideByZeroException occurred somewhere in the called function, without giving any insight into the details of the exception or how it might be fixed. Sometimes this is okay if the exception passes back obvious information. Caller Inform In Caller Inform, additional information is returned for the user. The caught exception is wrapped in an exception that has additional information. using System; public class Summer { int sum = 0; int count = 0; float average; public void DoAverage() { try { average = sum / count; } catch (DivideByZeroException e) { // wrap exception in another one, // adding additional context. throw (new DivideByZeroException( "Count is zero in DoAverage()", e)); }
106

} } public class Test { public static void Main() { Summer summer = new Summer(); try { summer.DoAverage(); } catch (Exception e) { Console.WriteLine("Exception: {0}", e); } } } When the DivideByZeroException is caught in the DoAverage() function, it is wrapped in a new exception that gives the user additional information about what caused the exception. Usually the wrapper exception is the same type as the caught exception, but this might change depending on the model presented to the caller. Exception: System.DivideByZeroException: System.DivideByZeroException at Summer.DoAverage() at Summer.DoAverage() at Test.Main() Ideally, each function that wants to rethrow the exception will wrap it in an exception with additional contextual information. User-Defined Exception Classes One drawback of the last example is that the caller cant tell what exception happened in the call to DoAverage() by looking at the type of the exception. To know that the exception was because the count was zero, the expression message would have to be searched for the string count is zero ". That would be pretty bad, since the user wouldnt be able to trust that the text would remain the same in later versions of the class, and the class writer wouldnt be able to change the text. In this case, a new exception class can be created. using System; public class CountIsZeroException: Exception { public CountIsZeroException() { } public CountIsZeroException(string message) : base(message) {
107

Count

is

zero

in

DoAverage()

--->

} public CountIsZeroException(string message, Exception inner) : base(message, inner) { } } public class Summer { int sum = 0; int count = 0; float average; public void DoAverage() { if (count == 0) throw(new CountIsZeroException("Zero count in DoAverage")); else average = sum / count; } } class Test { public static void Main() { Summer summer = new Summer(); try { summer.DoAverage(); } catch (CountIsZeroException e) { Console.WriteLine("CountIsZeroException: {0}", e); } } } DoAverage() now determines whether there would be an exception (whether count is zero), and if so, creates a CountIsZeroException and throws it. Finally Sometimes, when writing a function, there will be some cleanup that needs to be done before the function completes, such as closing a file. If an exception occurs, the cleanup could be skipped: using System; using System.IO; class Processor { int count; int sum; public int average; void CalculateAverage(int countAdd, int sumAdd) { count += countAdd; sum += sumAdd;
108

average = sum / count; } public void ProcessFile() { FileStream f = new FileStream("data.txt", FileMode.Open); try { StreamReader t = new StreamReader(f); string line; while ((line = t.ReadLine()) != null) { int count; int sum; line = t.ReadLine(); count = Int32.FromString(line); sum = Int32.FromString(line); CalculateAverage(count, sum); } f.Close(); } // always executed before function exit, even if an // exception was thrown in the try. finally { f.Close(); } } } class Test { public static void Main() { Processor processor = new Processor(); try { processor.ProcessFile(); } catch (Exception e) { Console.WriteLine("Exception: {0}", e); } } } This example walks through a file, reading a count and sum from a file and using it to accumulate an average. What happens, however, if the first count read from the file is a zero? If this happens, the division in CalculateAverage() will throw a DivideByZero-Exception, which will interrupt the file-reading loop. If the programmer had written the function without thinking about exceptions, the call to file.Close() would have been skipped, and the file would have remained open.

109

The code inside the finally block is guaranteed to execute before the exit of the function, whether there is an exception or not. By placing the file.Close() call in the finally block, the file will always be closed. Efficiency and Overhead In languages without garbage collection, adding exception handling is expensive, since all objects within a function must be tracked to make sure that they are properly destroyed if an exception is thrown. The required tracking code both adds execution time and code size to a function. In C#, however, objects are tracked by the garbage collector rather than the compiler, so exception handling is very inexpensive to implement and imposes little runtime overhead on the program when the exceptional case doesnt occur. Exceptions should be used to communicate exceptional conditions. Dont use them to communicate events that are expected, such as reaching the end of a file. In the normal operation of a class, there should be no exceptions thrown. Conversely, dont use return values to communicate information that would be better contained in an exception. If theres a good predefined exception in the System namespace that describes the exception conditionone that will make sense to the users of the classuse that one rather than defining a new exception class, and put specific information in the message. If the user might want to differentiate one case from others where that same exception might occur, then that uld be a good place for a new exception class. Finally, if code catches an exception that it isnt going to handle, consider whether it should wrap that exception with additional information before rethrowing it.

110

CHAPTER : 9

ASSEMBLIES
An assembly is a file that is automatically generated by the compiler upon successful compilation of every .NET application. It can be either a Dynamic Link Library or an executable file. It is generated only once for an application and upon each subsequent compilation the assembly gets updated. The entire process will run in the background of your application; there is no need for you to learn deeply about assemblies. However, a basic knowledge about this topic will help you to understand the architecture behind a .NET application. An Assembly contains Intermediate Language (IL) code, which is similar to Java byte code. In the .NET language, it consists of metadata. Metadata enumerates the features of every "type" inside the assembly or the binary. In addition to metadata, assemblies also have a special file called Manifest. It contains information about the current version of the assembly andotherrelatedinformation. In .NET, there are two kinds of assemblies, such as Single file and Multi file. A single file assembly contains all the required information (IL, Metadata, and Manifest) in a single package. The majority of assemblies in .NET are made up of single file assemblies. Multi file assemblies are composed of numerous .NET binaries or modules and are generated for larger applications. One of the assemblies will contain a manifest and others will have IL and Metadatainstructions. The main benefit of Intermediate Language is its power to integrate with all NET languages. This is because all .NET languages produce the same IL code upon successful compilation; hence, they can interact with each other very easily. However, .NET is not yet declared as a platform-independent language; efforts are on at Microsoft to achieve this objective. As of today, .NET applications are equipped to run only on Windows. Dissecting the Intermediate Language (IL) Code You can view the IL code generated by a .NET-aware compiler with the help of a utility called ILDASM.exe, which comes with the .NET Framework. ILDASM stands for Intermediate Language Disassembler. It may be located under the BIN directory of the .NET SDK installation folder. If you have Visual Studio NET 2003, you can locate this tool from the SDK folder of the installation directory. Figure 9.1 shows a screenshot of this tool loaded with information about a simple C# program named HelloWorld.exe.

111

Figure 9.1

The above utility parses the application's metadata and displays information about the application in a user-friendly and treelike fashion. This process is termed as Reflection. .NET provides a separate namespace named System.Reflection to dissect an application's metadata. You can use the classes and methods included in this namespace to display type information and also for various other tasks. You will learn more about Reflection in a later FAQ. Moreover, the utility shows special icons before each category to enable you to understand their identity. For example, the red arrow before the term MANIFEST indicates that additional information is available for this particular type. The cyan diamond marked "S" signifies that it is a static field. The blue rectangle with three outputs indicates that the title is a class. If you look into the IL code for advanced assemblies, there will be a down arrow icon in blue. It denotes that the item is a namespace. You will find a detailed explanation about each one of these icons from the online documentation that comes with the .NET Framework. Double-clicking the relevant titles will open a new window containing the IL code, but you cannot edit them. For instance, if you open the Main() method, you will be presented with a window as shown in Figure 9.2.

Figure 9.2

If you carefully monitor the code given in the above figure, you will be able to learn some information. But, don't worry if you can't understand anything. Knowledge of these codes is not essential for learning C#. If you attempt to open an executable file created with other
112

languages, such as Visual Basic 6.0, the disassembler will display an error message as shown in Figure 9.3. This message indicates that the file you tried to open is not managed, or as technically called, is unmanaged code. Unmanaged codes are not produced by the .NET Framework; hence, they cannot be dissected with the disassembler.

Figure 9.3

Every assembly, whether static or dynamic, contains a collection of data that describes how the elements in the assembly relate to each other. The assembly manifest contains this assembly metadata. An assembly manifest contains all the metadata needed to specify the assembly's version requirements and security identity, and all metadata needed to define the scope of the assembly and resolve references to resources and classes. The assembly manifest can be stored in either a PE file (an .exe or .dll) with Microsoft intermediate language (MSIL) code or in a standalone PE file that contains only assembly manifest information. The following illustration shows the different ways the manifest can be stored. Types of assemblies

For an assembly with one associated file, the manifest is incorporated into the PE file to form a single-file assembly. You can create a multifile assembly with a standalone manifest file or with the manifest incorporated into one of the PE files in the assembly. Each assembly's manifest performs the following functions: Enumerates the files that make up the assembly. Governs how references to the assembly's types and resources map to the files that contain their declarations and implementations. Enumerates other assemblies on which the assembly depends. Provides a level of indirection between consumers of the assembly and the assembly's implementation details. Renders the assembly self-describing.
113

Assembly Manifest Contents The following table shows the information contained in the assembly manifest. The first four itemsthe assembly name, version number, culture, and strong name informationmake up the assembly's identity. Information Assembly name Version number Description A text string specifying the assembly's name. A major and minor version number, and a revision and build number. The common language runtime uses these numbers to enforce version policy. Information on the culture or language the assembly supports. This information should be used only to designate an assembly as a satellite assembly containing culture- or language-specific information. (An assembly with culture information is automatically assumed to be a satellite assembly.)

Culture

Strong name The public key from the publisher if the assembly has been given a information strong name. List of all files in A hash of each file contained in the assembly and a file name. Note that the assembly all files that make up the assembly must be in the same directory as the file containing the assembly manifest. Type reference Information used by the runtime to map a type reference to the file that information contains its declaration and implementation. This is used for types that are exported from the assembly. Information referenced assemblies on A list of other assemblies that are statically referenced by the assembly. Each reference includes the dependent assembly's name, assembly metadata (version, culture, operating system, and so on), and public key, if the assembly is strong named.

You can add or change some information in the assembly manifest by using assembly attributes in your code. You can change version information and informational attributes, including Trademark, Copyright, Product, Company, and Informational Version. For a complete list of assembly attributes, see Setting Assembly Attributes. In general, a static assembly can consist of four elements: The assembly manifest, which contains assembly metadata. Type metadata. Microsoft intermediate language (MSIL) code that implements the types. A set of resources. Only the assembly manifest is required, but either types or resources are needed to give the assembly any meaningful functionality.

114

There are several ways to group these elements in an assembly. You can group all elements in a single physical file, which is shown in the following illustration. Single-file assembly

Alternatively, the elements of an assembly can be contained in several files. These files can be modules of compiled code (.netmodule), resources (such as .bmp or .jpg files), or other files required by the application. Create a multifile assembly when you want to combine modules written in different languages and to optimize downloading an application by putting seldom used types in a module that is downloaded only when needed. In the following illustration, the developer of a hypothetical application has chosen to separate some utility code into a different module and to keep a large resource file (in this case a .bmp image) in its original file. The .NET Framework downloads a file only when it is referenced; keeping infrequently referenced code in a separate file from the application optimizes code download. Multifile assembly

Note: The files that make up a multifile assembly are not physically linked by the file system. Rather, they are linked through the assembly manifest and the common language runtime manages them as a unit. In this illustration, all three files belong to an assembly, as described in the assembly manifest contained in MyAssembly.dll. To the file system, they are three separate files. Note that the file Util.netmodule was compiled as a module because it contains no assembly information. When the assembly was created, the assembly manifest was added to MyAssembly.dll, indicating its relationship with Util.netmodule and Graphic.bmp. As you currently design your source code, you make explicit decisions about how to partition the functionality of your application into one or more files. When designing .NET Framework code, you will make similar decisions about how to partition the functionality into one or more assemblies.
115

Assembly Benefits Assemblies are designed to simplify application deployment and to solve versioning problems that can occur with component-based applications. End users and developers are familiar with versioning and deployment issues that arise from today's component-based systems. Some end users have experienced the frustration of installing a new application on their computer, only to find that an existing application has suddenly stopped working. Many developers have spent countless hours trying to keep all necessary registry entries consistent in order to activate a COM class. Many deployment problems have been solved by the use of assemblies in the .NET Framework. Because they are self-describing components that have no dependencies on registry entries, assemblies enable zero-impact application installation. They also simplify uninstalling and replicating applications. Versioning Problems Currently two versioning problems occur with Win32 applications: Versioning rules cannot be expressed between pieces of an application and enforced by the operating system. The current approach relies on backward compatibility, which is often difficult to guarantee. Interface definitions must be static, once published, and a single piece of code must maintain backward compatibility with previous versions. Furthermore, code is typically designed so that only a single version of it can be present and executing on a computer at any given time. There is no way to maintain consistency between sets of components that are built together and the set that is present at run time. These two versioning problems combine to create DLL conflicts, where installing one application can inadvertently break an existing application because a certain software component or DLL was installed that was not fully backward compatible with a previous version. Once this situation occurs, there is no support in the system for diagnosing and fixing the problem. An End to DLL Conflicts Microsoft Windows 2000 began to fully address these problems. It provides two features that partially fix DLL conflicts: Windows 2000 enables you to create client applications where the dependent .dll files are located in the same directory as the application's .exe file. Windows 2000 can be configured to check for a component in the directory where the .exe file is located before checking the fully qualified path or searching the normal path. This enables components to be independent of components installed and used by other applications. Windows 2000 locks files that are shipped with the operating system in the System32 directory so they cannot be inadvertently replaced when applications are installed. The common language runtime uses assemblies to continue this evolution toward a complete solution to DLL conflicts.

The Assembly Solution


116

To solve versioning problems, as well as the remaining problems that lead to DLL conflicts, the runtime uses assemblies to do the following: Enable developers to specify version rules between different software components. Provide the infrastructure to enforce versioning rules. Provide the infrastructure to allow multiple versions of a component to be run simultaneously (called side-by-side execution). The Global Assembly Cache Assemblies can be either private or shared. By default, assemblies are private, and types contained within those assemblies are only available to applications in the same directory as the assembly. But every computer with the .NET Framework installed also has a global assembly cache (GAC) containing assemblies that are designed to be shared by multiple applications. There are three ways to add an assembly to the GAC:

Install them with the Windows Installer 2.0 Use the Gacutil.exe tool Drag and drop the assemblies to the cache with Windows Explorer

Note that in most cases you should plan to install assemblies to the GAC on end-user computers by using the Windows Installer. The gacutil.exe tool and the drag and drop method exist for use during the development cycle. You can view the contents of your global assembly cache by using Windows Explorer to navigate to the WINNT\assembly

117

You might also like