Upload
krishna-prasanna
View
208
Download
4
Tags:
Embed Size (px)
Citation preview
Pantech Academy of Technical Excellence
INTRODUCTION TO .NET
1
.Net Framework
Common Language Runtime(CLR)
Common Type System(CTS)
Common Language Specification(CLS)
Microsoft Intermediate Language(MSIL)
Pantech Academy of Technical Excellence
Common Language Runtime (CLR)
The Common Language Runtime (CLR) is a core component of Microsoft's .NET
initiative. It is Microsoft's implementation of the Common Language Infrastructure (CLI)
standard, which defines an execution environment for program code. The CLR runs a form of
byte code called the Common Intermediate Language (CIL, previously known as MSIL --
Microsoft Intermediate Language). Central to the .NET Framework is its runtime execution
environment, known as the Common Language Runtime (CLR) or the .NET runtime. Code
running under the control of the CLR is often termed managed code.
Developers using the CLR write code in a language such as C# or VB.NET. At
compile time, a .NET compiler converts such code into CIL code. At runtime, the CLR's just-
in-time compiler converts the CIL code into code native to the operating system.
Alternatively, the CIL code can be compiled to native code in a separate step prior to runtime.
This speeds up all later runs of the software as the CIL-to-native compilation is no longer
necessary. Compilation occurs in two steps in .NET:
1. Compilation of source code to IL
2. Compilation of IL to platform-specific code by the CLR
Although some other implementations of the Common Language Infrastructure run on
non-Windows operating systems, Microsoft's implementation runs only on Microsoft
Windows operating systems. The CLR allows programmers to ignore many details of the
specific CPU that will execute the program. It also provides other important services,
including the following:
Memory management
Thread management
Exception handling
Garbage collection
Security
2
Pantech Academy of Technical Excellence
Common Type System (CTS)
The Common Type System (CTS) is a standard that specifies how Type definitions
and specific values of Types are represented in computer memory. It is intended to allow
programs written in different programming languages to easily share information. As used in
programming languages, a Type can be described as a definition of a set of values. The CLI
and the CTS were created by Microsoft, and the Microsoft .NET framework is an
implementation of the standard.
Functions of the Common Type System
To establish a framework that helps enable cross-language integration, type safety,
and high performance code execution.
To provide an object-oriented model that supports the complete implementation of
many programming languages.
To define rules that languages must follow, which helps ensure that objects
written in different languages can interact with each other.
The CTS also defines the rules that ensure that the data types of objects written in
various languages are able to interact with each other.
Type categories
The common type system supports two general categories of types:
Value types
Value types directly contain their data, and instances of value types are either allocated on
the stack or allocated inline in a structure. Value types can be built-in (implemented by the
runtime), user-defined, or enumerations.
3
Pantech Academy of Technical Excellence
Reference types
Reference types store a reference to the value's memory address, and are allocated on the
heap. Reference types can be self-describing types, pointer types, or interface types. The type
of a reference type can be determined from values of self-describing types. Self-describing
types are further split into arrays and class types. The class types are user-defined classes,
boxed value types, and delegates.
Example
4
using System;
class Class1
{
public int Value = 0;
}
class Test
{
static void Main( )
{
int val1 = 0;
int val2 = val1;
val2 = 123;
Class1 ref1 = new Class1( );
Class1 ref2 = ref1;
ref2.Value = 123;
Console.WriteLine("Values: {0}, {1}", val1, val2);
Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value);
}
}
Pantech Academy of Technical Excellence
Common Language Specification
The Common Language Specification is a set of base rules to which any language
targeting the CLI (Common Language Infrastructure) should conform in order to interoperate
to other CLS-compliant languages. The CLS rules define a subset of the Common Type
System.
The Common Language Specification (CLS) works with the CTS to ensure language
interoperability. The CLS is a set of minimum standards that all compilers targeting .NET
must support. Since IL is a very rich language, writers of most compilers will prefer to
restrict the capabilities of a given compiler to only support a subset of the facilities offered by
IL and the CTS. That is fine, as long as the compiler supports everything that is defined in the
CLS. For example, let’s look at case sensitivity. IL is case-sensitive. Developers who work
with case-sensitive languages regularly take advantage of the flexibility this case sensitivity
gives them when selecting variable names. Visual Basic .NET, however, is not case sensitive.
The CLS works around this by indicating that CLS-compliant code should not expose any
two names that differ only in their case. Therefore, Visual Basic .NET code can work with
CLS-compliant code.
This example shows that the CLS works in two ways. First, it means that individual
compilers do not have to be powerful enough to support the full features of .NET—this
should encourage the development of compilers for other programming languages that
target .NET. Second, it provides a guarantee that, if you restrict your classes to only exposing
CLS-compliant features, then it is guaranteed that code written in any other compliant
language can use your classes. Within the private implementations of your classes, you can
write whatever non-CLS code you want, because code in other assemblies cannot access this
part of your code anyway. In general, the CLS won’t affect your C# code very much, because
there are very few non–CLS-compliant features of C# anyway. The Common Language
Specification (CLS) is a specification for creating or porting programming languages to that
they are .NET compatible. The CLS uses a Common Type System (CTS). This is a
component of the CLR and provides a common set of data types that are consistent between
all .NET languages.
5
Pantech Academy of Technical Excellence
Microsoft Intermediate Language (MSIL)
All .NET source code is compiled to IL. This IL is then converted to machine code at
the point where the software is installed, or at run-time by a Just-In- Time (JIT) compiler.
Microsoft Intermediate Language (MSIL) is a language used as the output of a number of
compilers (C#, VB, .NET, and so forth). The ILDasm (Intermediate Language Disassembler)
program that ships with the .NET Framework SDK (FrameworkSDK\Bin\ildasm.exe) allows
the user to see MSIL code in human-readable format. By using this utility, we can open
any .NET executable file (EXE or DLL) and see MSIL code.
The ILAsm program (Intermediate Language Assembler) generates an executable file
from the MSIL language. We can find this program in the WINNT\Microsoft.NET\
Framework\vn.nn.nn directory.
Any Visual C++ programmer starting with .NET development is interested in what
happens in the low level of the .NET Framework. Learning MSIL gives a user the chance to
understand some things that are hidden from a programmer working with C# or VB.NET.
Knowing MSIL gives more power to a .NET programmer. We never need to write programs
in MSIL directly, but in some difficult cases it is very useful to open the MSIL code in
ILDasm and see how things are done.
A MSIL reference in DOC format is available to a .NET developer and may be found
in the Framework SDK directory: FrameworkSDK\Tool Developers Guide\docs\Partition II
Metadata.doc (Metadata Definition and Semantics). In this file, I found a description of all
MSIL directives such as .entrypoint, .locals, and so on. FrameworkSDK\Tool Developers
Guide\docs\Partition III CIL.doc (CIL Instruction Set) contains a full list of the MSIL
commands.
6
Pantech Academy of Technical Excellence
7
Introduction to C#
What is C#?
How Does a C# Application Run?
Does C# have a Runtime Library?
What can I do with C#?
Pantech Academy of Technical Excellence
What is C#?
C# (pronounced C Sharp) is one of many .NET programming languages. It is object-
oriented and allows you to build reusable components for a wide variety of application types.
Microsoft introduced C# on June 26th, 2000 and it became a v1.0 product on Feb 13th 2002.
C# is an evolution of the C and C++ family of languages. However, it borrows features from
other programming languages, such as Delphi and Java. If you look at the most basic syntax
of C# and Java, the code looks very similar, but then again, the code looks a lot like C++. C#
is intended to be a simple, modern, general-purpose, Object oriented Programming Language.
It has an object-oriented syntax based on C++. The most recent version of the language is 3.0
which were released in conjunction with the .Net Framework 3.5 in 2007. The next proposed
version, 4.0, is in development.
In C#, Private is the default accessibility. The accessibility options are:
Public –accessible to all
Private-accessible to containing class
Protected-accessible to containing or derived class
Internal-accessible to code in same assembly
Protected internal means protected or internal
Type members in C# are:
Fields: The state of an object or type
Methods: Constructors, Functions, Properties (smart fields)
Members come in two basic forms
Instance - per object data and methods (default)
Static - per type data and methods (use the static keyword)
8
Pantech Academy of Technical Excellence
How Does a C# Application Run?
An important point is that C# is a "managed" language, meaning that it requires the
.NET Common Language Runtime (CLR) to execute. Essentially, as an application that is
written in C# executes, the CLR is managing memory, performing garbage collection,
handling exceptions, and providing many more services that you, as a developer, don't have
to write code for. The C# compiler produces Intermediate Language (IL), rather than machine
language, and the CLR understands IL. When the CLR sees the IL, it Just-In-Time (JIT)
compiles it, method by method, into compiled machine code in memory and executes it. As
mentioned previously, the CLR manages the code as it executes.
Because C# requires the CLR, you must have the CLR installed on your system. All
new Windows operating systems ship with a version of the CLR and it is available via
Windows Update for older systems. The CLR is part of the .NET, so if you see updates for
the .NET Framework Runtime, it contains the CLR and .NET Framework Class Library
(FCL). It follows that if you copy your C# application to another machine, then that machine
must have the CLR installed too.
Does C# have a Runtime Library?
Instead of a runtime library (such as APIs for file I/O, string handling, etc.) being
dedicated to a single language, .NET ships with a .NET Framework Class Library (FCL),
which includes literally tens of thousands of reusable objects. Since all .NET languages target
the CLR with the same IL, all languages can use the FCL. This shortens the learning curve
for any developer moving from one .NET language to another, but also means that Microsoft
is able to add many more features because there is only one FCL, rather than a separate
implementation for common features in every programming language.
Similarly, 3rd party software vendors can write managed code that any .NET
developer, regardless of language, can use. In addition to all of the services you would expect
of a runtime library, such as collections, file I/O, networking, etc., the FCL includes the APIs
for all of the other .NET technologies, such as for desktop and Web development.
9
Pantech Academy of Technical Excellence
What can I do with C#?
C# is only a programming language. However, because C# targets the CLR and has
access to the entire FCL, there's a lot you can do. You can write desktop applications with
Windows Forms, Windows Presentation Foundation (WPF), or even Console applications.
For the Web, you can write ASP.NET and Silverlight applications in addition to enabling
systems to communicate with Web Services with Windows Communications Foundation
(WCF). When you need to access data, there is both ADO.NET and LINQ. Of course, these
are only a few of the technologies available and as a general purpose programming language;
you can do a lot more than this with C#.
10
Pantech Academy of Technical Excellence
C#.NET LANGUAGE BASICS
11
Data Types and Variables
Data Types
Integer Types
Boolean
Floating Point Types
Decimal Types
Character and Strings
Variables
Static Variables
Variable of Instance
Array’s Elements
Local Variables
Constants
Pantech Academy of Technical Excellence
Data Types
Integer Types:
C# supports eight predefined integer types.
C# supports a rich palette of signed and unsigned integer types ranging in size from 8 to 64
bits. In C#, an int is always a 32-bit signed integer.
Boolean Types:
The C# bool type is used to contain Boolean values of either true or false.
Name CTS Type Values
bool System. Boolean True or False
12
Name CTS Type Description Range (min: max)sbyte System.SByte 8-bit signed integer -128:127 (-27:27-1)short System.Int16 16-bit signed integer -32,768:32,767 (-215:215-1)int System.Int32 32-bit signed integer -2, 147, 483,64 :
2,147,483,647(-231:231-1)long System.Int64 64-bit signed integer -9,223,372,036,854,775,808:
9,223,372,036,854,775,807 (-263:263-1)
Byte System.Byte 8-bit unsigned integer 0:255 (0:28-1)ushort System.UInt16 16-bit unsigned integer 0:65,535 (0:216-1)uint System.UInt32 32-bit unsigned integer 0:4,294,967,295 (0:232-1)ulong System.UInt64 64-bit unsigned integer 0:18,446,744,073,709,551,615
(0:264-1)
Pantech Academy of Technical Excellence
Floating Point and Decimal Types:
A C# Floating Point type is either float or double. It’s used when you need to perform
operations requiring fractional representations. Decimal Types should be used when
representing financial or money values.
Type Size (in bits) Precision Range
Float 32 7 digits 1.5 x 10-45 to 3.4 x 1038
Double 64 15-16 digits 5.0 x 10-324 to 1.7 x 10308
Decimal 128 28-29 decimal places 1.0 x 10-28 to 7.9 x 1028
Character and String Types:
A char type is used to represent Unicode Characters. A variable of type char
represents a single 16-bit Unicode Character. There are no implicit conversions from char to
other data types available. That means treating a char variable just as another integral data
type is not possible.
A string is a sequence of text characters. You typically create a string with a string
literal, enclosed in quotes: "This is an example of a string". C# has a special syntax where
characters can be escaped to represent non-printable characters.
Below Table shows a list of common escape sequences.
Escape Sequence Meaning
\' Single Quote
\" Double Quote
\\ Backslash
\0 Null, not the same as the C# null value
\a Bell
\b Backspace
\f form Feed
13
Pantech Academy of Technical Excellence
\n Newline
\r Carriage Return
\t Horizontal Tab
\v Vertical Tab
Variables
Variables in c# using the following syntax.
Syntax:
datatype identifier;
For Example:
int i;
Once it has been declared, We can assign a value to the variable using the assignment
operator,=;
Eg: i=10;
We can also declare the variable and initialize its value at the same time.
Static Variables:
Static Variables will be alive throughout the life of a program. It can be declared
using static modifier.
Variable of Instance:
An Instance variable is a variable declared without static modifier. Usually it is declared
inside a class or structure definition.
14
Pantech Academy of Technical Excellence
Array Elements:
An array is a data structure that contains a number of variables. The new operator is
used to create the array and initialize the array elements to their default values. C# supports:
One-dimensional arrays,
Multidimensional arrays (rectangular arrays)
Arrays of arrays (jagged arrays).
The array indexes start as zero. When declaring arrays, the square bracket [ ] must
come after the type, not the identifiers, In C#, arrays are derived from the System.Array class.
Notes:
One-dimensional Arrays
For Example:
int[ ] table.
int[ ] numbers = new int[20]; we can initialize arrays very simply:
int[ ] numbers = {0, 1, 2, 3, 5}; This is identical to the complete initialization
statement:
int [ ] numbers = new int[ ] {0, 1, 2, 3, 5};
Multi-dimensional Arrays
Arrays can have more one dimension. C# also supports multi-dimensional arrays.
For Example:
Two-dimensional arrays: int[,] y;
15
Arrays in C# use square brackets, not parentheses. C++ users will be familiar with the square
brackets, but should check the code we present here carefully because C# syntax for actually
declaring array variables is not the same as C++ syntax
Pantech Academy of Technical Excellence
Three-dimensional arrays: int[,,] y;
Figure: Usage Of multi-dimensional arrays
Jagged Arrays
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."
For Example:
16
Using System;Class multi{
Public static void main(){int[ ,]x;x=new int[4,4];x[0,0] = 1;x[1,1] = 1;x[2,2] = 1;x[3,3] = 1;Console.WriteLine(x[0,0]+” “+x[0,1]+” “+x[0,2]+” “x[0,3]);Console.WriteLine(x[1,1]+” “+x[1,1]+” “+x[1,2]+” “x[1,3]);Console.WriteLine(x[2,2]+” “+x[2,1]+” “+x[2,2]+” “x[2,3]);Console.WriteLine(x[3,3]+” “+x[3,1]+” “+x[3,2]+” “x[3,3]);}
}
Pantech Academy of Technical Excellence
Figure: Usage of Jagged arrays
Local Variables and Constants
Local Variables can optionally be constant (const). Constant Local Variables are
stored in the assembly data region, While non-constant local variables are stored on (or
17
Using System;Class Jagged{
int [ ][ ] x=new int[2][ ];void set ( ){x[0]=new int[2];x[1]=new int[2];x[0][0]=1;x[0][1]=2;}void show ( ){
for (int i=0; i<2; i++){
for (j=0; j<2; j++){Console.WriteLine(x[i] [j]);}
}}
Public static void main ( ){Jagged j=new Jagged ( );j.set( );j.show( );}
}
Pantech Academy of Technical Excellence
referenced from) the stack. They thus have both a scope and extend of the method or
statement block that declares them.
The const keyword is used to modify a declaration of a field or local variable. It
specifies that the value of the field or the local variable is constant, which means it cannot be
modified.
For example:
const int x = 0;
public const double gravitationalConstant = 6.673e-11;
private const string productName = "Visual C#";
The type of a constant declaration specifies the type of the members introduced by the
declaration. A constant expression must yield a value of the target type or of a type that can
be implicitly converted to the target type. A constant expression is an expression that can be
fully evaluated at compile time. Therefore, the only possible values for constants of reference
types are string and null. The constant declaration can declare multiple constants, such as:
public const double x = 1.0, y = 2.0, z = 3.0;
The static modifier is not allowed in a constant declaration. A constant can participate
in a constant expression, as follows:
public const int c1 = 5;
public const int c2 = c1 + 100;
Notes
18
The readonly keyword is different from the const keyword. A const field can only be
initialized at the declaration of the field. A readonly field can be initialized either at the
declaration or in a constructor. Therefore, readonly fields can have different values depending
on the constructor used. Also, while a const field is a compile-time constant, the readonly
field can be used for run-time constants, as in this line:
public static readonly uint l1 = (uint)DateTime.Now.Ticks;
Pantech Academy of Technical Excellence
19
Type Conversions
What is Conversion?
Widening Conversion
Narrowing Conversion
Boxing and Un-Boxing Conversion
Interface Conversion
Implicit Conversion
Explicit Conversion
Pantech Academy of Technical Excellence
What is Conversions?
Conversion is the process of changing a value from one type to another. Conversions
may either be widening or narrowing.
Widening Conversions
When a particular data type is converted to a data type that is capable of storing more
data than the source data type is referred to as widening conversion. The simple examples of
widening conversion can be Numeric type conversions in the direction Byte, Short, Integer,
Long, Decimal, Single, Double, here every type listed to the right of a type is capable of
storing more data than its left listed type. If we takes the case of reference data types then
Conversions from any derived type to one of its base types is also referred to as widening
conversion. Widening conversion never cause overflow.
Narrowing Conversions
A narrowing conversion occurs when a variable is converted to a type that is not
capable of storing the data held by the source data type variable. These conversions are
known to possibly lose information and conversions across domains of types sufficiently
different to merit narrowing notation. These means narrowing conversions entail loss of
information and may fail.
Following list defines different types of conversion:
Identity conversion
An identity conversion converts from any type to the same type. This conversion
exists only such that an entity that already has a required type can be said to be convertible to
that type.
20
Pantech Academy of Technical Excellence
Numeric conversions: Some examples of numeric conversions are:
From sbyte to short, int, long, float, double, or decimal.
From byte to short, ushort, int, uint, long, ulong, float, double, or decimal.
From short to int, long, float, double, or decimal.
Etc.
Reference conversions
These are conversion from one reference type to another. Reference conversions,
implicit or explicit, never change the referential identity of the object being converted. In
other words, while a reference conversion may change the type of the reference, it never
changes the type or value of the object being referred to.
Boxing and Un-Boxing Conversions
A boxing conversion permits any value-type to be converted to the type object or to
any interface-type implemented by the value-type. Boxing a value of a value-type consists of
allocating an object instance and copying the value-type value into that instance. Unboxing is
an explicit conversion from the type object to a value type or from an interface type to a value
type that implements the interface.
An unboxing operation consists of:
- Checking the object instance to make sure it is a boxed value of the given value type.
- Copying the value from the instance into the value-type variable.
Interface Conversion
Converting an interface to a value type consists of copying the value in the interface
instance to the target variable (which may be a temporary).
21
Pantech Academy of Technical Excellence
Implicit Conversion
The implicit keyword is used to declare an implicit user-defined type conversion
operator.
Syntax:
class SomeType
{
public static implicit operator int(SomeType typ)
{
// code to convert from SomeType to int
}
}
Implicit conversion operators can be called implicitly, without being specified by explicit
casts in the source code.
SomeType x;
int i = x; // implicitly call SomeType’s implicit conversion function that returns int value.
Since the implicit conversion occurs with out user specifying the casting type, may
sometimes lead to confusing code.
The following implicit conversions are classified as standard implicit conversions:
Identity conversions
Implicit numeric conversions
Implicit reference conversions
22
Pantech Academy of Technical Excellence
Boxing conversions
Implicit constant expression conversions
The standard implicit conversions specifically exclude user-defined implicit conversions.
// hold any value an int can hold, and more!
int num = 2147483647;
long bigNum = num;
Explicit Conversion
Explicit conversion is performed using casting. Casting occurs when you prefix a
conversion with a data type that defines the type of the conversion you want to perform. The
explicit keyword is used to declare an explicit user-defined type conversion operator. The set
of explicit conversions includes all implicit conversions.
Syntax:
class SomeType
{
public static explicit operator SomeType (int i)
{
// code to convert from int to SomeType
}
}
Unlike implicit conversion, explicit conversion operators must be invoked via a cast.
int i;
SomeType x = (SomeType) i;
Omitting the cast results in a compile-time error. Using explicit prevents the compiler from
silently invoking the conversion operation with possibly unforeseen consequences.
23
Pantech Academy of Technical Excellence
The following conversions are classified as explicit conversions:
All implicit conversions.
Explicit numeric conversions.
Explicit enumeration conversions.
Explicit reference conversions.
Explicit interface conversions.
Unboxing conversions.
User-defined explicit conversions
For Example:
24
class Test
{
static void Main()
{
double x = 1234.7;
int a;
// Cast double to int.
a = (int)x;
System. Console.WriteLine (a);
}
}
// Output: 1234
Pantech Academy of Technical Excellence
25
Boxing and Un-Boxing
What is boxing and Un-Boxing?
Example Program
Boxing Conversion
Un-Boxing Conversion
Pantech Academy of Technical Excellence
What is Boxing and Un-Boxing?
Boxing and unboxing is an essential concept in C# type system. With Boxing and
unboxing one can link between value-types and reference-types by allowing any value of a
value-type to be converted to and from type object. Boxing and unboxing enables a unified
view of the type system wherein a value of any type can ultimately be treated as an object.
Converting a value type to reference type is called Boxing. Unboxing is an explicit
operation. C# provides a unified type system. All types including value types derive from the
type object. It is possible to call object methods on any value, even values of primitive types
such as int.
Example for Boxing and Un-Boxing
Figure: Usage Of Boxing and Un-Boxing
Here, in this example an int value can be converted to object and back again to int.
This example shows both boxing and unboxing. When a variable of a value type
needs to be converted to a reference type, an object box is allocated to hold the value, and the
value is copied into the box. Unboxing is just the opposite. When an object box is cast back
to its original value type, the value is copied out of the box and into the appropriate storage
location.
26
class Test{
static void Main() {
int i = 1;object o = i; // boxingint j = (int) o; // unboxing
}}
Pantech Academy of Technical Excellence
Boxing Conversions
A Boxing conversion permits any value-type to be implicitly converted to the type
object or to any interface-type implemented by the value-type. Boxing a value of a value-type
consists of allocating an object instance and copying the value-type value into that instance.
For example any value-type G, the boxing class would be declared as follows:
class vBox
{
G value;
G_Box(G g)
{
value = g;
}
}
Boxing of a value v of type G now consists of executing the expression new G_Box (v), and
returning the resulting instance as a value of type object.
Thus, the statements
int i=12;
object box = i;
Conceptually correspond to
int i=12;
object box = new int_Box(i);
Boxing classes like G_Box and int_Box above don’t actually exist and the dynamic
type of a boxed value isn’t actually a class type. Instead, a boxed value of type G has the
dynamic type G, and a dynamic type check using this operator can simply reference type G.
27
Pantech Academy of Technical Excellence
For Example:
int i=12;
object box=i;
if (box is int)
{
Console.Write("Box contains an int");
}
will output the string. ”Box contains an int”.
A boxing conversion implies making a copy of the value being boxed. This is different from
a conversion of a reference-type to type object, in which the value continues to reference the
same instance and simply is regarded as the less derived type object.
For example, given the declaration
struct Point
{
public int x, y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
The following statements
Point p=new Point (10,10);
object box =p;
p.x=20;
Console.Write(((Point)box).x);
28
Pantech Academy of Technical Excellence
will output the value 10 on the console because the implicit boxing operation that occurs in
the assignment of p to box causes the value of p to be copied. Had Point instead been
declared a class, the value 20 would be output because p and box would reference the same
instance.
Un-Boxing Conversion
An unboxing conversion permits an explicit conversion from type object to any value-
type or from any interface-type to any value-type that implements the interface-type. An
unboxing operation consists of first checking that the object instance is a boxed value of the
given value-type, and then copying the value out of the instance. Unboxing conversion of an
object box to a value-type G consists of executing the expression ((G_Box)box).value.
Thus, the statements object box =12;
int i = (int) box;
Conceptually correspond to
Object box=new int_Box (12);
int i = ((int_Box) box).value;
For an unboxing conversion to a given value-type to succeed at run-time, the value of the
source argument must be a reference to an object that was previously created by boxing a
value of that value-type. If the source argument is null or a reference to an incompatible
object, an InvalidCastException is thrown.
29
Pantech Academy of Technical Excellence
30
Conditional Statements
If-Else Statement
Nested If Statement
Switch Statement
Pantech Academy of Technical Excellence
Conditional Statements
Conditional statements allow us to branch our code depending on whether certain
conditions are met or on the value of an expression. C# has two constructs for branching code
— the if statement, which allows us to test whether a specific condition is met, and the switch
statement, which allows us to compare an expression with a number of different values.
If-Else Statement
The most commonly used conditional statement is the If… Else block, in which a
statement is evaluated to true or false and depending on the result, a different section of code
is executed. In this sample the statement checks if the condition is true, if it is it executes the
first block of code. If the condition is false then it executes the second.
SYNTAX:
if (condition)
statement(s)
else
statement(s)
Example:
Figure: Sample If… Else statement
Nested If Statement
31
int x = 5;
if (x >= 3)
{
Console.WriteLine ("X is greater than or equal to 3.");
}
else
{
Console.WriteLine ("X is less than 3.");
}
Pantech Academy of Technical Excellence
If you have multiple scenarios, you can nest if… else statements:
Example:
Figure: Nested If Statement
Switch Statement
The switch statement is a control statement that handles multiple selections and
enumerations by passing control to one of the case statements within its body .The
switch...case statement is good for selecting one branch of execution from a set of mutually
exclusive ones. It will be familiar to C++ and Java programmers and is similar to the Select
Case statement in Visual Basic. It takes the form of a switch argument followed by a series of
case clauses. When the expression in the switch argument evaluates to one of the values
beside a case clause, the code immediately following the case clause executes. We mark the
end of the code for each case using the break statement. We can also include a default case in
the switch statement, which will execute if the expression evaluates to none of the other
cases.
The following switch statement tests the value of the integer A variable:
Example:
32
if (dayOfWeek = Monday)
Console.WriteLine ("Today is Monday");
else if (dayOfWeek = Tuesday)
Console.WriteLine ("Today is Tuesday");
else if (dayOfWeek = Wednesday)
Console.WriteLine ("Today is Wednesday");
else if (dayOfWeek = Thursday)
Console.WriteLine ("Today is Thursday");
else if (dayOfWeek = Friday)
Console.WriteLine ("Today is Friday");
Pantech Academy of Technical Excellence
Notes:
33
switch (integerA){case 1:Console.WriteLine (“integerA =1”);break;case 2:Console.WriteLine (“integerA =2”);break;case 3:Console.WriteLine (“integerA =3”);break;default:Console.WriteLine (“integerA is not 1, 2, or 3”);break;}
The case values must be constant expressions; variables are not permitted. Though the switch...case statement should be familiar to C and C++ programmers, C#’s switch.. case is a bit safer than its C++ equivalent. Specifically, it prohibits fall-through conditions in almost all cases. This means that if a case clause is fired early on in the block, later clauses cannot be fired unless you use a goto statement to mark that you want them fired too. The compiler enforces this restriction by flagging every case clause that is not equipped with a break statement as an error similar to this: Control cannot fall through from one case label (‘case 2:’) to another 49
Pantech Academy of Technical Excellence
34
Looping Statements
while Loop
do-while Loop
for Loop
foreach Loop
Pantech Academy of Technical Excellence
Looping Statement
C# provides four different loops for, while, do...while, and foreach that allow us to
execute a block of code repeatedly until a certain condition is met.
C# provides a number of the common loop statements:
while
do-while
for
foreach
While Loop
A 'while' loop executes a statement, or a block of statements wrapped in curly braces,
repeatedly until the condition specified by the boolean expression returns false. The
statements can be any valid C# statements.
Syntax:
while (condition)
statement[s];
For Example:
Produces the following output: 0 1 2
do-while Loop
35
int a = 0;
while (a < 3)
{
System. Console.WriteLine (a);
a++;
}
do
{
Console.WriteLine(number);
number = number + 1;
} while(number < 5);
Pantech Academy of Technical Excellence
The do...while loop is the post-test version of the while loop. It does the same thing
with the same syntax as do...while in C++ and Java, and the same thing as Loop...While in
Visual Basic. This means that the loop’s test condition is evaluated after the body of the loop
has been executed. Consequently, do...while loops are useful for situations in which a block
of statements must be executed at least one time.
Syntax:
do
{
statement[s];
}while (condition);
For Example:
for Loop
C# for loops provides a mechanism for iterating through a loop where we test whether
a particular Condition holds before we perform iteration.
Syntax:
for (initializer; condition; iterator)
statement(s)
Where:
36
do
{
Console.WriteLine(number);
number = number + 1;
} while(number < 5);
Pantech Academy of Technical Excellence
The initializer is the expression evaluated before the first loop is executed (usually
initializing a local variable as a loop counter).
The condition is the expression that is checked before each new iteration of the
loop (this must evaluate to true for another iteration to be performed).
The iterator is an expression that will be evaluated after each iteration (usually
incrementing the loop counter). The iterations end when the condition evaluates to
false.
The for loop is a so-called pre-test loop, because the loop condition is evaluated
before the loop statements are executed, and so the contents of the loop won’t be executed at
all if the loop condition is false. The for loop is excellent for repeating a statement or a block
of statements for a predetermined number of times. The following example is typical of the
use of a for loop.
foreach Loop
The foreach statement repeats a group of embedded statements for each element in an
array or an object collection. The foreach statement is used to iterate through the collection
to get the desired information, but should not be used to change the contents of the collection
to avoid unpredictable side effects.
Notes:
Figure: Usage Of foreach Loop
37
class ForEachTest
{
static void Main(string[ ] args)
{
int[ ] fibarray = new int[ ] { 0, 1, 2, 3, 5, 8, 13 };
foreach (int i in fibarray)
{
System.Console.WriteLine(i);
}
}
}
The embedded statements continue to execute for each element in the array or collection. After
the iteration has been completed for all the elements in the collection, control is transferred to the
next statement following the foreach block. At any point within the foreach block, you can break
out of the loop using the break keyword, or step directly to the next iteration in the loop by using
the continue keyword. A foreach loop can also be exited by the goto, return, or throw statements.
Pantech Academy of Technical Excellence
38
Pantech Academy of Technical Excellence
39
Methods in C#
Method.
Method Parameters
Return Values
Pantech Academy of Technical Excellence
Method
A Method is a code block containing a series of statements. In C#, every executed
instruction is done so in the context of a method. Methods are declared within a class or struct
by specifying the access level, the return value, the name of the method, and any method
parameters. Method parameters are surrounded by parentheses, and separated by commas.
Empty parentheses indicate that the method requires no parameters. All C# programs are
constructed from a number of classes and almost all the classes will contain methods. A class
when instantiated is called an object. Object-oriented concepts of programming say that the
data members of each object represent its state and methods represent the object behavior.
For Example:
The following Class contains three methods:
Calling a method on an object is similar to accessing a field. After the object name, add a
period, the name of the method, and parentheses. Arguments are listed within the
parentheses, and separated by commas. The methods of the Motorcycle class can therefore be
called like this:
Motorcycle moto = new Motorcycle();
moto.StartEngine();
moto.AddGas(15);
moto.Drive(5, 20);
40
class Motorcycle
{
public void StartEngine( ) { }
public void AddGas(int gallons) { }
public int Drive(int miles, int speed) { return 0; }
}
Pantech Academy of Technical Excellence
Method Parameters
In the above example, passing arguments to a method is simply providing them in the
parentheses when calling a method. To the method being called, the incoming arguments are
called parameters.
The parameters a method receives are also provided in a set of parentheses, but the
type and a name for each parameter must be specified. The name does not have to be the
same as the argument.
For Example:
Here a method called PassesInteger passes an argument to a method called
TakesInteger. Within PassesInteger, the argument is named fortyFour, but in TakesInteger,
this is a parameter named i. This parameter exists only within the TakesInteger method. Any
number of other variables can be named i, and they can be of any type, so long as they are not
parameters or variables declared inside this method.
By default, when a value type is passed to a method, a copy is passed instead of the
object itself. Because they are copies, any changes made to the parameter have no effect
within the calling method. Value types get their name from the fact that a copy of the object
is passed instead of the object itself. The value is passed, but not the same object.
41
public static void PassesInteger()
{
int fortyFour = 44;
TakesInteger(fortyFour);
}
static void TakesInteger(int i)
{
i = 33;
}
Pantech Academy of Technical Excellence
Passing Parameters
In C#, parameters can be passed either by value or by reference. Passing parameters
by reference allows function members, methods, properties, indexers, operators, and
constructors, to change the value of the parameters and have that change persist. To pass a
parameter by reference, use the ref or out keyword.
For Example:
Passing Value-Type Parameters
A value-type variable contains its data directly as opposed to a reference-type
variable, which contains a reference to its data. Therefore, passing a value-type variable to a
method means passing a copy of the variable to the method. Any changes to the parameter
that take place inside the method have no affect on the original data stored in the variable. If
you want the called method to change the value of the parameter, you have to pass it by
reference, using the ref or out keyword.
Example:
Passing Value Types by Value
The following example demonstrates passing value-type parameters by value. The
variable n is passed by value to the method SquareIt. Any changes that take place inside the
method have no affect on the original value of the variable.
42
// Passing by value
static void Square(int x)
{
// code...
}
// Passing by reference
static void Square(ref int x)
{
// code...
}
Pantech Academy of Technical Excellence
Output
Output
The value before calling the method: 5
The value inside the method: 25
The value after calling the method: 5
Passing Reference-Type Parameters
A variable of a reference type does not contain its data directly; it contains a reference
to its data. When you pass a reference-type parameter by value, it is possible to change the
data pointed to by the reference, such as the value of a class member. However, you cannot
change the value of
the reference itself; that is, you cannot use the same reference to allocate memory for a new
class and have it persist outside the block.
43
class PassingValByVal
{
static void SquareIt(int x)
// The parameter x is passed by value.
// Changes to x will not affect the original value of x.
{
x *= x;
System.Console.WriteLine("The value inside the method: {0}", x);
}
static void Main ( )
{
int n = 5;
System.Console.WriteLine("The value before calling the method: {0}", n);
SquareIt(n); // Passing the variable by value.
System.Console.WriteLine("The value after calling the method: {0}", n);
}
}
Pantech Academy of Technical Excellence
Example:
Passing Reference Types by Value
The following example demonstrates passing a reference-type parameter, arr, by
value, to a method, Change. Because the parameter is a reference to arr, it is possible to
change the values of the array elements. However, the attempt to reassign the parameter to a
different memory location only works inside the method and does not affect the original
variable, arr.
Output
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: -3
Return Values
Methods can return a value to the caller. If the return type, the type listed before the
method name, is not void, then the method can return the value using the return keyword. A
statement with the keyword return followed by a value that matches the return type will
44
class PassingRefByVal { static void Change(int[ ] pArray) { pArray[0] = 888; // This change affects the original element. pArray = new int[5] {-3, -1, -2, -3, -4}; // This change is local. System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]); }
static void Main() { int[ ] arr = {1, 4, 5}; System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr [0]);
Change(arr); System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr [0]); }}
Pantech Academy of Technical Excellence
return that value to the method caller. The return keyword also stops the execution of the
method. If the return type is void, a return statement with no value is still useful to stop the
execution of the method. Without the return keyword, the method will stop executing when it
reaches the end of the code block. Methods with a non-void return type are required to use
the return keyword to return a value.
For Example:
These two methods use the return keyword to return integers:
To use a value returned from a method, the calling method can use the method call itself
anywhere a value of the same type would suffice. You can also assign the return value to a
variable.
For example:
The following two code examples accomplish the same goal:
45
class SimpleMath{ public int AddTwoNumbers(int number1, int number2) { return number1 + number2; }
public int SquareANumber(int number) { return number * number; }}
int result = obj.AddTwoNumbers(1, 2);
obj.SquareANumber(result);
obj.SquareANumber(obj.AddTwoNumbers(1, 2));
Pantech Academy of Technical Excellence
46
Properties
Properties.
Property Declaration
Example Program
Overview of Properties
Pantech Academy of Technical Excellence
Properties
Properties are members that provide a flexible mechanism to read, write, or compute
the values of private fields. Properties can be used as if they are public data members, but
they are actually special methods called accessors. This enables data to be accessed easily
and still helps promote the safety and flexibility of methods.
Property Declaration
Property declaration takes one of the following forms:
[attributes] [modifiers] type identifier {accessor-declaration}
[attributes] [modifiers] type interface-type.identifier {accessor-declaration}
where:
attributes (Optional) -Additional declarative information.
modifiers(Optional) - The allowed modifiers are new, static, virtual, abstract,
override
o and a valid combination of the four access modifiers.
type - The property type, which must be at least as accessible as the
o property itself.
identifier - The Property name
accessor-declaration - declaration of the property accessors, which are used to read
and
o write the property.
interface-type - the interface in a fully qualified property name.
Example for Property
A property declared using the static modifier is classified as a static property;
otherwise, it is classified as an instance property. Like other static members, a static property
is not associated with a specific instance and cannot be referenced through an instance.
Instead, it is associated with the type and can only be referenced through the type name
47
Pantech Academy of Technical Excellence
Output:
Employee number: 101
Employee name: Claude Vige
48
using System;public class Employee { public static int numberOfEmployees; private static int counter; private string name; public string Name // A read-write instance property: { get { return name; } set { name = value; } } public static int Counter // A read-only static property: { get { return counter; } } // Constructor: public Employee() { // Calculate the employee's number: counter = ++counter + numberOfEmployees; }}public class MainClass{ public static void Main() { Employee.numberOfEmployees = 100; Employee e1 = new Employee(); e1.Name = "Claude Vige"; Console.WriteLine("Employee number: {0}", Employee.Counter); Console.WriteLine("Employee name: {0}", e1.Name); }}
Pantech Academy of Technical Excellence
Properties Overview
Properties enable a class to expose a public way of getting and setting values, while
hiding implementation or verification code.
A get property accessor is used to return the property value, and a set accessor is used
to assign a new value. These accessors can have different access levels.
The value keyword is used to define the value being assigned by the set accessor.
Properties that do not implement a set accessor are read only.
For simple properties that require no custom accessor code, consider the option of
using auto-implemented properties.
Arrays
An array is a data structure that contains a number of variables. The new operator is
used to create the array and initialize the array elements to their default values.
C# supports:
One-dimensional arrays,
Multidimensional arrays (rectangular arrays)
Arrays of arrays (jagged arrays).
We discussed this topic previously please refer it.
49
Pantech Academy of Technical Excellence
50
Indexers
Indexer
Indexer Declaration
Example for Indexer
Overview of Indexer
Comparison between properties and Indexer
Pantech Academy of Technical Excellence
Indexers
Indexers allow instances of a class or struct to be indexed just like arrays. Indexers
resemble properties except that their accessors take parameters.
Indexer Declaration
Indexers allow you to index a class or a struct instance in the same way as an array.
To declare an indexer, use the following:
[attributes] [modifiers] indexer-declarator {accessor-declarations}
The indexer-declarator takes one of the forms:
type this [formal-index-parameter-list]
type interface-type.this [formal-index-parameter-list]
The formal-index-parameter takes the form:
[attributes] type identifier
where:
attributes (Optional) -Additional declarative information.
modifiers (Optional) -Allowed modifiers are new, virtual, sealed, override,
abstract, extern, and a valid combination of the
four access
modifiers
indexer-declarator - Includes the type of the element introduced by the
indexer, this, and the formal-index-parameter-
list. If the indexer is an explicit interface
member implementation, the interface-type is
included.
type -A type name.
interface-type -The interface name.
formal-index-parameter-list -Specifies the parameters of the indexer. The parameter
51
Pantech Academy of Technical Excellence
includes optional attributes, the index type, and the
index identifier. At least one parameter must be
specified. The parameter modifiers out and ref are not
allowed.
accessor-declarations -The indexer accessors, which specify the executable
statements associated with reading and writing
indexer
elements
identifier -The parameter name.
get Accessor
The get accessor body of an indexer is similar to a method body. It returns the type of
the indexer. The get accessor uses the same formal-index-parameter-list as the indexer.
For Example:
get
{
return myArray[index];
}
set Accessor
The set accessor body of an indexer is similar to a method body. It uses the same
formal-index-parameter-list as the indexer, in addition to the value implicit parameter.
For Example:
set
{
myArray[index] = value;
}
Notes:
52
Pantech Academy of Technical Excellence
Example for Indexer
Output:
53
The type of an indexer and each of the types referenced in the formal-index-parameter-list must be
at least as accessible as the indexer itself. The signature of an indexer consists of the number and
types of its formal parameters. It does not include the indexer type or the names of the formal
parameters. If you declare more than one indexer in the same class, they must have different
signatures. An indexer value is not classified as a variable; therefore, it is not possible to pass an
indexer value as a ref or out parameter.
// cs_keyword_indexers.csusing System;class IndexerClass { private int [ ] myArray = new int[100]; public int this [int index] // Indexer declaration { get { // Check the index limits. if (index < 0 || index >= 100) return 0; else return myArray[index]; } set { if (!(index < 0 || index >= 100)) myArray[index] = value; } }}public class MainClass { public static void Main() { IndexerClass b = new IndexerClass(); // Call the indexer to initialize the elements #3 and #5. b[3] = 256; b[5] = 1024; for (int i=0; i<=10; i++) { Console.WriteLine ("Element #{0} = {1}", i, b[i]); } }}
Pantech Academy of Technical Excellence
Element #0 = 0
Element #1 = 0
Element #2 = 0
Element #3 = 256
Element #4 = 0
Element #5 = 1024
Element #6 = 0
Element #7 = 0
Overview of Indexer
Indexers enable objects to be indexed in a similar manner to arrays.
A get accessor returns a value. A set accessor assigns a value.
This keyword is used to define the indexers.
The value keyword is used to define the value being assigned by the set indexer.
Indexers do not have to be indexed by an integer value; it is up to you how to define
the specific look-up mechanism.
Indexers can be overloaded.
Indexers can have more than one formal parameter, for example, when accessing a
two-dimensional array.
Comparison between Properties and Indexer
Indexers are similar to properties. Except for the differences shown in the following
table, all of the rules defined for property accessors apply to indexer accessors as well.
Property Indexer
54
Pantech Academy of Technical Excellence
Identified by its name. Identified by its signature.
Accessed through a simple name or a member access. Accessed through an element access.
Can be a static or an instance member. Must be an instance member.
A get accessor of a property has no parameters A get accessor of an indexer has the same
formal parameter list as the indexer.
A set accessor of a property contains the implicit
value parameter.
A set accessor of an indexer has the same
formal parameter list as the indexer, in
addition to the value parameter.
55
Pantech Academy of Technical Excellence
Structure
56
Structures and Enumerations
Structures
Example for Structure
Structure Features
Enumeration
Example for Enumeration
Pantech Academy of Technical Excellence
A struct type is a value type that is typically used to encapsulate small groups of
related variables, such as the coordinates of a rectangle or the characteristics of an item in an
inventory.
The following example shows a simple struct declaration:
public struct Book
{
public decimal price;
public string title;
public string author;
}
Example:
Output:
4
Inside aMethod
57
class TestStruct { public static void Main() { SimpleStruct st = new SimpleStruct(4); System.Console.WriteLine(st.i); st.aMethod(); }}
struct SimpleStruct { public int i; public SimpleStruct(int j) { i=j; }
public void aMethod() { System.Console.WriteLine("Inside aMethod");
}
}
Pantech Academy of Technical Excellence
Notes:
Features of Structures
A structure or struct can contain only fields, methods, Etc.,
In struct we can't initialize the variable whereas in class it can be possible
It must be initialized either through function or using object
Fields can't be given initial values at that time of creation.
struct can't have a constructor.
struct can't support the concept of inheritance
We can derive a structure from an interface
We can't inherit from sealed class
A structure could not inherit from a class, the reverse is also true
You can't declare a struct as sealed, because by default it is sealed
A structure declare as abstract can't be used as a base class, i.e., be used in a
derivation
No destructor for struct
58
Structs can also contain constructors, constants, fields, methods, properties, indexers, operators,
events, and nested types, although if several such members are required, you should consider
making your type a class instead. Structs can implement an interface but they cannot inherit from
another struct. For that reason, struct members cannot be declared as protected.
Pantech Academy of Technical Excellence
struct are created on the stack and die when we reach the closing brace
Enumerations
An enumeration (also named enum) is a user-defined integer type. When we declare
an enumeration, we specify a set of acceptable values that instances of that enumeration can
contain. Not only that, but we can give the values user-friendly names. If, somewhere in our
code, we attempt to assign a value that is not in the acceptable set of values to an instance of
that enumeration, the compiler will flag an error.
There are at least three benefits to using enumerations instead of plain integers:
Enumerations make your code easier to maintain by helping to ensure that your
variables are assigned only legitimate, anticipated values.
Enumerations make your code clearer by allowing you to refer to integer values by
descriptive names rather than by obscure “magic” numbers.
Enumerations make your code easier to type, too. When you go to assign a value to an
instance of an enumerated type, the Visual Studio .NET IDE will, through
IntelliSense, pop up a list box of acceptable values in order to save you some
keystrokes and to remind you of what the possible options are.
Example:
We can define an enumeration as follows:
public enum TimeOfDay
{
Morning = 0,
Afternoon = 1,
Evening = 2
}
In this case, we use an integer value to represent each period of the day in the
enumeration. We can now access these values as members of the enumeration. For example,
59
Pantech Academy of Technical Excellence
TimeOfDay.Morning will return the value 0. We will typically use this enumeration to pass
an appropriate value into a method, and iterate through the possible values in a switch
statement:
60
class EnumExample
{
public static int Main()
{
WriteGreeting(TimeOfDay.Morning);
return 0;
}
static void WriteGreeting(TimeOfDay timeOfDay)
{
switch(timeOfDay)
{
case TimeOfDay.Morning:
Console.WriteLine(“Good morning!”);
break;
case TimeOfDay.Afternoon:
Console.WriteLine(“Good afternoon!”);
break;
case TimeOfDay.Evening:
Console.WriteLine(“Good evening!”);
break;
default:
Strings
Strings
String Escape Sequences
Substring
Null Strings and Empty Strings
Pantech Academy of Technical Excellence
61
Pantech Academy of Technical Excellence
Strings
A string is an object of type String whose value is text. Internally, the text is stored as
a readonly collection of Char objects, each of which represents one Unicode character
encoded in UTF-16. There is no null-terminating character at the end of a C# string (unlike C
and C++); therefore a C# string can contain any number of embedded null characters ('\0').
string keyword in C# actually refers to the .NET base class System.String. System.String is a
very powerful and versatile class.
String vs System.String
In C#, the string keyword is an alias for String. Therefore, String and string are
equivalent, and you can use whichever naming convention you prefer. The String class
provides many methods for safely creating, manipulating, and comparing strings. In addition,
the C# language overloads some operators to simplify common string operations.
Declaring and Initializing Strings
// Declare without initializing.
string message1;
// Initialize to null.
string message2 = null;
// Initialize as an empty string.
// Use the Empty constant instead of the literal "".
string message3 = System.String.Empty;
//Initialize with a regular string literal.
string oldPath = "c:\\Program Files\\Microsoft Visual Studio 8.0";
62
Pantech Academy of Technical Excellence
Notes:
String objects are immutable: they cannot be changed after they have been created. All of
the String methods and C# operators that appear to modify a string actually return the results
in a new string object. In the following example, when the contents of s1 and s2 are
concatenated to form a single string, the two original strings are unmodified. The += operator
creates a new string that contains the combined contents. That new object is assigned to the
variable s1, and the original object that was assigned to s1 is released for garbage collection
because no other variable holds a reference to it.
Example for String concatenation
63
Do not use the new operator to create a string object except when initializing the string with an array
of chars. Initialize a string with the Empty constant value to create a new String object whose string
is of zero length. The string literal representation of a zero-length string is "". By initializing strings
with the Empty value instead of null, you can reduce the chances of a NullReferenceException
occurring. Use the static IsNullOrEmpty(String) method to verify the value of a string before you try
to access it.
string s1 = "A string is more ";
string s2 = "than the sum of its chars.";
// Concatenate s1 and s2. This actually creates a new
// string object and stores it in s1, releasing the
// reference to the original object.
s1 += s2;
System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.
Pantech Academy of Technical Excellence
String Escape Sequences
Escape Sequence Character Name
\’ Single Quote
\” Double Quote
\\ Black Slash
\0 Null
\a Alert
\b Backspace
\f Form Feed
\n New Line
\r Carriage Return
\t Horizontal tab
\u Unicode Escape Sequence
\v Vertical tab
Substrings
A substring is any sequence of characters that is contained in a string. Use the
Substring method to create a new string from a part of the original string. You can search for
one or more occurrences of a substring by using the IndexOf method. Use the Replace
method to replace all occurrences of a specified substring with a new string. Like the
Substring method, Replace actually returns a new string and does not modify the original
string.
Example:
64
string s3 = "Visual C# Express";
System.Console.WriteLine(s3.Substring(7, 2));
// Output: "C#"
System.Console.WriteLine(s3.Replace("C#", "Basic"));
// Output: "Visual Basic Express"
// Index values are zero-based
int index = s3.IndexOf("C");
// index = 7
Pantech Academy of Technical Excellence
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 = String.Empty;
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:
65
static void Main()
{
string str = "hello";
string nullStr = null;
string emptyStr = "";
string tempStr = str + nullStr; // tempStr = "hello"
bool b = (emptyStr == nullStr);// b = false;
string newStr = emptyStr + nullStr; // creates a new empty string
int len = nullStr.Length; // throws NullReferenceException
Pantech Academy of Technical Excellence
66
Collections
Collections
Creating and Manipulating Collections
Collection Classes Overview
Specialized Collections
Pantech Academy of Technical Excellence
Collections
A collection is a set of similarly typed objects that are grouped together. Objects of
any type can be grouped into a single collection of the type Object to take advantage of
constructs that are inherent in the language. For example, the C# foreach statement (for each
in Visual Basic) expects all objects in the collection to be of a single type. However, in a
collection of type Object, additional processing is done on the elements individually, such as
boxing and unboxing or conversions, which affect the performance of the collection. Boxing
and unboxing typically occur if storing or retrieving a value type in a collection of type
Object.
Creating and Manipulating Collections
The most common collections are provided by the .NET Framework. You can use any
of them or create your own collection based on one of them. Each collection is designed for
specific purposes. The members included in each System.Collections class reflect the purpose
of the collection. In addition, the generic collections in System.Collections.Generic make it
easy to create strongly typed collections.
If you decide to implement your own collection, use the following guidelines:
Start with the right base class and interfaces.
Consider making your collection strongly typed. Strongly typed collections provide
automatic type validation and avoid processes that adversely affect performance, such
as boxing and unboxing and conversions. If your language supports generics, use one
of the System.Collections.Generic types. If your language does not support generics,
System.Collections.Specialized contains examples of strongly typed collections.
Consider providing synchronization in your class.
Consider enabling serialization for your class.
67
Pantech Academy of Technical Excellence
Collection Classes Overview
Collection Classes have the following properties:
Collection classes are defined as part of the System.Collections or
System.Collections.Generic namespace.
Most collection classes derive from the interfaces ICollection, IComparer,
IEnumerable, IList, IDictionary, and IDictionaryEnumerator and their generic
equivalents.
Using generic collection classes provides increased type-safety and in some cases can
provide better performance, especially when storing value types.
Specialized Collections
Specialized collections are collections with highly specific purposes.
NameValueCollection is based on NameObjectCollectionBase; however,
NameValueCollection accepts multiple values per key, whereas NameObjectCollectionBase
accepts only one value per key.
Some strongly typed collections in the System.Collections.Specialized namespace are
StringCollection and StringDictionary, both of which contain values that are exclusively
strings. The CollectionsUtil class creates instances of case-insensitive collections.
Some collections transform. For example, the HybridDictionary class starts as a
ListDictionary and becomes a Hashtable when it becomes large. The KeyedCollection is a list
but it also creates a lookup dictionary when the number of elements reaches a specified
threshold
68
Pantech Academy of Technical Excellence
C# PROGRAMMING BASICS
69
Namespaces
Namespaces
Example for Namespace
Namespace Overview
Pantech Academy of Technical Excellence
Namespaces
Namespaces are used in C# programming in two ways. First, the .NET Framework
uses namespaces to organize its many classes, as follows:
System.Console.WriteLine("Hello World!");
System is a namespace and Console is a class in that namespace. The using keyword can be
used so that the complete name is not required, as in the following example:
using System;
Console.WriteLine("Hello");
Console.WriteLine("World!");
Second, declaring your own namespaces can help you control the scope of class and method
names in larger programming projects. Use the namespace keyword to declare a namespace,
as in the following example:
Namespaces Overview:
Namespaces have the following properties:
They organize large code projects.
They are delimited by using the. operator.
The using directive obviates the requirement to specify the name of the namespace
for every class.
70
namespace SampleNamespace{ class SampleClass{ public void SampleMethod( ){ System.Console.WriteLine("SampleMethod inside SampleNamespace"); } }}
Pantech Academy of Technical Excellence
The global namespace is the "root" namespace: global::System will always refer
to the .NET Framework namespace System.
71
OOPs Concept
Encapsulation
Inheritance
Polymorphism
Classes and Objects
Constructors
Destructors
Method Overloading
Method Overriding
Early Binding, Late Binding
Abstract Classes
Abstract Methods
Interfaces
Multiple Inheritance
Static Classes
Static Constructors
Object Initializer
Events
Attributes
Statements
Pantech Academy of Technical Excellence
Why Object oriented approach?
A major factor in the invention of Object-Oriented approach is to remove some of the
flaws in procedural approach. In OOP, data is treated as a critical element and does not allow
it to flow freely. It bounds data closely to the functions that operate on it and protects it from
accidental modification from outside functions. OOP allows decomposition of a problem into
a number of entities called objects and then builds data and functions around these objects. A
major advantage of OOP is code reusability.
Some important features of Object Oriented programming are as follows:
Emphasis on data rather than procedure
Programs are divided into Objects
Data is hidden and cannot be accessed by external functions
Objects can communicate with each other through functions
New data and functions can be easily added whenever necessary
Follows bottom-up approach
Concepts of OOP
Objects
Classes
Data Abstraction and Encapsulation
Inheritance
Polymorphism
Encapsulation
Encapsulation is the procedure of covering up of data and functions into a single unit
(called class). An encapsulated object is often called an abstract data type. Storing data and
functions in a single unit (class) is encapsulation. Data cannot be accessible to the outside
world and only those functions which are stored in the class can access it.
72
Pantech Academy of Technical Excellence
Need For Encapsulation
The need of encapsulation is to protect or prevent the code (data) from accidental
corruption due to the errors. In Object oriented programming data is treated as a critical
element in the program development and data is packed closely to the functions that operate
on it and protects it from accidental modification from outside functions.
Encapsulation provides a way to protect data from accidental corruption. Rather than
defining the data in the form of public, we can declare those fields as private. The Private
data are manipulated indirectly by two ways. The first method is using a pair of conventional
accessor and mutator methods. Another one method is using a named property.
Encapsulation Using Accessors and Mutator’s
Let us see an example of Department class. To manipulate the data in that class (String
departname) we define an accessor (get method) and mutator (set method).
73
using system;public class Department{private string departname;.......
// Accessor.public string GetDepartname(){ return departname;}
// Mutator.public void SetDepartname( string a){departname=a;}}
Pantech Academy of Technical Excellence
Inheritance
Inheritance is the process by which objects can acquire the properties of objects of
other class. In OOP, inheritance provides reusability, like, adding additional features to an
existing class without modifying it. This is achieved by deriving a new class from the existing
one. The new class will have combined features of both the classes. The class whose
members are inherited is called the base class, and the class that inherits those members is
called the derived class.
Example
Output
Parent Constructor.
Child Constructor.
I'm a Parent Class.
74
using System;public class ParentClass{ public ParentClass( ){ Console.WriteLine("Parent Constructor."); } public void print(){ Console.WriteLine("I'm a Parent Class."); }}public class ChildClass : ParentClass{ public ChildClass( ){ Console.WriteLine("Child Constructor."); } public static void Main( ){ ChildClass child = new ChildClass(); child.print(); }}
Pantech Academy of Technical Excellence
Polymorphism
Polymorphism means the ability to take more than one form. An operation may
exhibit different behaviors in different instances. The behavior depends on the data types
used in the operation. Polymorphism is extensively used in implementing Inheritance. It has
two distinct aspects:
At run time objects of a derived class may be treated as objects of a base
class in places such as method parameters and collections or arrays. When
this occurs, the objects declared type is no longer identical to its run-time
type.
Base classes may define and implement virtual methods and derived
classes can override them, which means they provide their own definition
and implementation. At run-time, when client code calls method, the CLR
looks up the run-time type of the object, and invokes that override of the
virtual method. Thus in your source code you can call a method on a base
class and cause a derived class’s version of the method to be executed.
Example:
75
public class Shape{ public int X { get; private set; } public int Y { get; private set; } public int Height { get; set; } public int Width { get; set; }
// Virtual method public virtual void Draw() { Console.WriteLine("Performing base class drawing tasks"); }}
class Circle : Shape{ public override void Draw() { // Code to draw a circle... Console.WriteLine("Drawing a circle"); base.Draw(); }}
Pantech Academy of Technical Excellence
76
class Rectangle : Shape{ public override void Draw() { // Code to draw a rectangle... Console.WriteLine("Drawing a rectangle"); base.Draw(); }}class Triangle : Shape{ public override void Draw() { // Code to draw a triangle... Console.WriteLine("Drawing a triangle"); base.Draw(); }}
class Program{ static void Main(string[ ] args) { // Polymorphism at work #1: a Rectangle, Triangle and Circle // can all be used where ever a Shape is expected. No cast is // required because an implicit conversion exists from a derived // class to its base class. System.Collections.Generic.List<Shape> shapes = new System.Collections.Generic.List<Shape>(); shapes.Add(new Rectangle()); shapes.Add(new Triangle()); shapes.Add(new Circle());
// Polymorphism at work #2: the virtual method Draw is // invoked on each of the derived classes, not the base class. foreach (Shape s in shapes) { s.Draw(); }
// Keep the console open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); }}
Pantech Academy of Technical Excellence
Output
Drawing a rectangle
Performing base class drawing tasks
Drawing a triangle
Performing base class drawing tasks
Drawing a circle
Performing base class drawing tasks
Polymorphism Overview
When a derived class inherits from a base class, it gains all the methods, fields,
properties and events of the base class. The designer of the derived class can choose whether
to
override virtual members in the base class,
inherit the closest base class method without overriding it
define new non-virtual implementation of those members that hide the
base class implementations
A derived class can override a base class member only if the base class member is
declared as virtual or abstract. The derived member must use the override keyword to
explicitly indicate that the method is intended to participate in virtual invocation
Classes and Objects
A class is a construct that enables you to create your own custom types by grouping
together variables of other types, methods and events. It defines the data and behavior of a
type. If the class is not declared as static, client code can use it by creating objects or
instances which are assigned to a variable. The variable remains in memory until all
references to it go out of scope. At that time, the CLR marks it as eligible for garbage
collection. If the class is declared as static, then only one copy exists in memory and client
code can only access it through the class itself, not an instance variable.
77
Pantech Academy of Technical Excellence
Classes are declared 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.
Objects
An object is basically a block of memory that has been allocated and configured
according to the blueprint. A program may create many objects of the same class. Objects are
also called instances, and they can be stored in either a named variable or in an array or
collection. Client code is the code that uses these variables to call the methods and access the
public properties of the object.
For Example:
Class classname
{
}
classname obj=new classname( ); // Object Created as obj
Creating Objects
An object is a concrete entity based on a class, and is sometimes referred to as an
instance of a class. A class defines a type of object, but it is not an object itself. Objects can
be created by using the new keyword followed by the name of the class.
78
Pantech Academy of Technical Excellence
Customer object1 = new Customer( );
You can create an object reference without creating an object at all. 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;
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. A constructor is a special member function whose task is to
initialize the objects of it's class. This is the first method that is run when an instance of a
type is created.
A constructor is invoked whenever an object of it's associated class is created. If a
class contains a constructor, then an object created by that class will be initialized
automatically. We pass data to the constructor by enclosing it in the parentheses following
the class name when creating an object. Constructors can never return a value, and can be
overridden to provide custom initialization functionality.
A constructor that takes no parameters is called a default constructor. Default
constructors are invoked whenever an object is instantiated by using the new operator and no
arguments are provided to new.
Constructors that take parameters must be called through a new statement or a base
statement. classes and structs can also define multiple constructors, and neither is required to
define a default constructor.
A constructor can use the base keyword to call the constructor of a base class. The
constructor for the base class is called before the block for the constructor is executed. The
79
Pantech Academy of Technical Excellence
base keyword can be used with or without parameters. Any parameters to the constructor can
be used as parameters to base, or as part of an expression. In a derived class, if a base-class
constructor is not called explicitly by using the base keyword, the default constructor, if there
is one, is called implicitly. If a base class does not offer a default constructor, the derived
class must make an explicit call to a base constructor by using base.
A constructor can invoke another constructor in the same object by using the “this
“keyword. Like base, this can be used with or without parameters, and any parameters in the
constructor are available as parameters to this, or as part of an expression.
For Example:
public Employee(int weeklySalary, int numberOfWeeks)
: this(weeklySalary * numberOfWeeks)
{
}
Constructors can be marked as public, private, protected, internal, or protected
internal. These access modifiers define how users of the class can construct the class. A
constructor can be declared static by using the static keyword. Static constructors are called
automatically, immediately before any static fields are accessed, and are generally used to
initialize static class members.
For Example:
80
public class Taxi
{
public bool isInitialized;
public Taxi( )
{
isInitialized = true;
}
}
Pantech Academy of Technical Excellence
Destructors
Destructors are used to destruct instances of classes. A destructor, also know as
finalizer, is the last method run by a class. Within a destructor we can place code to clean up
the object after it is used, which might include decrementing counters or releasing resources.
When working with destructors we need to use the overrides keyword with Finalize method
as we will override the Finalize method built into the Object class. We normally use Finalize
method to deallocate resources and inform other objects that the current object is going to be
destroyed.
Destructors overview
Destructors cannot be defined in structs. They are only used with classes.
A class can only have one destructor.
Destructors cannot be inherited or overloaded.
Destructors cannot be called. They are invoked automatically.
A destructor does not take modifiers or have parameters.
For Example:
Notes:
81
class Car{ ~Car( ) // destructor { // cleanup statements... }}
Empty destructors should not be used. When a class contains a destructor, an entry is created in
the Finalize queue. When the destructor is called, the garbage collector is invoked to process the
queue. If the destructor is empty, this just causes a needless loss of performance. Destructors are
also called when the program exits. It is possible to force garbage collection by calling Collect,
but most of the time, this should be avoided because it may create performance issues
Pantech Academy of Technical Excellence
Method Overloading
C# allows you to define different versions of a method in class, and the compiler will
automatically select the most appropriate one based on the parameters supplied.
For Example:
public int add(int x, int y)
public int add(int x, int y, int z)
Overload 1 requires two integers - exactly the parameters that we are passing in. The
compiler will generate code that calls this overload.
Overload 2 requires three integers.
Method Overriding
Method overriding in C# is a feature like the virtual function in C++. Method
overriding is a feature that allows you to invoke functions (that have the same signatures) that
belong to different classes in the same hierarchy of inheritance using the base class reference.
C# makes use of two keywords: virtual and overrides to accomplish Method overriding.
82
public class AddingNumbers
{
public int Add(int a, int b)
{
return a+b;
}
public int Add(int a, int b, int c)
{
return a+b+c;
Pantech Academy of Technical Excellence
For Example:
Output:
BC::Display
DC::Display
The above program compiles and runs successfully to give the expected desired
output. The function Display( ) of Base class BC is declared as virtual, while the Derived
class's implementation of Display( ) is decorated with the modifier override. Doing so
enables C# to invoke functions like Display( ) based on objects the reference variable refers
to and not the type of reference that is invoking the function. Hence in the above program
when b refers to the object of class BC it invokes Display( ) of BC and then when b refers to
the object of class DC it invokes Display() of class DC.
83
class BC{public virtual void Display(){System.Console.WriteLine(“BC::Display“);}}class DC : BC{public override void Display(){System.Console.WriteLine(“DC::Display“);}}Class Demo{Public static void Main(){ BC b; b = new BC(); b.Display();
b = new DC(); b.Display(); }}
Pantech Academy of Technical Excellence
Early Binding
Binding refers the object type definition. In Early Binding, if the object type is
specified when declaring your object. Early binding allows developers to interact with the
object’s properties and methods during coding. Also early binding permits the compiler to
check your code. Errors are caught at compile time. Early binding also results in faster code.
It’s disadvantage is that you cannot create a generic object which may be bound to different
types of objects.
Late Binding
Declare your objects as a generic type are late binding. Binding is determined at
object declaration and not at object instantiation. Late binding on the other hand permits
defining generic objects which may be bound to different objects. You could then query the
Controls collection and determine which control you are working on using the TypeOf
method and branch to the section of your code that provides for that type. This is the only
benefit of late binding. Late binding can only be used to access type members that are
declared as Public. And its disadvantage is accessing members declared as Friend or
Protected Friend results in a runtime error.
Abstract Classes
Classes can be declared as abstract. This is accomplished by putting the keyword
abstract before the keyword class in the class definition.
For example:
84
public abstract class A
{
// Class members here.
}
Pantech Academy of Technical Excellence
An abstract class cannot be instantiated. The purpose of an abstract class is to provide
a common definition of a base class that multiple derived classes can share. For example, a
class library may define an abstract class that is used as a parameter to many of its functions,
and require programmers using that library to provide their own implementation of the class
by creating a derived class.
Abstract classes may also define abstract methods. This is accomplished by adding
the keyword abstract before the return type of the method.
Abstract Methods
An abstract class can contain abstract and non-abstract methods. When a class inherits
from an abstract, the derived class must implement all the abstract methods declared in the
base class.
An abstract method is a method without any method body. They are implicitly virtual.
But by declaring the derived class also abstract, we can avoid the implementation of all or
certain abstract methods. This is what is known as partial implementation of an abstract class.
Abstract methods have no implementation, so the method definition is followed by a
semicolon instead of a normal method block.
Derived classes of the abstract class must implement all abstract methods. When an
abstract class inherits a virtual method from a base class, the abstract class can override the
virtual method with an abstract method.
For Example:
85
public abstract class A
{
public abstract void DoWork(int i);
}
Pantech Academy of Technical Excellence
If a virtual method is declared abstract, it is still virtual to any class inheriting from
the abstract class. A class inheriting an abstract method cannot access the original
implementation of the method. An abstract class can force derived classes to provide new
method implementations for virtual methods.
Interfaces
Interfaces are defined using the interface keyword. An interface looks like a class, but
has no implementation. The only things it contains are definitions of events, indexers,
methods and/or properties. The reason interfaces only provide definitions is because they are
inherited by classes and structs, which must provide an implementation for each interface
member defined.
For Example:
Interfaces describe a group of related behaviors that can belong to any class or struct.
Interfaces can be made up of methods, properties, events, indexers, or any combination of
those four member types. An interface can not contain fields. Interfaces members are
automatically public.
Classes and structs can inherit from interfaces in a manner similar to how classes can
inherit a base class or struct, with two exceptions:
A class or struct can inherit more than one interface.
When a class or struct inherits an interface, it inherits only the method names and
signatures, because the interface itself contains no implementations
86
interface IComparable
{
int CompareTo(object obj);
}
Pantech Academy of Technical Excellence
Interfaces Overview:
An interface has the following properties:
An interface is similar to an abstract base class: any non-abstract type inheriting
the interface must implement all its members.
An interface cannot be instantiated directly.
Interfaces can contain events, indexers, methods and properties.
Interfaces contain no implementation of methods.
Classes and structs can inherit from more than one interface.
An interface can itself inherit from multiple interfaces.
Multiple Inheritance
Static Classes
A class can be declared static, indicating that it contains only static members. It is not
possible to create instances of a static class using the new keyword. Static classes are loaded
automatically by the .NET Framework common language runtime (CLR) when the program
or namespace containing the class is loaded.
Use a static class to contain methods that are not associated with a particular object.
For example, it is a common requirement to create a set of methods that do not act on
instance data and are not associated to a specific object in your code. You could use a static
class to hold those methods.
The main features of a static class are:
They only contain static members.
They cannot be instantiated.
They cannot contain Instance Constructors.
They are sealed.
87
Pantech Academy of Technical Excellence
Creating a static class is therefore much the same as creating a class that contains only
static members and a private constructor. A private constructor prevents the class from being
instantiated.
The advantage of using a static class is that the compiler can check to make sure that
no instance members are accidentally added. The compiler will guarantee that instances of
this class cannot be created.
Static classes are sealed and therefore cannot be inherited. Static classes cannot
contain a constructor, although it is still possible to declare a static constructor to assign
initial values or set up some static state. Use a static class as a unit of organization for
methods not associated with particular objects. Also, a static class can make your
implementation simpler and faster because you do not have to create an object in order to call
its methods.
For Example:
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.
88
public static class TemperatureConverter{ public static double CelsiusToFahrenheit(string temperatureCelsius) { }
public static double FahrenheitToCelsius(string temperatureFahrenheit) { }}
Pantech Academy of Technical Excellence
Syntax:
class SimpleClass
{
// Static constructor
static SimpleClass()
{
//...
}
}
Static constructors have the following properties:
A static constructor does not take access modifiers or have parameters.
A static constructor is called automatically to initialize the class before the first
instance is created or any static members are referenced.
A static constructor cannot be called directly.
The user has no control on when the static constructor is executed in the program.
A typical use of static constructors is when the class is using a log file and the
constructor is used to write entries to this file.
Static constructors are also useful when creating wrapper classes for unmanaged code,
when the constructor can call the LoadLibrary method.
For Example:
The class Bus has a static constructor and one static member, Drive( ). When Drive( ) is
called, the static constructor is invoked to initialize the class.
89
Pantech Academy of Technical Excellence
90
public class Bus{ // Static constructor: static Bus() { System.Console.WriteLine("The static constructor invoked."); }
public static void Drive() { System.Console.WriteLine("The Drive method invoked."); }}
class TestBus{ static void Main() { Bus.Drive(); }}
Pantech Academy of Technical Excellence
Output:
The static constructor invoked.
The Drive method invoked.
Object Initializer
Object initializer, assign values to any accessible fields or properties of an object at
creation time without having to explicitly invoke a constructor. The following example shows
how to use an object initializer with a named type.
private class Cat
{
// Auto-implemented properties
public int Age { get; set; }
public string Name { get; set; }
}
static void MethodA()
{
// Object initializer
Cat cat = new Cat { Age = 10, Name = "Sylvester" };
}
It is a compile-time error to use a collection initializer with a nullable struct. You can
use object initializers to initialize type objects in a declarative manner without having to
invoke the type's constructor.
Anonymous types must be declared with an object initializer.The following example
shows how to initialize a single new StudentName type by using an object initializer.
StudentName student = new StudentName
91
Pantech Academy of Technical Excellence
{
FirstName = "Craig",
LastName = "Playstead",
ID = 116
};
Events
An event in C# is a way for a class to provide notifications to clients of that class
when some interesting thing happens to an object. The most familiar use for events is in
graphical user interfaces; typically, the classes that represent controls in the interface have
events that are notified when the user does something to the control (for example, click a
button).
Events, however, need not be used only for graphical interfaces. Events provide a
generally useful way for objects to signal state changes that may be useful to clients of that
object. Events are an important building block for creating classes that can be reused in a
large number of different programs.
Events are declared using delegates. If you have not yet studied the Delegates
Tutorial, you should do so before continuing. Recall that a delegate object encapsulates a
method so that it can be called anonymously. An event is a way for a class to allow clients to
give it delegates to methods that should be called when the event occurs. When the event
occurs, the delegate(s) given to it by its clients are invoked.
Declaring an event To declare an event inside a class, first a delegate type for the
event must be declared, if none is already declared.
public delegate void ChangedEventHandler(object sender, EventArgs e);
The delegate type defines the set of arguments that are passed to the method that
handles the event. Multiple events can share the same delegate type, so this step is only
necessary if no suitable delegate type has already been declared.
92
Pantech Academy of Technical Excellence
Next, the event itself is declared.
public event ChangedEventHandler Changed;
An event is declared like a field of delegate type, except that the keyword event
precedes the event declaration, following the modifiers. Events usually are declared public,
but any accessibility modifier is allowed.
Invoking an event Once a class has declared an event, it can treat that event just like
a field of the indicated delegate type. The field will either be null, if no client has hooked up a
delegate to the event, or else it refers to a delegate that should be called when the event is
invoked. Thus, invoking an event is generally done by first checking for null and then calling
the event.
if (Changed != null)
Changed(this, e);
Invoking an event can only be done from within the class that declared the event.
Hooking up to an event From outside the class that declared it, an event looks like a field,
but access to that field is very restricted.
This is done with the += and -= operators. To begin receiving event invocations,
client code first creates a delegate of the event type that refers to the method that should be
invoked from the event. Then it composes that delegate onto any other delegates that the
event might be connected to using +=.
// Add "ListChanged" to the Changed event on "List":
List.Changed += new ChangedEventHandler(ListChanged);
When the client code is done receiving event invocations, it removes its delegate from
the event by using operator -=.
\
Detach the event and delete the list:
List.Changed -= new ChangedEventHandler(ListChanged);
93
Pantech Academy of Technical Excellence
Events in Interfaces
One other difference between events and fields is that an event can be placed in an
interface while a field cannot. When implementing the interface, the implementing class must
supply a corresponding event in the class that implements the interface.
Events and Inheritance
When creating a general component that can be derived from, what seems to be a
problem sometimes arises with events. Since events can only be invoked from within the
class that declared them, derived classes cannot directly invoke events declared within the
base class. Although this is sometimes what is desired, often it is appropriate to give the
derived class the freedom to invoke the event.
Attributes
C# provides a mechanism for defining declarative tags, called attributes, which you
can place on certain entities in your source code to specify additional information. The
information that attributes contain can be retrieved at run time through reflection. You can
use predefined attributes or you can define your own custom attributes.
Attributes can be placed on most any declaration (though a specific attribute might restrict the
types of declarations on which it is valid). Syntactically, an attribute is specified by placing
the name of the attribute, enclosed in square brackets, in front of the declaration of the entity
to which it applies.
For example, a class with the attribute DllImport is declared like this:
[DllImport] public class MyDllimportClass { ... }
Many attributes have parameters, which can be either positional (unnamed) or named. Any
positional parameters must be specified in a certain order and cannot be omitted; named
parameters are optional and can be specified in any order. Positional parameters are specified
first. More than one attribute can be placed on a declaration, either separately or within the
same set of brackets. Some attributes can be specified more than once for a given entity.
94
Pantech Academy of Technical Excellence
Statements
The actions that a program takes are expressed in statements. Common actions
include declaring variables, assigning values, calling methods, looping through collections,
and branching to one or another block of code, depending on a given condition. The order in
which statements are executed in a program is called the flow of control or flow of execution.
The flow of control may vary every time that a program is run, depending on how the
program reacts to input that it receives at run time.
A statement can consist of a single line of code that ends in a semicolon, or a series of
single-line statements in a block. A statement block is enclosed in {} brackets and can
contain nested blocks.
Types of Statements
The following table lists the various types of statements in C# and their associated keywords.
Category C# keywords / notes
Declaration statements A declaration statement introduces a new variable or constant. A
variable declaration can optionally assign a value to the variable. In a
constant declaration, the assignment is required.
Expression statements Expression statements that calculate a value must store the value in a
variable.
Selection statements Selection statements enable you to branch to different sections of
code, depending on one or more specified conditions
Iteration statements Iteration statements enable you to loop through collections like arrays,
or perform the same set of statements repeatedly until a specified
condition is met.
Jump statements Jump statements transfer control to another section of code.
95
Pantech Academy of Technical Excellence
Exception handling
statements
Exception handling statements enable you to gracefully recover from
exceptional conditions that occur at run time.
Checked and
unchecked
Checked and unchecked statements enable you to specify whether
numerical operations are allowed to cause an overflow when the result
is stored in a variable that is too small to hold the resulting value.
The fixed statement The fixed statement prevents the garbage collector from relocating a
movable variable.
The lock statement The lock statement enables you to limit access to blocks of code to
only one thread at a time.
Labeled statements You can give a statement a label and then use the goto keyword to
jump to the labeled statement.
The empty statement The empty statement consists of a single semicolon. It does nothing
and can be used in places where a statement is required but no action
needs to be performed.
96
Pantech Academy of Technical Excellence
EVENT HANDLING
97
Event Handling
System Defined Exceptions
Custom Exceptions
Try, Catch, Finally
Throwing Exceptions
Pantech Academy of Technical Excellence
System Defined Exceptions
The C# language's exception handling features provide a way to deal with any
unexpected or exceptional situations that arise while a program is running. Exception
handling uses the try, catch, and finally keywords to attempt actions that may not succeed, to
handle failures, and to clean up resources afterwards. Exceptions can be generated by the
common language runtime (CLR), by third-party libraries, or by the application code using
the throw keyword.
In this example, a method tests for a division by zero, and catches the error. Without
the exception handling, this program would terminate with a DivideByZeroException was
unhandled error.
int SafeDivision(int x, int y)
{
try
{
return (x / y);
}
catch (System.DivideByZeroException dbz)
{
System.Console.WriteLine("Division by zero attempted!");
return 0;
}
}
Exceptions Overview
Exceptions have the following properties:
When your application encounters an exceptional circumstance, such as a division
by zero or low memory warning, an exception is generated.
Use a try block around the statements that might throw exceptions.
Once an exception occurs within the try block, the flow of control immediately
jumps to an associated exception handler, if one is present.
98
Pantech Academy of Technical Excellence
If no exception handler for a given exception is present, the program stops
executing with an error message.
If a catch block defines an exception variable, you can use it to get more
information on the type of exception that occurred.
Actions that may result in an exception are executed with the try keyword.
An exception handler is a block of code that is executed when an exception
occurs. In C#, the catch keyword is used to define an exception handler.
Exceptions can be explicitly generated by a program using the throw keyword.
Exception objects contain detailed information about the error, including the state
of the call stack and a text description of the error.
Code in a finally block is executed even if an exception is thrown, thus allowing a
program to release resources.
Custom Exceptions
Introduction
The implementation of Custom Exception Handling using the existing features of C# .Net.
The Concept
The whole idea of having customized Exception Handling is centered on the fact that
there needs to be a generic approach of catching and throwing Exceptions.
For implementing custom Exception Handling we need to derive the class
CustomException from the system base class ApplicationException. In general, for
customizing Exception Handling the following components are necessary:
1. A custom exception class which would host the exception thrown and can be thrown
as a new exception.
2. A message class that would host all user - friendly Application messages that need to
be displayed.
99
Pantech Academy of Technical Excellence
Implementation
The following are the steps that are required to implement custom exception for
creation of the above mentioned components:
Step 1:
Define a project called Enumerators that would contain the definitions of all the Enumerators
as the following:
1. The Severity level determines the criticality of the error.
2. The LogLevel determines whether an entry needs to be made in Log. Based on the log
level chosen, entries can be made either in the Debug Log or System Event Log .
Step 2:
Add another project named CustomException. Add a reference of the Enumerators project.
To the project add the following class deriving from ApplicationException:
100
using System;namespace CustomException{/// <summary>/// Severity level of Exception/// </summary>public enum SeverityLevel {Fatal,Critical,Information}/// <summary>/// Log level of Exception/// </summary>public enum LogLevel {Debug,Event}}
Pantech Academy of Technical Excellence
101
using System;namespace CustomException{/// <summary>/// Summary description for CustomException/// </summary>public class CustomException : ApplicationException{// private members // defines the severity level of the Exceptionprivate SeverityLevel severityLevelOfException ;// defines the logLevel of the Exceptionprivate LogLevel logLevelOfException ;// System Exception that is thrown private Exception innerException ;// Custom Message private string customMessage ;/// <summary>/// Public accessor of customMessage/// </summary>public string CustomMessage{get {return this.customMessage; }set {this.customMessage = value; }}/// <summary>/// Standard default Constructor/// </summary>public CustomException( ){ }/// <summary>/// Constructor with parameters /// </summary>/// <param name="severityLevel"></param>/// <param name="logLevel"></param>/// <param name="exception"></param>/// <param name="customMessage"></param> public CustomException( SeverityLevel severityLevel , LogLevel logLevel, Exception exception, string customMessage){this.severityLevelOfException = severityLevel ; this.logLevelOfException = logLevel ; this.innerException = exception ;this.customMessage = customMessage ;}}}
Pantech Academy of Technical Excellence
One advantage of creating a custom Exception class is that the
Constructor can be enabled to writing to a Log on instantiation of the CustomException
Object using TraceListeners. The entry to the log would be based on the logLevel. This
would force - write an entry each time the custom Exception is thrown.
Thus we have a customException which could be thrown in the catch - handlers of
system - defined exceptions.
Step 3:
For implementing the CustomMessage component , create an Exception.resx File that would
host the error string and the corresponding message string as key-value pair.
Ex. "Test", "Testing Error"
Step 4:
Add to project a CustomMessage.cs class File. This File would look in the following way:
102
using System.Threading;using System.Resources;using System.Reflection;namespace CustomException{/// <summary>/// Summary description for CustomMessage./// </summary>public class CustomMessage{public CustomMessage(){}public string GetString(string key) {// Create a resource manager to retrieve resources.ResourceManager rm = new ResourceManager("Exceptions", Assembly.GetExecutingAssembly());// Get the culture of the currently executing thread.// The value of ci will determine the culture of// the resources that the resource manager retrieves.CultureInfo ci = Thread.CurrentThread.CurrentCulture;// Retrieve the value of the string resource and return it String str = rm.GetString(key, ci);return str ; }}}
Pantech Academy of Technical Excellence
The GetString() of the CustomMessage class is used to retrieve the value string
corresponding to the key passed as parameter from the Exceptions resource File.
Usage
a) Add to existing Solution a Test Windows Applications Project.
b) Add the References to both the CustomException and the Enumerators projects.
c) Write a function which would throw a system exception , encapsulated in a try-catch block
as follows :
d) In the Catch Block , re-throw the Exception as a CustomException
e) On a button_click event handler add the following code :
103
private void Updater( ){try {int i = 0; int j = 8 ;int k= j/i;}catch(Exception ex){ SeverityLevel severityLevel = SeverityLevel.Critical;LogLevel logLevel = LogLevel.Debug; CustomMessage customMessage = new CustomMessage();throw new CustomException.CustomException( severityLevel,logLevel,ex.InnerException,customMessage.GetString("Test"));}}
private void buttonTest_Click(object sender, System.EventArgs e){try{this.Updater();}catch(CustomException.CustomException ce){MessageBox.Show(ce.CustomMessage);}}
Pantech Academy of Technical Excellence
Try, Catch, Finally
A common usage of catch and finally together is to obtain and use resources in a try
block, deal with exceptional circumstances in a catch block, and release the resources in the
finally block
The purpose of a try-catch block is to catch and handle an exception generated by
working code. Some exceptions can be handled in a catch block and the problem solved
without the exception being re-thrown; however, more often the only thing you can do is
make sure the appropriate exception is thrown.
For Example:
In this example, IndexOutOfRangeException is not the most appropriate exception:
ArgumentOutOfRangeException makes more sense for the method because the error is
caused by the index argument passed in by the caller.
104
class TestTryCatch{ static int GetInt(int[] array, int index) { try { return array[index]; } catch (System.IndexOutOfRangeException e) // CS0168 { System.Console.WriteLine(e.Message); //set IndexOutOfRangeException to the new exception's InnerException throw new System.ArgumentOutOfRangeException("index parameter is out of range.", e); } }}
Pantech Academy of Technical Excellence
Example for Try, Catch, Finally
Sample Output
Executing the try statement.
System.NullReferenceException: Object reference not set to an instance of an object.
at EHClass.Main() Caught exception #1.
Executing finally block.
Throwing Exceptions
The throw statement is used to signal the occurrence of an anomalous situation (exception)
during the program execution. The thrown exception is an object whose class is derived from
System.Exception,
105
// try_catch_finally.csusing System;public class EHClass{ static void Main() { try { Console.WriteLine("Executing the try statement."); throw new NullReferenceException(); } catch (NullReferenceException e) { Console.WriteLine("{0} Caught exception #1.", e); } catch { Console.WriteLine("Caught exception #2."); } finally { Console.WriteLine("Executing finally block."); } }}
Pantech Academy of Technical Excellence
For Example:
class MyException : System.Exception {}
// ...
throw new MyException();
Usually the throw statement is used with try-catch or try-finally statements. When an
exception is thrown, the program looks for the catch statement that handles this exception.
You can also rethrow a caught exception using the throw statement.
Example:
This example demonstrates how to throw an exception using the throw statement.
Output
The ArgumentNullException exception occurs.
106
using System;public class ThrowTest { static void Main() { string s = null;
if (s == null) { throw new ArgumentNullException(); }
Console.Write("The string s is null"); // not executed }}
Pantech Academy of Technical Excellence
OPERATOR OVERLOADING
107
Operators
Unary Operators
Binary Operators
Pantech Academy of Technical Excellence
Operators
An operator is a symbol that operates on one or more arguments to produce a
result.C# provides a large set of operators, which are symbols that specify which operations
to perform in an expression. Operations on integral types such as ==, !=, <, >, <=, >=, binary
+, binary -, ^, &, |, ~, ++, --, and sizeof( ) are generally allowed on enumerations. In addition,
many operators can be overloaded by the user, thus changing their meaning when applied to a
user-defined type.
Unary Operators
The +, -, !, ~, ++, --, and cast operators are called the unary operators.
The unary-expression is:
primary-expression
+ unary-expression
- unary-expression
! unary-expression
~ unary-expression
pre-increment-expression
pre-decrement-expression
cast-expression
Unary plus Operator
The operand is converted to the parameter type of the selected operator, and the type of the
result is the return type of the operator. The predefined unary plus operators are:
int operator +(int x);
uint operator +(uint x);
long operator +(long x);
ulong operator +(ulong x);
float operator +(float x);
108
Pantech Academy of Technical Excellence
double operator +(double x);
decimal operator +(decimal x);
For each of these operators, the result is simply the value of the operand.
Unary minus Operator
For an operation of the form –x, unary operator overload resolution is applied to select a
specific operator implementation. The operand is converted to the parameter type of the
selected operator, and the type of the result is the return type of the operator. The predefined
negation operators are:
Integer negation:
int operator –(int x);
long operator –(long x);
The result is computed by subtracting x from zero. If the value of x is the smallest
representable, value of the operand type (−231 for int or −263 for long), then the mathematical
negation of x is not representable within the operand type. If this occurs within a checked
context, a System.OverflowException is thrown; if it occurs within an unchecked context, the
result is the value of the operand and the overflow is not reported.
If the operand of the negation operator is of type uint, it is converted to type long, and
the type of the result is long. An exception is the rule that permits the int value −2147483648
(−231) to be written as a decimal integer literal.
If the operand of the negation operator is of type ulong, a compile-time error occurs. An
exception is the rule that permits the long value −9223372036854775808 (−263) to be written
as a decimal integer literal.
Floating-point negation:
float operator –(float x);
double operator –(double x);
The result is the value of x with its sign inverted. If x is NaN, the result is also NaN.
109
Pantech Academy of Technical Excellence
Decimal negation:
decimal operator –(decimal x);
The result is computed by subtracting x from zero. Decimal negation is equivalent to using
the unary minus operator of type System.Decimal.
Logical negation Operator
For an operation of the form !x, unary operator overload resolution is applied to
select a specific operator implementation. The operand is converted to the parameter type of
the selected operator, and the type of the result is the return type of the operator. Only one
predefined logical negation operator exists:
bool operator !(bool x);
This operator computes the logical negation of the operand: If the operand is true, the
result is false. If the operand is false, the result is true.
Bitwise complement Operator
For an operation of the form ~x, unary operator overload resolution is applied to
select a specific operator implementation. The operand is converted to the parameter type of
the selected operator, and the type of the result is the return type of the operator.
The predefined bitwise complement operators are:
int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);
For each of these operators, the result of the operation is the bitwise complement of
x.Every enumeration type E implicitly provides the following bitwise complement operator:
E operator ~(E x);
110
Pantech Academy of Technical Excellence
The result of evaluating ~x, where x is an expression of an enumeration type E with an
underlying type U, is exactly the same as evaluating (E)(~(U)x).
Prefix Increment and Decrement Operators
The operand of a prefix increment or decrement operation must be an expression
classified as a variable, a property access, or an indexer access. The result of the operation is
a value of the same type as the operand.
If the operand of a prefix increment or decrement operation is a property or indexer
access, the property or indexer must have both a get and a set accessor. If this is not the case,
a compile-time error occurs.
Unary operator overload resolution is applied to select a specific operator
implementation. Predefined ++ and -- operators exist for the following types: sbyte, byte,
short, ushort, int, uint, long, ulong, char, float, double, decimal, and any enum type. The
predefined ++ operators return the value produced by adding 1 to the operand, and the
predefined -- operators return the value produced by subtracting 1 from the operand.
The run-time processing of a prefix increment or decrement operation of the form ++x or
--x consists of the following steps:
If x is classified as a variable.
x is evaluated to produce the variable.
The selected operator is invoked with the value of x as its argument.
The value returned by the operator is stored in the location given by the evaluation
of x.
The value returned by the operator becomes the result of the operation.
111
pre-increment-expression:
++ unary-expression
pre-decrement-expression:
-- unary-expression
Pantech Academy of Technical Excellence
If x is classified as a property or indexer access:
The instance expression (if x is not static) and the argument list (if x is an indexer
access) associated with x are evaluated, and the results are used in the subsequent
get and set accessor invocations.
The get accessor of x is invoked.
The selected operator is invoked with the value returned by the get accessor as its
argument.
The set accessor of x is invoked with the value returned by the operator as its
value argument.
The value returned by the operator becomes the result of the operation.
The ++ and -- operators also support postfix notation. The result of x++ or x-- is the
value of x before the operation, whereas the result of ++x or --x is the value of x after the
operation. In either case, x itself has the same value after the operation.
An operator ++ or operator -- implementation can be invoked using either postfix or prefix
notation. It is not possible to have separate operator implementations for the two notations.
Cast Expressions
A cast-expression is used to explicitly convert an expression to a given type.
cast-expression:
( type ) unary-expression
A cast-expression of the form (T)E, where T is a type and E is a unary-expression,
performs an explicit conversion of the value of E to type T. If no explicit conversion exists
from the type of E to T, a compile-time error occurs. Otherwise, the result is the value
produced by the explicit conversion. The result is always classified as a value, even if E
denotes a variable.
The grammar for a cast-expression leads to certain syntactic ambiguities. For
example, the expression (x)–y could either be interpreted as a cast-expression (a cast of –y to
type x) or as an additive-expression combined with a parenthesized-expression (which
computes the value x – y).
112
Pantech Academy of Technical Excellence
To resolve cast-expression ambiguities, the following rule exists: A sequence of one or more
tokens enclosed in parentheses is considered the start of a cast-expression only if at least one
of the following are true:
The sequence of tokens is correct grammar for a type, but not for an expression.
The sequence of tokens is correct grammar for a type, and the token immediately
following the closing parentheses is the token "~", the token "!", the token "(", an
identifier, a literal, or any keyword except as and is.
The term "correct grammar" above means only that the sequence of tokens must
conform to the particular grammatical production. It specifically does not consider the actual
meaning of any constituent identifiers. For example, if x and y are identifiers, then x.y is
correct grammar for a type, even if x.y doesn't actually denote a type.
From the disambiguation rule it follows that, if x and y are identifiers, (x)y, (x)(y),
and (x)(-y) are cast-expressions, but (x)-y is not, even if x identifies a type. However, if x is a
keyword that identifies a predefined type (such as int), then all four forms are cast-
expressions (because such a keyword could not possibly be an expression by itself).
Binary Operators
A binary operator must take two parameters, at least one of which must have the class
or struct type in which the operator is declared. Parameters of the shift operators are further
constrained. A binary operator can return any type.
The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^,
<<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters. The return type
and the names of the formal parameters are not part of a binary operator's signature.
Certain binary operators require pair-wise declaration. For every declaration of either
operator of a pair, there must be a matching declaration of the other operator of the pair. Two
operator declarations match when they have the same return type and the same type for each
parameter. The following operators require pair-wise declaration:
113
Pantech Academy of Technical Excellence
operator == and operator !=
operator > and operator <
operator >= and operator <=
Binary operators are those operators that work with two operands. For example, a
common binary expression would be a + b—the addition operator (+) surrounded by two
operands. The binary operators are further subdivided into arithmetic, relational, logical, and
assignment operators.
Arithmetic Operators
Arithmetic expressions are composed of two expressions with an arithmetic operator
between them. This includes all the typical mathematical operators as expected in algebra.
The Multiplication Operators
The multiplication operator (*) evaluates two expressions and returns their product. Here's an
example:
int expr1 = 3;
int expr2 = 7;
int product;
product = expr1 * expr2; // product = 21
The Division Operator
The division operator (/), as its name indicates, performs mathematical division. It
takes a dividend expression and divides it by a divisor expression to produce a quotient.
Example:
int dividend = 45;
int divisor = 5;
int quotient;
quotient = dividend / divisor; // quotient = 9
114
Pantech Academy of Technical Excellence
The result been a fractional number, it would have been truncated to produce the integer
result.
The Remainder Operator
The remainder operator (%) returns the remainder of a division operation between a
dividend and divisor. A common use of this operator is to create equations that produce a
remainder that falls within a specified range.
Example:
int dividend = 33;
int divisor = 10;
int remainder;
remainder = dividend % divisor; // remainder = 3
The remainder will always be between 0 and 9.
The Addition Operator
The addition operator (+) performs standard mathematical addition by adding one number to
another.
Example:
int one = 1;
int two;
two = one + one; // two = 2
The Subtraction Operator
The subtraction operator (-) performs standard mathematical subtraction by subtracting the
value of one expression from another.
Example:
decimal debt = 537.50m;
decimal payment = 250.00m;
115
Pantech Academy of Technical Excellence
decimal balance;
balance = debt - payment; // balance = 287.50
The Left Shift Operator
To shift the bits of a number to the left, use the left shift operator (<<). The effect of this
operation is that all bits move to the left a specified number of times. High-order bits are lost.
Lower order bits are zero filled. This operator may be used on the int, uint, long, and ulong
data types.
Example:
uint intMax = 4294967295; // 11111111111111111111111111111111b
uint byteMask;
byteMask = intMax << 8; // 11111111111111111111111100000000b
The Right Shift Operator
The right shift operator (>>) shifts the bits of a number to the right. By providing a
number to operate on and the number of digits, every bit shifts to the right by the number of
digits specified. Only use the right shift operator on int, uint, long, and ulong data types. The
uint, ulong, positive int, and positive long types shift zeros from the left. The negative int and
negative long types keep a 1 in the sign bit position and fill the next position to the right with
a 0.
Examples:
uint intMax = 4294967295; // 11111111111111111111111111111111b
uint shortMask;
shortMask = intMax >> 16; // 00000000000000001111111111111111b
int intMax = -1; // 11111111111111111111111111111111b
int shortMask;
shortMask = intMax >> 16; // 10000000000000001111111111111111b
C# doesn't have a right shift with zero extension operator (>>>).
116
Pantech Academy of Technical Excellence
Relational Operators
Relational operators are used to make a comparison between two expressions. The
primary difference between relational operators and arithmetic operators is that relational
operators return a bool type rather than a number. Another difference is that arithmetic
operators are applicable to certain C# types whereas relational operators can be used on every
possible C# type, whether built-in or not. Floating-point types are evaluated according to
IEEE 754. The results of a relational expression are either true or false.
The Equal Operator
To see if two expressions are the same, use the equal operator (==). The equal operator works
the same for integral, floating-point, decimal, and enum types. It simply compares the two
expressions and returns a bool result.
Example:
bool bresult;
decimal debit = 1500.00m;
decimal credit = 1395.50m;
bresult = debit == credit; // bresult = false
When comparing floating-point types, +0.0 and -0.0 are considered equal. If either floating-
point number is NaN (not a number), equal returns false.
The Not Equal Operator
The not equal operator (!=) is the opposite of the equal operator for all types, with a slight
variation for floating-point types only. If one of the floating-point numbers is NAN (not a
number), not equal returns true.
There are two forms of not equal applicable to expressions. The first is the normal not
equal operator (!=). The other is a negation of the equal operator !(a==b). Normally, these
two forms always evaluate to the same value. The exception occurs when evaluating floating-
point expressions where one or both expressions evaluate to NaN and the relational operator
117
Pantech Academy of Technical Excellence
in the negation of an expression is <, >, <=, or >=. The a > b form evaluates to false, but the !
(a<=b) evaluates to true.
Examples:
bool bresult;
decimal debit = 1500.00m;
decimal credit = 1395.50m;
bresult = debit != credit; // bresult = true
bresult = !(debit == credit); // bresult = true
The Less Than Operator
If it's necessary to find out if one value is smaller than another, use the less than
operator (<). The expression on the left is being evaluated and the expression on the right is
the basis of comparison. When the expression on the left is a lower value than the expression
on the right, the result is true. Otherwise, the result is false.
Example:
short redBeads = 2;
short whiteBeads = 23;
bool bresult;
bresult = redBeads < whiteBeads; // bresult=true, work harder
The Greater Than Operator
If it's necessary to know that a certain value is larger than another, use the greater than
operator (>). It compares the expression on the left to the basis expression on the right. When
the expression on the left is a higher value than the expression on the right, the result is true.
Otherwise, the result is false.
118
Pantech Academy of Technical Excellence
Example:
short redBeads = 13;
short whiteBeads = 12;
bool bresult;
bresult = redBeads > whiteBeads; // bresult=true, good job!
The Less than or Equal Operator
The expression on the left is compared to the expression on the right. When the
expression on the left is either the same value as or less than the one on the right, less than or
equal returns true. This operator is the opposite of the greater than operator, which means that
!(a>b) would produce the same results. The exception is when there's a floating-point
expression evaluating to NaN, in which case the result is always true.
Example:
float limit = 4.0f;
float currValue = 3.86724f;
bool Bresult;
bresult = currValue <= limit; // bresult = true
The Greater than or Equal Operator
The greater than or equal operator (>=) checks a value to see if it's greater than or
equal to another. When the expression to the left of the operator is the same as or more than
the expression on the right, greater than or equal returns true. The greater than or equal
operator is the opposite of the less than operator.
Example:
double rightAngle = 90.0d;
double myAngle = 96.0d;
bool isAbtuse;
isAbtuse = myAngle >= rightAngle; // Yes, myAngle is abtuse
119
Pantech Academy of Technical Excellence
Logical Operators
Logical operators perform Boolean logic on two expressions.There are three types of
logical operators in C#:
Bitwise
Boolean
Conditional.
The bitwise logical operators perform Boolean logic on corresponding bits of two
integral expressions. Valid integral types are the signed and unsigned int and long types.
They return a compatible integral result with each bit conforming to the Boolean evaluation.
Boolean logical operators perform Boolean logic upon two Boolean expressions. The
expression on the left is evaluated, and then the expression on the right is evaluated. Finally,
the two expressions are evaluated together in the context of the Boolean logical operator
between them. They return a bool result corresponding to the type of operator used.
The conditional logical operators operate much the same way as the Boolean logical
operators with one exception in behavior: Once the first expression is evaluated and found to
satisfy the results of the entire expression, the second expression is not evaluated. This is
efficient because it doesn't make sense to continue evaluating an expression when the result is
already known.
The Bitwise AND Operator
The bitwise AND operator (&) compares corresponding bits of two integrals and
returns a result with corresponding bits set to 1 when both integrals have 1 bits. When either
or both integrals have a 0 bit, the corresponding result bit is 0.
Example:
byte oddMask = 1; // 00000001b
byte someByte = 85; // 01010101b
bool isEven;
isEven = (oddMask & someByte) == 0; //(oddMask & someByte) = 1
120
Pantech Academy of Technical Excellence
The Bitwise Inclusive OR Operator
The bitwise inclusive OR operator (|) compares corresponding bits of two integrals and
returns a result with corresponding bits set to 1 if either of the integrals have 1 bits in that
position. When both integrals have a 0 in corresponding positions, the result is zero in that
position.
Example:
byte option1 = 1; // 00000001b
byte option2 = 2; // 00000010b
byte totalOptions;
totalOptions = (byte) (option1 | option2); // 00000011b
The Bitwise Exclusive OR Operator
The bitwise exclusive OR operator (^) compares corresponding bits of two integrals
and returns a result with corresponding bits set to 1 if only one of the integrals has a 1 bit
and the other integral has a 0 bit in that position. When both integral bits are 1 or when
both are 0, the result's corresponding bit is 0.
Example:
byte invertMask = 255; // 11111111b
byte someByte = 240; // 11110000b
byte inverse;
inverse = (byte)(someByte ^ invertMask); //inversion=00001111b
The Boolean AND Operator
The Boolean AND operator (&) evaluates two Boolean expressions and returns true when
both expressions evaluate to true. Otherwise, the result is false. The result of each expression
evaluated must return a bool result.
121
Pantech Academy of Technical Excellence
Example:
bool inStock = false;
decimal price = 18.95m;
bool buy;
buy = inStock & (price < 20.00m); // buy
The Boolean Inclusive OR Operator
The Boolean inclusive OR operator (|) evaluates the results of two Boolean expressions
and returns true if either of the expressions returns true. When both expressions are false, the
result of the Boolean inclusive OR evaluation is false. Both expressions evaluated must return
a bool type value.
Example:
int mileage = 2305;
int months = 4;
bool changeOil;
changeOil = mileage > 3000 | months > 3; // changeOil = true
The Boolean Exclusive OR Operator
The Boolean exclusive OR operator (^) evaluates the results of two Boolean expressions
and returns true if only one of the expressions returns true. When both expressions are true or
both expressions are false, the result of the Boolean exclusive OR expression is false. In other
words, the expressions must be different.
Example:
bool availFlag = false;
bool toggle = true;
bool available;
available = availFlag ^ toggle; // available = true
122
Pantech Academy of Technical Excellence
The Conditional AND Operator
The conditional AND operator (&&) is similar to the Boolean AND operator in that it
evaluates two expressions and returns true when both expressions are true. It is different
when the first expression evaluates to false. Since both expressions must be true, it's
automatically assumed that if the first expression evaluates to false, the entire expression is
false. Therefore, the conditional AND operator returns false and does not evaluate the second
expression. When the first expression is true, the conditional AND operator goes ahead and
evaluates the second expression.
Example:
bool inStock = false;
decimal price = 18.95m;
bool buy;
buy = inStock && (price < 20.00m); // buy = false
that price < 20 will never be evaluated.
The Conditional OR Operator
The conditional OR operator (||) is similar to the Boolean inclusive OR operator (|) in
that it evaluates two expressions and returns true when either expression is true. The
difference is when the first expression evaluates to true. Since either expression can be true to
prove that the overall expression is true, the operator automatically assumes that the entire
expression is true when it finds the first expression is true. Therefore, the conditional OR
operator returns true without evaluating the second expression. When the first expression is
false, the conditional OR operator goes ahead and evaluates the second expression.
Example:
int mileage = 4305;
int months = 4;
bool changeOil;
changeOil = mileage > 3000 || months > 3; // changeOil = true
that mileage>3000 is true, months>3 will never be evaluvated
123
Pantech Academy of Technical Excellence
The Assignment Operator
A compound operator is a combination of the assignment operator and an arithmetic
operator, bitwise logical operator, or Boolean logical operator.
Example:
int total = 7;
total += 3; // total = 10
This is the same as saying: total = total + 3.
Table 3.1 shows a list of the available compound assignment operators.
Table 3.1 Compound Assignment Operators
Operator Function
*= Multiplication
/= Division
%= Remainder
+= Addition
-= Subtraction
<<= Left Shift
>>= Right Shift
&= AND
^= Exclusive OR
|= Inclusive OR
Notes
124
When using conditional AND and conditional OR operators, a program does not depend upon
evaluation of the right-hand side of the expression, because it may not be evaluated. Such side
effects are likely to cause bugs.
Pantech Academy of Technical Excellence
GENERIC COLLECTIONS
125
Generic Collections
Introduction
Problem Statement
What are Generics
Applying Generics
Generic Constraints
Generics and Casting
Inheritance and Generics
Generics Methods
Generics Delegates
Generics and Reflection
Generics and the .NET Framework
Conclusion
Pantech Academy of Technical Excellence
Introduction
Generics are the most powerful feature of C#. Generics allow you to define type-safe
data structures, without committing to actual data types. In concept, generics are similar to
C++ templates, but different in implementation and capabilities.
Generics Problem Statement
Consider, data structure such as a stack, providing the classic Push( ) and Pop( )
methods. When developing a general-purpose stack, you would like to use it to store
instances of various types. Under C# 1.1, you have to use an Object-based stack, meaning
that the internal data type used in the stack is an amorphous Object, and the stack methods
interact with Objects:
public class Stack
{
object[ ] m_Items;
public void Push(object item)
{...}
public object Pop()
{...}
}
Code block 1 shows the full implementation of the Object-based stack. Because Object is the
canonical .NET base type, you can use the Object-based stack to hold any type of items, such
as integers:
Stack stack = new Stack( );
stack.Push(1);
stack.Push(2);
int number = (int)stack.Pop( );
Code block 1. An Object-based stack
126
public class Stack
{
readonly int m_Size;
int m_StackPointer = 0;
object[ ] m_Items;
Pantech Academy of Technical Excellence
127
public Stack( ):this(100)
{}
public Stack(int size)
{
m_Size = size;
m_Items = new object[m_Size];
}
public void Push(object item)
{
if(m_StackPointer >= m_Size)
throw new StackOverflowException( );
m_Items[m_StackPointer] = item;
m_StackPointer++;
}
public object Pop( )
{
m_StackPointer--;
if(m_StackPointer >= 0)
{
return m_Items[m_StackPointer];
}
else
{
m_StackPointer = 0;
throw new InvalidOperationException("Cannot pop an empty stack");
}
}
}
Pantech Academy of Technical Excellence
However, there are two problems with Object-based solutions. The first issue is
performance. When using value types, you have to box them in order to push and store them,
and unbox the value types when popping them off the stack. Boxing and unboxing incurs a
significant performance penalty in their own right, but it also increases the pressure on the
managed heap, resulting in more garbage collections, which is not great for performance
either. Even when using reference types instead of value types, there is still a performance
penalty because you have to cast from an Object to the actual type you interact with and incur
the casting cost:
Stack stack = new Stack();
stack.Push("1");
string number = (string)stack.Pop();
The second problem with the Object-based solution is type safety. Because the
compiler lets you cast anything to and from Object, you lose compile-time type safety. For
example, the following code compiles fine, but raises an invalid cast exception at run time:
Stack stack = new Stack();
stack.Push(1);
//This compiles, but is not type safe, and will throw an exception:
string number = (string)stack.Pop();
You can overcome these two problems by providing a type-specific (and hence, type-safe)
performant stack.
Unfortunately, solving the performance and type-safety problems this way introduces
a third, and just as serious problem—productivity impact. Writing type-specific data
structures are tedious, repetitive, and error-prone task. When fixing a defect in the data
structure, you have to fix it not just in one place, but in as many places as there are type-
specific duplicates of what is essentially the same data structure. In addition, there is no way
to foresee the use of unknown or yet-undefined future types, so you have to keep an Object-
based data structure as well.
128
Pantech Academy of Technical Excellence
What Are Generics
Generics allow you to define type-safe classes without compromising type safety,
performance, or productivity. You implement the server only once as a generic server, while
at the same time you can declare and use it with any type. To do that, use the < and >
brackets, enclosing a generic type parameter.
Example, Usage Of generic stack:
Code block 2 shows the full implementation of the generic stack. Compare Code block 1 to
Code block 2 and see that it is as if every use of object in Code block 1 is replaced with T in
Code block 2, except that the Stack is defined using the generic type parameter T:
public class Stack<T>
{...}
When using a generic stack, you have to instruct the compiler which type to use instead of the
generic type parameter T, both when declaring the variable and when instantiating it:
Stack<int> stack = new Stack<int>();
The compiler and the runtime do the rest. All the methods (or properties) that accept or return
a T will instead use the specified type, an integer in the example above.
129
public class Stack<T>
{
T[ ] m_Items;
public void Push(T item)
{...}
public T Pop()
{...}
}
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
int number = stack.Pop();
Pantech Academy of Technical Excellence
Code block 2. The generic stack
Note T is the generic type parameter (or type parameter) while the generic type is the
Stack<T>. The int in Stack<int> is the type argument.
The advantage of this programming model is that the internal algorithms and data
manipulation remain the same while the actual data type can change based on the way the
client uses your server code.
130
public class Stack<T>{ readonly int m_Size; int m_StackPointer = 0; T[] m_Items; public Stack():this(100) {} public Stack(int size) { m_Size = size; m_Items = new T[m_Size]; } public void Push(T item) { if(m_StackPointer >= m_Size) throw new StackOverflowException(); m_Items[m_StackPointer] = item; m_StackPointer++; } public T Pop() { m_StackPointer--; if(m_StackPointer >= 0) { return m_Items[m_StackPointer]; } else { m_StackPointer = 0; throw new InvalidOperationException("Cannot pop an empty stack"); } }}
Pantech Academy of Technical Excellence
Generics Benefits
Generics in .NET let you reuse code and the effort you put into implementing it. The
types and internal data can change without causing code bloat, regardless of whether you are
using value or reference types. You can develop, test, and deploy your code once, reuse it
with any type, including future types, all with full compiler support and type safety. Because
the generic code does not force the boxing and unboxing of value types, or the down casting
of reference types, performance is greatly improved. With value types there is typically a 200
percent performance gain, and with reference types you can expect up to a 100 percent
performance gain in accessing the type (of course, the application as a whole may or may not
experience any performance improvements). The source code available with this article
includes a micro-benchmark application, which executes a stack in a tight loop. The
application lets you experiment with value and reference types on an Object-based stack and
a generic stack, as well as changing the number of loop iterations to see the effect generics
have on performance.
Applying Generics
Because of the native support for generics in the IL and the CLR, most CLR-
compliant language can take advantage of generic types. For example, here is some Visual
Basic .NET code that uses the generic stack of Code block 2:
You can use generics in classes and in structs. Here is a useful generic point struct:
public struct Point<T>
{
public T X;
131
Dim stack As Stack(Of Integer)
stack = new Stack(Of Integer)
stack.Push(3)
Dim number As Integer
number = stack.Pop()
Pantech Academy of Technical Excellence
public T Y;
}
You can use the generic point for integer coordinates, for example:
Point<int> point;
point.X = 1;
point.Y = 2;
Or for charting coordinates that require floating point precision:
Point<double> point;
point.X = 1.2;
point.Y = 3.4;
Besides the basic generics syntax presented so far, C# 2.0 has some generics-specific
syntax. For example, consider the Pop( ) method of Code block 2. Suppose instead of
throwing an exception when the stack is empty, you would like to return the default value of
the type stored in the stack. If you were using an Object-based stack, you would simply return
null, but a generic stack could be used with value types as well. To address this issue, you can
use the default() operator, which returns the default value of a type.
Here is how you can use default in the implementation of the Pop( ) method:
The default value for reference types is null, and the default value for value types
(such as integers, enum, and structures) is a zero whitewash (filling the structure with zeros).
132
public T Pop()
{
m_StackPointer--;
if(m_StackPointer >= 0)
{
return m_Items[m_StackPointer];
}
else
{
m_StackPointer = 0;
return default(T);
}
}
Pantech Academy of Technical Excellence
Consequently, if the stack is constructed with strings, the Pop() methods return null when the
stack is empty, and when the stack is constructed with integers, the Pop() methods return
zero when the stack is empty.
Generic Constraints
With C# generics, the compiler compiles the generic code into IL independent of any
type arguments that the clients will use. As a result, the generic code could try to use
methods, properties, or members of the generic type parameters that are incompatible with
the specific type arguments the client uses. This is unacceptable because it amounts to lack of
type safety.
In C# you need to instruct the compiler which constraints the client-specified types
must obey in order for them to be used instead of the generic type parameters. There are three
types of constraints. A derivation constraint indicates to the compiler that the generic type
parameter derives from a base type such an interface or a particular base class.
A default constructor constraint indicates to the compiler that the generic type
parameter exposes a default public constructor (a public constructor with no parameters). A
reference/value type constraint constrains the generic type parameter to be a reference or a
value type.
A generic type can employ multiple constraints, and you even get IntelliSense
reflecting the constraints when using the generic type parameter, such as suggesting methods
or members from the base type.
It is important to note that although constraints are optional, they are often essential
when developing a generic type. Without them, the compiler takes the more conservative,
type-safe approach and only allows access to Object-level functionality in your generic type
parameters. Constraints are part of the generic type metadata so that the client-side compiler
can take advantage of them as well. The client-side compiler only allows the client developer
to use types that comply with the constraints, thus enforcing type safety.
133
Pantech Academy of Technical Excellence
Generics and Casting
The C# compiler only lets you implicitly cast generic type parameters to Object, or to
constraint-specified types, as shown in Code block 5. Such implicit casting is type safe
because any incompatibility is discovered at compile-time.
Code block 5: Implicit casting of generic type parameters
The compiler lets you explicitly cast generic type parameters to any other interface, but not to
a class:
134
interface ISomeInterface
{...}
class BaseClass
{...}
class MyClass<T> where T : BaseClass,ISomeInterface
{
void SomeMethod(T t)
{
ISomeInterface obj1 = t;
BaseClass obj2 = t;
object obj3 = t;
}
}
interface ISomeInterface
{...}
class SomeClass
{...}
class MyClass<T>
{
void SomeMethod(T t)
{
ISomeInterface obj1 = (ISomeInterface)t;//Compiles
SomeClass obj2 = (SomeClass)t; //Does not compile
}
}
Pantech Academy of Technical Excellence
However, you can force a cast from a generic type parameter to any other type using a
temporary Object variable:
Needless to say, such explicit casting is dangerous because it may throw an exception
at run time if the type argument used instead of the generic type parameter does not derive
from the type to which you explicitly cast. Instead of risking a casting exception, a better
approach is to use the is and as operators, as shown in Code block 6. The is operator returns
true if the generic type parameter is of the queried type, and as will perform a cast if the types
are compatible, and will return null otherwise. You can use is and as on both generic type
parameters and on generic classes with specific type arguments.
Code block 6.:Using 'is' and 'as' operators on generic type parameters
135
class SomeClass{...}
class MyClass<T> { void SomeMethod(T t) { object temp = t; SomeClass obj = (SomeClass)temp; }}
Pantech Academy of Technical Excellence
136
public class MyClass<T>
{
public void SomeMethod(T t)
{
if(t is int)
{...}
if(t is LinkedList<int,string>)
{...}
string str = t as string;
if(str != null)
{...}
LinkedList<int,string> list = t as LinkedList<int,string>;
if(list != null)
{...}
}
}
Pantech Academy of Technical Excellence
Inheritance and Generics
When deriving from a generic base class, you must provide a type argument instead
of the base-class's generic type parameter:
public class BaseClass<T>
{...}
public class SubClass : BaseClass<int>
{...}
If the subclass is generic, instead of a concrete type argument, you can use the subclass
generic type parameter as the specified type for the generic base class:
public class SubClass<T> : BaseClass<T>
{...}
When using the subclass generic type parameters, you must repeat any constraints stipulated
at the base class level at the subclass level. For example, derivation constraint:
137
public class BaseClass<T> where T : ISomeInterface
{...}
public class SubClass<T> : BaseClass<T> where T : ISomeInterface
{...}
Or constructor constraint:
public class BaseClass<T> where T : new()
{
public T SomeMethod()
{
return new T();
}
}
public class SubClass<T> : BaseClass<T> where T : new()
{...}
Pantech Academy of Technical Excellence
A base class can define virtual methods whose signatures use generic type parameters.
When overriding them, the subclass must provide the corresponding types in the method
signatures:
You can define generic interfaces, generic abstract classes, and even generic abstract
methods. These types behave like any other generic base type:
138
public class BaseClass<T>
{
public virtual T SomeMethod()
{...}
}
public class SubClass: BaseClass<int>
{
public override int SomeMethod()
{...}
}
If the subclass is generic it can also use its own generic type parameters for the override:
public class SubClass<T>: BaseClass<T>
{
public override T SomeMethod()
{...}
}
public interface ISomeInterface<T>
{
T SomeMethod(T t);
}
public abstract class BaseClass<T>
{
public abstract T SomeMethod(T t);
}
public class SubClass<T> : BaseClass<T>
{
public override T SomeMethod(T t)
{...)
}
Pantech Academy of Technical Excellence
There is an interesting use for generic abstract methods and generic interfaces. In C#
2.0, it is impossible to use operators such as + or += on generic type parameters. For
example, the following code does not compile because C# 2.0 does not have operator
constraints:
public class Calculator<T>
{
public T Add(T arg1,T arg2)
{
return arg1 + arg2;//Does not compile
}
//Rest of the methods
}
Generic Methods
In C# 2.0, a method can define generic type parameters, specific to its execution
scope:
public class MyClass<T>
{
public void MyMethod<X>(X x)
{...}
}
This is an important capability because it allows you to call the method with a different type
every time, which is very handy for utility classes.
You can define method-specific generic type parameters even if the containing class does not
use generics at all:
public class MyClass
{
public void MyMethod<T>(T t)
{...}
}
139
Pantech Academy of Technical Excellence
This ability is for methods only. Properties or indexers can only use generic type parameters
defined at the scope of the class.
When calling a method that defines generic type parameters, you can provide the type to use
at the call site:
MyClass obj = new MyClass();
obj.MyMethod<int>(3);
That said, when the method is invoked the C# compiler is smart enough to infer the correct
type based on the type of parameter passed in, and it allows omitting the type specification
altogether:
MyClass obj = new MyClass();
obj.MyMethod(3);
This ability is called generic type inference. Note that the compiler cannot infer the type
based on the type of the returned value alone:
public class MyClass
{
public T MyMethod<T>()
{}
}
MyClass obj = new MyClass();
int number = obj.MyMethod();//Does not compile
When a method defines its own generic type parameters, it can also define constraints for
these types:
However, you cannot provide method-level constraints for class-level generic type
parameters. All constraints for class-level generic type parameters must be defined at the
class scope.
140
Pantech Academy of Technical Excellence
When overriding a virtual method that defines generic type parameters, the subclass method
must redefine the method-specific generic type parameter:
Notes:
Generic Static Method
C# allows you to define static methods that use generic type parameters. However,
when invoking such a static method, you need to provide the concrete type for the containing
class at the call site, such as in this example:
public class MyClass<T>
{
public static T SomeMethod(T t)
{...}
}
int number = MyClass<int>.SomeMethod(3);
Static methods can define method-specific generic type parameters and constraints, similar to
instance methods. When calling such methods, you need to provide the method-specific types
at the call site, either explicitly:
Generic static methods are subjected to all constraints imposed on the generic type parameter
they use at the class level. As with instance method, you can provide constraints for generic
type parameters defined by the static method:
Operators in C# are nothing more than static methods and C# allows you to overload
operators for your generic types. Imagine that the generic LinkedList of Code block 3
141
The method override cannot define new constraints that did not appear at the base method. If
the subclass method calls the base class implementation of the virtual method, it must specify
the type argument to use instead of the generic base method type parameter. You can either
explicitly specify it yourself or rely on type inference if it is available:
Pantech Academy of Technical Excellence
provided the + operator for concatenating linked lists. The + operator enables you to write the
following elegant code:
LinkedList<int,string> list1 = new LinkedList<int,string>();
LinkedList<int,string> list2 = new LinkedList<int,string>();
...
LinkedList<int,string> list3 = list1+list2;
Generic Delegates
A delegate defined in a class can take advantage of the generic type parameter of that
class.
For Example:
public class MyClass<T>
{
public delegate void GenericDelegate(T t);
public void SomeMethod(T t)
{...}
}
When specifying a type for the containing class, it affects the delegate as well. C# 2.0
allows you to make a direct assignment of a method reference into a delegate variable. The
compiler is capable of inferring the type of the delegate you assign into, finding if the target
object has a method by the name you specify, and verifying that the method's signature
matches. Then, the compiler creates a new delegate of the inferred argument type (including
the correct type instead of the generic type parameter), and assigns the new delegate into the
inferred delegate.
Like classes, structs, and methods, delegates can define generic type parameters too:
Delegates defined outside the scope of a class can use generic type parameters. In that case,
you have to provide the type arguments for the delegate when declaring and instantiating it:
142
Pantech Academy of Technical Excellence
The delegate-level constraints are enforced only on the using side, when declaring a
delegate variable and instantiating a delegate object, similar to any other constraint at the
scope of types or methods.
Generic delegates are especially useful when it comes to events. You can literally
define a limited set of generic delegates, distinguished only by the number of the generic type
parameters they require, and use these delegates for all of your event handling needs.
Obviously, if you need more parameters, you can simply add more generic type parameters,
but I wanted to model GenericEventHandler after the non-generic .NET EventHandler,
defined as:
public void delegate EventHandler(object sender,EventArgs args);
Unlike EventHandler, GenericEventHandler is type safe, as shown in Code block 8
because it accepts only objects of the type MyPublisher as senders, rather than mere Object.
In fact, .NET already defines a generic flavor of EventHandler in the System namespace:
public void delegate EventHandler(object sender,A args) where A : EventArgs;
Generics and Reflection
In .NET 2.0, reflection is extended to support generic type parameters. The type Type
can now represent generic types with specific type arguments (called bounded types), or
unspecified (unbounded) types. As in C# 1.1, you can obtain the Type of any type by using
the typeof operator or by calling the GetType() method that every type supports. Regardless
of the way you choose, both yield the same Type.
For example, in the following code sample, type1 is identical to type2.
LinkedList<int,string> list = new LinkedList<int,string>();
Type type1 = typeof(LinkedList<int,string>);
Type type2 = list.GetType();
Debug.Assert(type1 == type2);
Both typeof and GetType() can operate on generic type parameters:
public class MyClass<T>
143
Pantech Academy of Technical Excellence
{
public void SomeMethod(T t)
{
Type type = typeof(T);
Debug.Assert(type == t.GetType());
}
}
In addition, the typeof operator can operate on unbounded generic types.
Type has new methods and properties designed to provide reflection information about the
generic aspect of the type.
The most useful of these new members are the HasGenericArguments property and the
GetGenericArguments ( ) and GetGenericTypeDefinition() methods.
As its name indicates, HasGenericArguments is set to true if the type represented by the
Type object uses generic type parameters. GetGenericArguments() returns an array of
Types corresponding to the type arguments used. GetGenericTypeDefinition() returns a
Type representing the generic form of the underlying type.
Similar to Type, MethodInfo and its base class MethodBase have new members that reflect
generic method information.
you can use MethodInfo (as well as a number of other options) for late binding invocation.
However, the type of the parameters you pass for the late binding must match the bounded
types used instead of the generic type parameters (if any):
Attributes and Generics
When defining an attribute, you can instruct the compiler that the attribute should
target generic type parameters, using the new GenericParameter value of the enum
AttributeTargets:
144
Pantech Academy of Technical Excellence
Yet internally, an attribute class can take advantage of generics by using generic types or
define helper generic methods, like any other type. C# 2.0 does not allow you to define
generic attributes.
Generics and the .NET Framework
Here we discuss, how some other areas in .NET besides C# itself are taking advantage
of or interacting with generics.
System.Array and Generics
The System.Array type is extended with many generic static methods. The generic
static methods are designed to automate and streamline common tasks of working with
arrays, such as iterating over the array and performing an action on each element, scanning
the array looking for a value that matches a certain criteria (a predicate), converting and
sorting the array, and so on.
System.Array's static generic methods all work with the following four generic delegates
defined in the System namespace:
public delegate void Action<T>(T t);
public delegate int Comparison<T>(T x, T y);
public delegate U Converter<T, U>(T from);
public delegate bool Predicate<T>(T t);
It initializes an array with all the integers from 1 to 20. Then, using an anonymous
method and the Action<T> delegate, the code traces these numbers using the
Array.ForEach() method. Using a second anonymous method and the Predicate<T>
delegate, the code finds all the prime numbers in the array by calling the Array.FindAll()
method, which returns another array of the same generic type. Finally, the prime numbers are
traced using the same Action<T> delegate and anonymous method. Note the use of type
parameter inference in Code block 12. You can use the static methods without specifying the
type parameters.
145
Pantech Academy of Technical Excellence
Similar generic methods are also available on the class List<T> defined in the
System.Collections.Generic namespace. These methods use the same four generic delegates.
In fact, you can take advantage of those delegates in your code too, as shown in the following
section.
The Static Collection Class
While both System.Array and List<T> offer handy utility methods that greatly
simplify working with them, .NET does not offer such support for other collections.
The implementation of Collection is straightforward.
For example, here is the ForEach<T>( ) method:
public static void ForEach<T>(IEnumerator<T> iterator,Action<T> action)
{
/* Some parameter checking here, then: */
while(iterator.MoveNext())
{
action(iterator.Current);
}
}
The Collection static class is used very similar to both Array and List<T>, utilizing the
same generic delegates. You can use Collection with any collection, as long as that collection
supports either IEnumerable or IEnumerator:
Generic Collections
The data structures in System.Collections are all Object-based, and thus inheriting the
two problems described at the beginning of this article, namely, inferior performance and
lack of type safety. .NET 2.0 introduces a set of generic collections in the
System.Collections.Generic namespace. For example, there are generic Stack<T> and a
generic Queue<T> classes. The Dictionary<K,T> data structure is equivalent to the non-
generic HashTable, and there is also a SortedDictionary<K,T> class somewhat like
146
Pantech Academy of Technical Excellence
SortedList. The class List<T> is analogous to the non-generic ArrayList. Table 1 maps the
main types of System.Collections.Generic to those of System.Collections.
Table 1. Mapping System.Collections.Generic to System.Collections
System.Collections.Generic System.Collections
Comparer<T> Comparer
Dictionary<K,T> HashTable
LinkedList<T> -
List<T> ArrayList
Queue<T> Queue
SortedDictionary<K,T> SortedList
Stack<T> Stack
ICollection<T> ICollection
IComparable<T> System.IComparable
IDictionary<K,T> IDictionary
IEnumerable<T> IEnumerable
IEnumerator<T> IEnumerator
IList<T> IList
Briefly, IEnumerable<T> provides access to the IEnumerator<T> iterator
interface, used for abstracted iterations over the collection. All the collections implement
IEnumerable<T> on a nested struct, where the generic type parameter T is the type the
collection stores.
Of particular interest is the way the dictionary collections define their iterators. A dictionary
is actually a collection of not one but two types of generic parameter—the key and the value.
System.Collection.Generic provides a generic struct called KeyValuePair<K,V>
147
Pantech Academy of Technical Excellence
Defined as:
struct KeyValuePair<K,V>
{
public KeyValuePair(K key,V value);
public K Key(get;set;)
public V Value(get;set;)
}
KeyValuePair<K,V> simply stores a pair of a generic key and a generic value. This
struct is what the dictionary manages as a collection, and what it uses for its implementation
of IEnumerable<T>. The Dictionary class specifies the generic KeyValuePair<K,V>
structure as the item argument for IEnumerable<T> and ICollection<T>:
The key and value type parameters used in KeyValuePair<K,V> are of course the dictionary's
own generic key and value type parameters. You can certainly do the same in your own
generic data structures that use pairs of keys and values.
Serialization and Generics
.NET allows you to have serializable generic types:
When serializing a type, besides the state of the object members, .NET persists
metadata about the object and its type. If the serializable type is generic and it contains
bounded types, the metadata about the generic type contains type information about the
bounded types as well. Consequently, each permutation of a generic type with a specific
argument type is considered a unique type.
Note that IFormatter is object-based. You can compensate by defining a generic
version of IFormatter, the use of the two constraints on the generic type parameter F. While
it is possible to use GenericFormatter<F>.
Generics and Remoting
148
Pantech Academy of Technical Excellence
You can define and deploy remote classes that utilize generics, and you can use
programmatic or administrative configuration. Consider the class MyServer<T> that uses
generics and is derived from MarshalByRefObject:
public class MyServer<T> : MarshalByRefObject
{...}
You can only access this class over remoting when the type parameter T is a marshalable
object. This implies that T is either a serializable type or is derived from
MarshalByRefObject. You can enforce that by constraining T to derive from
MarshalByRefObject:
public class MyServer<T> : MarshalByRefObject
where T : MarshalByRefObject
{...}
When using administrative type registration, you need to specify the exact type
arguments to use instead of the generic type parameters. You must name the types in a
language-neutral manner, and provide fully qualified namespaces. For example, suppose the
class MyServer<T> is defined in the namespace RemoteServer in the assembly
ServerAssembly, and you want to use it with integer instead of the generic type parameter T,
in client activated mode. In that case, the required client-side type registration entry in the
configuration file would be:
<client url="...some url goes here...">
<activated type="RemoteServer.MyServer[[System.Int32]],ServerAssembly"/>
</client>
The matching host-side type registration entry in the configuration file is:
<service>
<activated type="RemoteServer.MyServer[[System.Int32]],ServerAssembly"/>
</service>
The double square brackets are used to specify multiple types.
For Example:
LinkedList[[System.Int32],[System.String]]
149
Pantech Academy of Technical Excellence
When using programmatic configuration, you configure activation modes and type
registration similar to C# 1.1, except when defining the type of the remote object you have to
provide type arguments instead of the generic type parameters.
What Generics Cannot Do
Under .NET 2.0, you cannot define generic Web services. That is, Web methods that
use generic type parameters. The reason is that none of the Web service standards support
generic services.
You also cannot use generic types on a serviced component. The reason is that
generics do not meet COM visibility requirements, which are required for serviced
components (just like you could not use C++ templates in COM or COM+).
Conclusion
C# generics are an invaluable part of your development arsenal. They improve
performance, type safety and quality, reduce repetitive programming tasks, simplify the
overall programming model, and do so with elegant, readable syntax. While the roots of C#
generics are C++ templates, C# takes generics to a new level by providing compile-time
safety and support. C# utilizes two-phase compilation, metadata, and innovative concepts
such as constraints and generic methods. No doubt future versions of C# will continue to
evolve generics, adding new capabilities and extending generics to other areas of .NET
Framework such as data access or localization.
150
Pantech Academy of Technical Excellence
ASSEMBLIES
151
Assemblies
Introduction
Private Assemblies
Shared Assemblies
Single File Assemblies
Multi File Assemblies
Global Assembly Cache
Pantech Academy of Technical Excellence
Introduction
An assembly is the logical unit that contains compiled code targeted at the .NET
Framework. An assembly is completely self-describing, and is a logical rather than a physical
unit, which means that it can be stored across more than one file. If an assembly is stored in
more than one file, then there will be one main file that contains the entry point and describes
the other files in the assembly.
Assemblies can be viewed using the command line utility ildasm, the MSIL
disassembler. An assembly can be opened by starting ildasm from the command line, with the
assembly as argument or by selecting the menu File➪Open.
Features of Assemblies
Assemblies are self-describing.
Version dependencies are recorded inside an assembly manifest.
Assemblies can be loaded side-by-side.
Application isolation is ensured using application domains.
Installation can be as easy as copying the files that belong to an assembly.
Private Assemblies
A private assembly is used by only one application while a shared assembly is shared
amongst different applications. By default, when a C# program is compiled, the assembly
produced will be a private assembly. This assembly (DLL/EXE) should be placed in the same
folder as the calling application. With a private assembly it’s not necessary to think about
naming conflicts with other classes or versioning problems because each application has its
own copy of the assembly.
152
Pantech Academy of Technical Excellence
Shared Assemblies
The assembly must be unique, and therefore have a unique name called a strong
name. Part of the strong name is a mandatory version number. Shared assemblies will mostly
be used when a vendor, different from that of the application, builds the component, or where
a large application is split into subprojects.
The combination of a file name, a public key, a version number and culture (locale
details) gives an assembly a strong name, which is guaranteed to be unique.
Single File Assemblies
A single-file assembly, which is the simplest type of assembly, contains type
information and implementation, as well as the assembly manifest. You can use command-
line compilers or Visual Studio .NET to create a single-file assembly. By default, the
compiler creates an assembly file with an .exe extension.
To create an assembly with an .exe extension
At the command prompt, type the following command:
<compiler command> <module name>
In this command, compiler command is the compiler command for the language used
in your code module, and module name is the name of the code module to compile
into the assembly.
Creating Library Assemblies
The previously described methods create a single-file assembly from a code module
that must contain a single entry point, such as a Main or WinMain method. The compiler
notifies you if the code module does not contain an entry point. If you do not want the
assembly to have an entry point, create a library assembly.
153
Pantech Academy of Technical Excellence
A library assembly is similar to a class library. It contains types that will be
referenced by other assemblies, but it has no entry point to begin execution.
To create a library assembly
At the command prompt, type the following command:
<compiler command> /t:library <module name>
In this command, compiler command is the compiler command for the language used
in your code module, and module name is the name of the code module to compile
into the assembly. You can also use other compiler options, such as the /out: option.
Multi File Assemblies
You can create multifile assemblies using command-line compilers or Visual Studio
2005 with Visual C++. One file in the assembly must contain the assembly manifest. An
assembly that starts an application must also contain an entry point, such as a Main or
WinMain method.
Notes:
There are several reasons you might want to create a multifile assembly:
To combine modules written in different languages. This is the most common
reason for creating a multifile assembly.
154
Multifile assemblies can have only one entry point, even if the assembly has multiple code
modules. If you are creating applications that will be downloaded using the <object> tag with
Microsoft Internet Explorer, it is important that you create multifile assemblies. In this
scenario, you create a file separate from your code modules that contain only the assembly
manifest.
Internet Explorer downloads the assembly manifest first, and then creates worker threads to
download any additional modules or assemblies required. While the file containing the
assembly manifest is being downloaded, Internet Explorer will be unresponsive to user input.
The smaller the file containing the assembly manifest, the less time Internet Explorer will be
unresponsive.
Pantech Academy of Technical Excellence
To optimize downloading an application by putting seldom-used types in a
module that is downloaded only when needed.
To combine code modules written by several developers. Although each
developer can compile each code module into an assembly, this can force some
types to be exposed publicly that are not exposed if all modules are put into a
multifile assembly.
Once you create the assembly, you can sign the file that contains the assembly
manifest (and hence the assembly), or you can give the file (and the assembly) a strong name
and put it in the global assembly cache.
Global Assembly Cache
To share an assembly among several applications, you can install it into the global
assembly cache. Each computer where the common language runtime is installed has this
machine-wide code cache. The global assembly cache stores assemblies specifically
designated to be shared by several applications on the computer. An assembly must have a
strong name to be installed in the global assembly cache.
You should share assemblies by installing them into the global assembly cache only
when necessary. Keep assembly dependencies private and locate assemblies in the
application directory unless sharing an assembly is explicitly required. To install assemblies
into the global assembly cache to make them accessible to COM interop or unmanaged code.
There are several reasons why you might want to install an assembly into the global assembly
cache:
Shared location.
Assemblies that should be used by applications can be put in the global
assembly cache. Consider, if all applications should use an assembly located in the
global assembly cache, a version policy statement can be added to the Machine.config
file that redirects references to the assembly.
155
Pantech Academy of Technical Excellence
File security.
Administrators often protect the systemroot directory using an Access Control
List (ACL) to control write and execute access. Because the global assembly cache is
installed in the systemroot directory, it inherits that directory's ACL. It is
recommended that only users with Administrator privileges be allowed to delete files
from the global assembly cache.
Side-by-side versioning.
Multiple copies of assemblies with the same name but different version
information can be maintained in the global assembly cache. ]
Additional search location.
The common language runtime checks the global assembly cache for an
assembly that matches the assembly request before probing or using the codebase
information in a configuration file.
Notes:
156
If you place one of the assemblies that make up an application into the global assembly cache,
you can no longer replicate or install the application by using XCOPY to copy the application
directory. In this case, you must also move the assembly into the global assembly cache
Assemblies placed in the global assembly cache must have the same assembly name and file
name.
Pantech Academy of Technical Excellence
DELEGATES
157
Delegates
Delegates
Delegates Overview
Function Pointers
Multicast Delegates
Pantech Academy of Technical Excellence
Delegates
A delegate is a type that references a method. Once a delegate is assigned a method, it
behaves exactly like that method. The delegate method can be used like any other method,
with parameters and a return value.
For Example:
public delegate int PerformCalculation(int x, int y);
Any method that matches the delegate's signature, which consists of the return type
and parameters, can be assigned to the delegate. This makes is possible to programmatically
change method calls, and also plug new code into existing classes. As long as you know the
delegate's signature, you can assign your own delegated method. This ability to refer to a
method as a parameter makes delegates ideal for defining callback methods.
Delegates Overview
Delegates have the following properties:
Delegates are similar to C++ function pointers, but are type safe.
Delegates allow methods to be passed as parameters.
Delegates can be used to define callback methods.
Delegates can be chained together; for example, multiple methods can be called on a
single event.
Methods don't need to match the delegate signature exactly.
C# version 2.0 introduces the concept of Anonymous Methods, which permit code
blocks to be passed as parameters in place of a separately defined method.
158
Pantech Academy of Technical Excellence
Why use delegates?
Delegates make our code more extensible by allowing other developers to substitute
their methods in place of our methods. Suppose you have a collection of business objects that
has a LoadData method. You could create an overloaded version of the LoadData method
that allows a developer to tell the LoadData method to call his/her custom method, via a
delegate, to fetch the data for the collection.
What exactly is a delegate?
Delegates are probably one the areas of C#. A delegate is to make an analogy to
classes and interfaces.
In object oriented programming we create classes that have methods and have
implementation code in those methods. If you simply want to define the methods but not
write the implementation code, you can create an interface that describes the methods but
there is no code or functionality to go along with it.
A delegate is to a method as an interface is to a class. In other words, if a function or method
is considered equivalent to a class (because there is implementation code) then a delegate is
equivalent to an interface. Basically, a delegate is a "function/method interface" in the sense
that the delegate describes the return type and parameters of a function.
Function Pointers
Delegates that provide the functionality of function pointers in C# called Function Pointers.
For Example:
Multicast Delegates
159
typedef int(* FUNC_PTR)(void *,void *);
int Compare(void *a, void *b){….}
int callFunction(void *a, void *b){
FUNC_PTR myfunc=Compare;
For(i=left+1;i<=right;i++)
If(myFunc(v[i],v[left]<0) }
Pantech Academy of Technical Excellence
A delegate can have more than one element in its invocation list. MulticastDelegate
is a special class. Compilers and other tools can derive from this class, but you cannot derive
from it explicitly.
A MulticastDelegate has a linked list of delegates, called an invocation list,
consisting of one or more elements. When a multicast delegate is invoked, the delegates in
the invocation list are called synchronously in the order in which they appear. If an error
occurs during execution of the list then an exception is thrown.
For Example:
160
delegate void Del(string s);
class TestClass{ static void Hello(string s) { System.Console.WriteLine(" Hello, {0}!", s); }
static void Goodbye(string s) { System.Console.WriteLine(" Goodbye, {0}!", s); }
static void Main() { Del a, b, c, d;
// Create the delegate object a that references // the method Hello: a = Hello;
// Create the delegate object b that references // the method Goodbye: b = Goodbye;
// The two delegates, a and b, are composed to form c: c = a + b;
// Remove a from the composed delegate, leaving d, // which calls only the method Goodbye: d = c - a; System.Console.WriteLine("Invoking delegate a:"); a("A"); System.Console.WriteLine("Invoking delegate b:"); b("B"); System.Console.WriteLine("Invoking delegate c:"); c("C"); System.Console.WriteLine("Invoking delegate d:"); d("D"); }
}
Pantech Academy of Technical Excellence
Output
Invoking delegate a:
Hello, A!
Invoking delegate b:
Goodbye, B!
Invoking delegate c:
Hello, C!
Goodbye, C!
Invoking delegate d:
Goodbye, D!
161
Pantech Academy of Technical Excellence
USER CONTROLS AND EVENT HANDLING
162
User Controls and Event Handling
User-Defined Controls
Inherited Controls
Inherited Forms
Event Handling
Pantech Academy of Technical Excellence
Overview
Embedding user controls in a Windows form is just like adding a simple button or text
box that are already provided with .NET. These basic controls were written essentially like
you code your own controls. Typically the controls you design are to be used in multiple
forms or to modularize your code. These reasons help reduce the amount of code you have to
type as well as make it easier for you to change your implementation. These reduce code
duplication as well as modularize your code.
Custom controls are a key theme in .NET development. They can help your
programming style by improving encapsulation, simplifying a programming model, and
making user interface more "pluggable" (i.e., making it easier to swap out one control and
replace it with a completely different one without rewriting your form code). Of course,
custom controls can have other benefits, including the ability to transform a generic window
into a state-of-the-art modern interface.
Generally, developers tackle custom control development for one of three reasons:
To create controls that abstract away unimportant details and are tailored for a
specific type of data.
To create controls that provide entirely new functionality, or just combine existing
UI elements in a unique way.
To create controls with a distinct original look, or ones that mimic popular
controls in professional applications that aren't available to the masses.
In .NET, creating a custom control is as easy as creating an ordinary class. You
simply inherit from the best possible ancestor and add the specific features you need. Best of
all, you can create a custom control class as part of an existing project, and then decide later
to place it in a separate assembly that can be shared with other programmers.
163
Pantech Academy of Technical Excellence
Types of Custom Controls
There are three or four types of controls:
User controls are the simplest type of control. They inherit from the
System.Windows.Forms.UserControl class, and follow a model of composition.
Usually, user controls combine more than one control in a logical
unit (like a group of text boxes for entering address information).
Inherited controls are generally more powerful and flexible. With an inherited
control, you choose the existing .NET control that is closest to what you
want to provide. Then, you derive a custom class that overrides or adds
properties and methods.
Owner-drawn controls generally use GDI+ drawing routines to generate their interfaces
from scratch. Because of this, they tend to inherit from a base class like
System.Windows.Forms.Control. Owner-drawn controls require the most work and provide
the most customizable user interface.
Extender providers, which aren't necessarily controls at all. These components add features
to other controls on a form, and provide a remarkable way to implement
extensible user interface.
Communication between User Controls and subscribing Applications
Because the basic .NET controls are contained within our user control, events are not
fired for the contained applications. Our user control is treated like any other and must
implement it's own properties (besides those inherited from System.Windows.Forms.Control)
and events.
Publishing and Subscribing Events
164
Pantech Academy of Technical Excellence
The Event model in C# finds its roots in the event programming model that is popular
in asynchronous programming. The basic foundation behind this programming model is the
idea of "publisher and subscribers." In this model, you have publishers who will do some
logic and publish an "event." Publishers will then send out their event only to subscribers
who have subscribed to receive the specific event.
In C#, any object can publish a set of events to which other applications can
subscribe. When the publishing class raises an event, all the subscribed applications are
notified. The following figure shows this mechanism.
165
Pantech Academy of Technical Excellence
Events and Delegates
The heart of Events in C# is Delegates. When an object generates events, it must send
the event out. The way that events are dispatched is through the use of delegates.
Events are declared in C# as follows:
[attributes] [modifier] event type member-name;
Modifier is any allowable scope modifier.
Type must be a delegate.
Member-name is the Name of the Event with which you will refer to the event
in your code.
The important thing to note here is the delegate type that events should use. The
delegate can be any legal delegate. But there is a convention that you should follow and is
one that Window Forms uses. By Convention, the delegate should accept two parameters:
1. The object that generated the event
2. The parameters for the specific event
An example of an event / delegate is as follows:
public delegate void SubmitClickedHandler(object sender, EventArgs e);
public event SubmitClickedHandler SubmitClicked;
SubmitClickedHandler is the name of the delegate, sender is self explanatory. EventArgs is
defined under the System namespace and is a very plain class. SubmitClicked is the name of
the event, which is published to the Subscriber.
Event Handling in C#
Event handlers are methods in an object that are executed in response to some events
occurring in the application. When a user interacts with a GUI control (e.g., clicking a button
on a form, Mouse click, Keyboard press etc.), one or more methods are executed in response
to the above event. Events can also be generated without user interactions
166
Pantech Academy of Technical Excellence
Microsoft Foundation Classes (MFC): These classes are based upon Microsoft Win32 API.
Normally development work is done through visual c++.
Java API: Here majority of the work is done by employing AWT and Swing packages.
Actions are enabled by applying Interfaces. The main difficulty is that you should learn and
remember all methods in the corresponding interfaces failing which you would get compile
time errors.
As compared to the above, Event handling in C# is much more simplified. It is possible to
handle various Mouse and Key related events quickly and in a more efficient manner.
1. Invoke the related event by supplying a custom method or event handler.
Using + = operator as shown here:
2.Click += new EventHandler(OnClick);
3.Apply the event handler as described below. It must be in conformity to a delegate of the
class System.EventHandler:
public delegate void EventHandler(object sender, Event args)
The first argument indicates the object sending the event and the second argument
contains information for the current event. You can use this argument object to handle
functionalities associated with the related event.
Listing-1 below illustrates how to print "Hello C#" inside a Textbox when a Button is
clicked:
167
Pantech Academy of Technical Excellence
The above example also shows how to handle Resize event. Try resizing the forms border
and observe the result.
168
using System;
using System.Windows.Forms;
using System.Drawing;
public class Butevent:Form
{
TextBox t1 = new TextBox();
Button b1 = new Button();
public Butevent()
{
this.Text = "Program developed by Anand.N";
t1.Location = new Point(20,30);
b1.Text = "Click here to activate";
b1.Location = new Point(20,55);
b1.Size = new Size(150,20);
// Invoking Method or EventHandler
b1.Click+=new EventHandler(OnClick);
this.Controls.Add(t1);
this.Controls.Add(b1);
// Invoking Method or EventHandler
this.Resize += new EventHandler(OnResize);
}
//Applying EventHandler
public void OnResize(object sender,EventArgs ee)
{
MessageBox.Show("oops! Form Resized");
}
//Applying EventHandler
public void OnClick(object sender,EventArgs e)
{
Pantech Academy of Technical Excellence
Handling Mouse Events
You can handle various Mouse actions by using the events specified in the Control
class. Following listing shows how to handle a simple MouseUp Event:
Try out the above example and observe the result. Similarly Click, DoubleClick, MouseEnter,
MouseLeave events can be handled in the similar way.
Using Keyboard Events
Every modern programming language contains all necessary functionalities for
handling Keyboard related events. C# also provides us with three events KeyPress, KeyUp
and KeyDown which you can use to handle Keyboard events.
169
using System;
using System.Windows.Forms;
using System.Drawing;
public class Mousedemo:Form
{
public Mousedemo()
{
this.MouseUp += new MouseEventHandler(OnMouseup);
}
public void OnMouseup(object sender,MouseEventArgs e)
{
this.Text = "Current Position (" +e.X + " , " + e.Y +")";
}
public static void Main()
{
Pantech Academy of Technical Excellence
Listing-3 below shows the usage of KeyUp Event.
170
using System;
using System.Windows.Forms;
using System.Drawing;
public class Keydemo:Form
{
public Keydemo()
{
this.KeyUp += new KeyEventHandler(OnKeyPress);
}
public void OnKeyPress(object sender, KeyEventArgs e)
{
MessageBox.Show(e.KeyCode.ToString(), "Your input");
}
public static void Main()
{
Pantech Academy of Technical Excellence
FILE HANDLING
171
File Handling
File Stream
Stream Reader
Stream Writer
File Info
Directory Info
Pantech Academy of Technical Excellence
Files and Folders
One of the ways of permanent storage of data is through files. A file is a collection of
data stored on a secondary storage device, such as the Hard Disk. Every file has a name
associated with it, such as Essay.txt. The name comprises of two parts the base name (in our
case ‘Essay’) and the extension (txt). The portion before the period (.) is the base name
whereas the one after it is the extension. A file name can also contain multiple periods - Dr.
No.avi. The most significant period is always the last one. So, in this case, the base name of
the file would be Dr. No and the extension avi.
The extension is what determines the type of the file. For example, a document has an
extension of doc while a JPEG Image file has an extension of jpg. The base name is what we
specify to identify that particular file.
Files are contained in folders or directories. A directory itself can contain other files
and folders, thus forming a hierarchy. Files within a folder (directory) need to have unique
names. Files with same base name but different extensions are allowed. So, the file My
File.txt and My File.doc can co-exist in a folder. The full path of the file marks its complete
address on a machine. For instance, if the file Notepad.exe is contained inside the Windows
directory of your C drive, its path would be “C:\Windows\Notepad.exe”. Likewise, if it were
to be contained inside the System32 directory which in turn is present in the Windows
directory, the full path would be “C:\Windows\System32\Notepad.exe”.
Stream
A stream is a sequence of bytes travelling from a source to a destination over a
communication medium. Streams can either be input or output. The input stream is used for
reading while the output stream is used for writing. This stream concept does not just apply to
files.
File Stream Classes
.NET provides a lot of classes for handling files. These are contained in the
System.IO namespace.
172
Pantech Academy of Technical Excellence
Listed below are various classes under this namespace.
Class Name Description
FileStream Is used to read from and write to any location within a file.
BinaryReader Is used to read primitive data types in the form of binary data from the stream.
StreamReader Is used to read characters from a byte stream.
StringReader Is used to read data from a string buffer.
BinaryWriter Is used to write primitive data types in the form of binary data to the stream.
StreamWriter Is used to write characters to a byte stream.
StringWriter Is used to write data to a string buffer.
DirectoryInfo Is used to perform operations on directories
FileInfo Is used to perform operations on files.
The FileStream class resides under the System.IO namespace and is derived from the
abstract class Stream. It supports random access through seeking. Before you begin reading
from and writing to a file, an object of the FileStream class needs to initialized. The
parameterized constructor of this class allows create/open files and setting up appropriate file
sharing modes.
Creating a new file
FileStream fs = new FileStream
(
"TestFile.txt",
FileMode.Create,
FileAccess.Write,
FileShare.Read
);
The first parameter to the constructor is the path to the file. In this case the new file will be
created in the working directory of the application. A complete path such as C:\Program
Files\Maxotek\Tutorials\C-Sharp\Lesson14\TestFile.txt can also be used. Backslash being the
escape sequence starter would need to be escaped in the string. The code would look
something like:-
173
Pantech Academy of Technical Excellence
FileStream fs = new FileStream
(
"C:\\Program Files\\Maxotek\\Tutorials\\C-Sharp\\Lesson14\\TestFile.txt",
FileMode.Create,
FileAccess.Write,
FileShare.Read
);
Another way of doing this, is to use the @ character before the string. This prevents
any escape sequence character as the backslash itself is taken as a normal character.
FileStream fs = new FileStream
(
@"C:\Program Files\Maxotek\Tutorials\C-Sharp\Lesson14\TestFile.txt",
FileMode.Create,
FileAccess.Write,
FileShare.Read
);
The second parameter to the constructor is the FileMode enumeration. It can have any of the
following values.
File Mode Description
Create Creates a new file or truncates the existing file.
CreateNew Creates a new file. If the file already exists, throws a
System.IO.IOException.
Open Opens an existing file. Throws a System.IO.FileNotFoundException if
the file does not exist.
OpenOrCreate Opens an existing file or creates a new one. The difference between this
mode and the Create mode is that it does not truncate the file.
Append Opens the file and seeks to the end. A new file is created if the file does
not exist
Truncate Opens an existing file and truncates its content so that the file size becomes
zero. Attempting to read in this mode throws an exception.
174
Pantech Academy of Technical Excellence
The third parameter is the FileAccess which specifies whether the file is to be opened for
Reading (FileAccess.Read), Writing (FileAccess.Write) or both
(FileAccess.ReadWrite).
The last parameter is FileShare mode which states how the file will be shared.
FileShare Mode Description
Read Allows other handles to read from the file.
Write Allows other handles to write to the file.
Delete Allows subsequent deleting of the file.
Inheritable Makes the file handle inheritable to child processes.
NoneDeclines sharing of file. No other process can open
the file until it is closed.
Writing to the File
We use the StreamWriter class which is inherited from the abstract class TextWriter
to write a series of characters. After opening the file in the appropriate mode (Write or
ReadWrite in this case), a new object of the StreamWriter class is created. The object of the
FileStream class is passed to it, thus associating the stream with the file. After this, the
content can be written to the file by using the Write or the WriteLine method. WriteLine
appends an end of line to the content. These two methods are highly overloaded to enable
almost all built in data types to be directly written.
Once you have done all the writing, make sure you close the file using the Close
method. This ensures all the content is physically written to the file. For performance reasons,
the write operation is buffered and sometimes the content might not be physically written to
the file even after the Write method was called. To force all the buffers to be cleared and all
buffered content to be written to the underlying stream (File in our case), use the Flush
method.
175
Pantech Academy of Technical Excellence
FileStream fs = new FileStream
(
"TestFile.txt",
FileMode.Create,
FileAccess.Write,
FileShare.Read
);
StreamWriter sw = new StreamWriter(fs);
sw.Write("Hello World");
sw.Close(); // Close the Stream
fs.Close(); // Close the File
Reading from a file
The StreamReader class is used to read a series of characters from a FilStream. This
class is derived from the abstract class TextReader. Before reading, the file must be opened
in the appropriate mode (Read or ReadWrite). To Read the next character or the next set of
characters, use the Read method. This method has two forms. One that does not take any
parameters and reads the next character. It returns the byte value of the character. The other
one takes three parameters:-
An array of characters - The read characters are stored in the variable passed here.
The index of the buffer at which to begin writing
Maximum number of characters to read
The ReadLine method reads a line of characters and returns the result in the form of a string.
FileStream fs = new FileStream
(
"TestFile.txt",
FileMode.Open,
FileAccess.Read,
FileShare.Read
);
StreamReader sr = new StreamReader(fs);
string str = sr.ReadLine();
176
Pantech Academy of Technical Excellence
while (str != null)
{
Console.WriteLine(str);
str = sr.ReadLine();
}
sr.Close(); // Close the Stream
fs.Close(); // Close the File
The file pointer can be seeked to any position of the file using the BaseStream
property’s Seek method. This method takes two parameters. The first, a byte offset relative to
the Origin (second) parameter. This value can be either positive which moves the pointer
down the file or negative which moves the file pointer upwards. The second parameter
known as the Origin parameter indicates the reference point to obtain the new position. The
permissible values are Begin, Current and End.
The following line of code moves the file pointer to the 5th character from the beginning.
sr.BaseStream.Seek(5, SeekOrigin.Begin);
Moves the pointer to the start.
sr.BaseStream.Seek(0, SeekOrigin.Begin);
Moves the pointer to 5 characters ahead of the current position.
sr.BaseStream.Seek(5, SeekOrigin.Current);
Moves the pointer 5 characters backward from the current position.
sr.BaseStream.Seek(-5, SeekOrigin.Begin);
Moves the pointer to the end.
sr.BaseStream.Seek(0, SeekOrigin.End);
The Peek method is used to read the next character without consuming it, i.e the file pointer
does not move ahead.
177
Pantech Academy of Technical Excellence
Implementing Binary READ and WRITE
The StreamReader and StreamWriter classes work with character data. In this mode
everything is written as plain text. To implement binary operations, wherein a number such as
327.68 would be written as a float value consuming four bytes and not just as plain text, we
need to use the BinaryReader and BinaryWriter classes.
They are very similar to their text counterparts except that no Line (ReadLine and
WriteLine) methods are provided. The BinaryReader class also supports data type specific
methods such as ReadInt32, ReadDouble, etc. These allow you to directly read the content
from the file in the appropriate format. If we were to use the StreamReader class, we would
have to convert the string to integer or double format using the Convert.ToInt32 and
Convert.ToDouble methods.]
Writing to File
int MyNumber = 69;
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(MyNumber);
Reading from file
BinaryReader br = new BinaryReader(fs);
int MyNumber = br.ReadInt32();
Implementing the Windows File System
Often we need to browse directories and locate files. Many such operations can be
accomplished by using the two classes - DirectoryInfo and FileInfo. Both of these are derived
from the FileSystemInfo class.
The tables below list some of the commonly used properties and methods corresponding to
both of these classes.
178
Pantech Academy of Technical Excellence
Properties of DirectoryInfo Class
Property Description
Attributes Gets or sets attributes associated with the current directory.
CreationTime Gets or sets the Creation time of the directory.
Exists Gets a Boolean value indicating whether the directory exists.
FullName Gets a string containing the full path of the directory.
LastAccessTime Gets the last accessed time of the directory.
Name Gets a string containing the name of the directory.
Methods of DirectoryInfo Class
Method Description
Create Creates a directory.
Delete Deletes a directory.
GetDirectories Returns the directories in the current directory. Supports filtering
and recursive listing.
GetFiles Returns the file in the current directory.
Properties of FileInfo class
Property Description
Attributes Gets or sets the attributes associated with the current file.
CreationTime Gets or sets the CreationTime of the file.
Directory Gets an instance of the directory to which the file belongs to.
Exists Gets a Boolean value indicating whether the file exists.
Extension Gets a string containing the extension of the file.
FullName Gets a string containing the full path to the file.
LastAccessTime Gets the last access time of the file.
LastWriteTime Gets the time of the last wriiten activity on the file.
Length Gets the length of the file in Bytes.
Name Gets the name of the File.
179
Pantech Academy of Technical Excellence
Methods of FileInfo class
Method Description
Create Creates a new file.
AppendText Appends a text to the file.
Delete Deletes the file.
Open Opens the file.
OpenRead Opens a file in the read-only mode.
Creating Objects
Creating a DirectoryInfo or FileInfo object is very simple. The constructor takes the path to
the file.
DirectoryInfo di = new DirectoryInfo("C:\\Windows");
FileInfo fi = new FileInfo("C:\\Windows\\Notepad.exe");
Listing All Files in a Directory
The following code snippet lists all the files in the C:\Windows directory.
DirectoryInfo di = new DirectoryInfo("C:\\Windows");
FileInfo[ ] fis = di.GetFiles();
foreach (FileInfo fi in fis)
{ Console.WriteLine(fi.Name); }
180
Pantech Academy of Technical Excellence
MULTI THREADING
181
Multi Threading
System, Threading
Thread Synchronization
Critical Sections
Thread Life Cycle
Pantech Academy of Technical Excellence
What is a thread?
A thread is nothing more than a process. On the computer, a thread is a process
moving through time. The process performs sets of sequential steps, each step executing a
line of code. Because the steps are sequential, each step takes a given amount of time. The
time it takes to complete a series of steps is the sum of the time it takes to perform each
programming step.
Problems with Threading
If every process in your program was mutually exclusive - that is, no process depended in any
way upon another, then multiple threading would be very easy and very few problems would
occur. Each process would run along in its own happy course and not bother the other
processes. However, when more than one process needs to read or write the memory used by
other processes, problems can occur. With a multithreaded program, two threads can enter a
piece of code at the same time, and wreak havoc on the results. The problem with threads is
that you need some way to control one thread accessing a shared piece of memory while
another thread running at the same time is allowed to enter the same code and manipulate the
shared data.
Thread Safety
In our program we force one thread to wait inside our code block while the other
thread is finishing its business. This activity, known as thread blocking or synchronizing
threads, allows us to control the timing of simultaneous threads running inside our program.
In C# we lock on a particular part of memory (usually an instance of an object) and don't
allow any thread to enter code of this object's memory until another thread is done using the
object.
Thread Synchronization
182
Pantech Academy of Technical Excellence
The .NET framework provides a number of classes and data types that you can use to
control the access to shared resources.
You can use the System.Threading.Monitor class to lock a section of code in an object's
method that should not be accessed concurrently by many threads.
System.Threading.WaitHandle objects help you respond to actions taken by other threads,
especially when interoperating with unmanaged code. Wait-based techniques use this class to
perform waits on one or more waitable objects.
The System.Threading.Mutex class allows more complex thread synchronization. It
allows single-threaded access. Thread synchronization refers to the act of shielding against
multithreading issues such as data- races, deadlocks and starvation.
The synchronization event classes like the ManualResetEvent and AutoResetEvent
(both in System.Threading namespace) allow one thread to notify the other threads of some
event. The common language runtime (CLR) provides three ways to synchronize access to
global fields, specific blocks of method, instance and static methods and fields.
Synchronized code regions (SyncBlock based): You can synchronize either the
entire static/instance methods or a part of them using the Monitor class. There is no
support for synchronized static fields. On instance methods, the current object (this
keyword, in C#) is used for synchronization. On static methods, the class is used for
synchronization.
Classic Manual Synchronization: Use the various synchronization classes (like the
WaitHandle, Mutex, ReaderWriterLock, ManualResetEvent, AutoResetEvent and the
Interlocked) to create your own synchronization mechanisms. You have to manually
synchronize the different fields and methods. The Manual based approach can be used
for interprocess synchronization and offers deadlock free waits for multiple resources.
These are two improvements over the SyncBlock based techniques.
Synchronized contexts: You can also use the SynchronizationAttribute to enable
simple, automatic synchronization for ContextBoundObject objects. You can use this
183
Pantech Academy of Technical Excellence
technique to synchronize only the instance fields and methods. All objects in the same
context domain share the same lock.
Monitor Class
The Monitor class (in the System.Threading namespace) is useful in situations where you
want a particular region of code to be used only by a single thread at a given point of time.
All the methods in this class are static so you do not need to instantiate this class. These static
methods provide a mechanism to synchronize access to objects thus protecting them against
data-races or deadlocks. A few of the methods are shown below:
Enter, TryEnter
Exit
Pulse / PulseAll
Wait
You can synchronize access to a piece of code by locking and unlocking a particular
object. The methods Monitor.Enter, Monitor.TryEnter and Monitor.Exit are used to
lock/unlock an object. Once a lock is obtained on a particular piece of code, no other thread
can obtain a lock on the same region of code.
The methods Pulse, PulseAll and Wait must be invoked from within a synchronized
block of code. For each synchronized object you need to maintain a reference to the thread
that currently holds the lock, a reference to a ready queue and a reference to a waiting queue
(that contains the threads that are waiting for notification of a change in the state of the
locked object).
If two threads call Monitor. Enter simultaneously. However close they might be in
calling the Enter method, one thread would always be the one to call it first, and that thread
gets a chance to lock the object. Since, Monitor. Enter is an atomic operation, the CPU cannot
preempt one thread in favor of another. For better performance, you should delay the process
of acquiring lock on the object and should release the lock as soon as possible. It is advisable
to acquire locks on private or internal objects, locking external objects might result in
184
Pantech Academy of Technical Excellence
deadlocks, because unrelated code could choose the same objects to lock on for different
purposes.
If it is a block of code on which you want to acquire a lock, then it is better to place
the set of instructions in a try block and to place the Monitor.Exit in the finally block. If it is
an entire method on which you want to acquire a lock (rather than a few lines of code) then
you can use the class MethodImplAttribute (in System.Runtime.CompilerServices
namespace) specifying the Synchronized value in the constructor of the class. This is an
alternative way of working. Since the lock is applied to the entire method the lock is released
when the method returns. But if you want to release the lock sooner then use the Monitor
class or the C# lock statement instead of this attribute.
The C# lock statement provides the same functionality as that provided by the Enter
and Exit methods. Use lock when you have a section of code that should not be interrupted by
code running on a separate thread.
WaitHandle Class
The WaitHandle class (in the System.Threading namespace) is used as a base class for
all synchronization objects that allow multiple wait operations. This class encapsulates the
Win32 synchronization handles. WaitHandle objects signal the status of one thread to another
thereby notifying other threads that they need exclusive access to a resource. Other threads
must then wait, until the wait handle is no longer in use, to use this resource.
Classes derived from this are:
Mutex
AutoResetEvent
ManualResetEvent
These classes define a signaling mechanism to take or release exclusive access to a shared
resource. They have two states, signaled and nonsignaled. A wait handle that is not owned by
any thread is in the signaled state otherwise nonsignaled. Threads that own a wait handle call
the Set method when they no longer need the wait handle. Other threads can thus call Reset
185
Pantech Academy of Technical Excellence
(to change the status to nonsignaled) or call any one of the WaitHandle methods (shown
below) to request ownership of a wait handle.
These are:
WaitOne: accepts a wait handle (as an argument) and causes the calling thread to wait
until the current wait handle signal by calling Set.
WaitAny: accepts an array of wait handles (as an argument) and causes the
calling thread to wait until any one of the specified wait handles signal by calling Set.
WaitAll: accepts an array of wait handles (as an argument) and causes the
calling thread to wait until all specified wait handles signal by calling Set.
These wait methods block a thread (similar to the Join method, on individual threads)
until one or more synchronization objects receive a signal. WaitHandle objects represent the
ability to wait on many objects at once and are operating-system waitable objects that are
useful for synchronizing between managed and unmanaged code. But they are less portable
than Monitor objects. Monitor objects are fully managed and are more efficient in their use of
operating system resources.
Mutex Class
The Mutex class (in the System.Threading namespace) is another way of achieving
synchronization between threads and across processes. This class provides interprocess
synchronization. It allows a thread to have exclusive access to a shared resource thus
preventing simultaneous access by multiple threads or processes. The name mutex itself
suggests that the ownership of the mutex is mutually exclusive. Once one thread acquires a
mutex the other thread that wants to acquire the mutex is suspended till the first thread
releases it.
The method Mutex.ReleaseMutex must be called to release the mutex. A thread can
request the same mutex in repeated calls to Wait but must call the Mutex.ReleaseMutex the
same number of times to release the ownership of the mutex. If no thread owns a mutex (or
the thread that owns a mutex, terminates normally) then the state of the Mutex object is set to
186
Pantech Academy of Technical Excellence
signaled otherwise nonsignaled. Once the state is set to signaled the next thread waiting in
the queue acquires the mutex. The Mutex class corresponds to the Win32 CreateMutex call.
The creation of a Mutex object is very simple. There are three ways of doing so:
public .ctor(); - creates a Mutex and sets the ownership to the calling thread.
public .ctor(bool owner); - allows you to decide whether the thread calling the Mutex
needs to be the owner or not.
public .ctor(bool owner string name); - also specifies the name of the Mutex.
A thread can always get the ownership of the mutex by calling any one of the methods
like WaitHandle.WaitOne or WaitHandle.WaitAny or WaitHandle.WaitAll. If no other
thread currently owns the mutex then the calling thread would be granted ownership and the
method WaitOne will return immediately.
But, if any other thread owns the mutex, then WaitOne will spin infinitely till it gets
access to the mutex. You may specify the time (in milliseconds) on the WaitOne method.
This will prevent the infinite wait on the mutex. You can call Close method on the mutex to
release it. Once a mutex has been created, we can use the GetHandle method to obtain a
handle to the mutex that can be used with the WaitHandle.WaitAny or WaitHandle.WaitAll
methods.
Synchronization Events
Synchronization events are wait handles that are used to notify other threads that
something has occurred or that a resource is available. They have two states: signaled and
nonsignaled. There are two synchronization event classes: AutoResetEvent and the
ManualResetEvent.
AutoResetEvent Class
This class notifies one or more waiting threads that an event has occurred. It
187
Pantech Academy of Technical Excellence
automatically changes the status to signaled when a waiting thread is released. Instances of
the AutoResetEvent class can also be set to signaled using Set, but the state will
automatically become nonsignaled by the system as soon as a waiting thread is notified that
the event became signaled. If no threads are waiting to listen to the event, the state remains
signaled. This class cannot be inherited.
ManualResetEvent Class
This class also notifies one or more waiting threads that an event has occurred. The
state of the event can be manually set or reset. The state of a manually reset event remains
signaled until the ManualResetEvent.Reset method sets it to the nonsignaled state and the
state remains nonsignaled until the ManualResetEvent.Set method changes the state back to
signaled. This class cannot be inherited.
Interlocked Class
This class (in the System.Threading namespace) helps to synchronize access to
variables that are shared amongst threads. It thus provides atomic operations for variables that
are shared by multiple threads.
You can increment or decrement a shared variable by calling Interlocked.Increment or
Interlocked.Decrement on the shared variable. The advantage is that the two methods operate
in an "atomic" manner meaning that the methods take an integer, increment (or decrement) it
and return its new value, all in one step. You can also use this class to set the variables to a
specific value (done with Interlocked.Exchange method) or to check the equality of two
variables, if they are equal, replaces one of the variables with a given value
(Interlocked.CompareExchange method).
ReaderWriterLock class
This class (in the System.Threading namespace) defines a lock that works on the
single- writer/multiple-reader mechanism. Thus it offers read/write-aware synchronization.
Any number of threads can read data concurrently. Data locking is needed only when threads
188
Pantech Academy of Technical Excellence
are updating. Reader threads can acquire locks, if and only if, there are no writer threads.
Writer threads can acquire locks if there are no reader and no writer threads. Hence, once a
writer-lock is requested, no new readers will be accepted until the writer has access. It
supports time-outs, thus preventing deadlocks. It also supports nested reader/writer locks.
The function that supports nested reader locks is
ReaderWriterLock.AcquireReaderLock. This thread blocks if a different thread has the writer
lock. The function that supports nested writer locks is ReaderWriterLock.AcquireWriterLock.
This thread blocks if a different thread has the reader lock. Worse it can deadlock if it
has the reader lock. It is always safe to use the ReaderWriterLock.UpgradeToWriterLock
function. This will upgrade the reader thread to writer. You can also change the writer thread
to a reader. The function that does this is called
ReaderWriterLock.DowngradeFromWriterLock.
You can call ReaderWriterLock.ReleaseLock to release the lock and you can use
ReaderWriterLock.RestoreLock to restore the lock state of the thread to what it was before
calling ReaderWriterLock.ReleaseLock.
Thread States: Life Cycle of a Thread
A thread is said to be in one of several thread states. Two classes critical for
multithreaded applications are Thread and Monitor (System.Threading namespace). This
section also discusses several methods of classes Thread and Monitor that cause state
transitions.
A new thread begins its lifecyle in the Unstarted state. The thread remains in the
Unstarted state until the program calls Thread method Start, which places the thread in the
Started state (sometimes called the Ready or Runnable state) and immediately returns control
to the calling thread. Then the thread that invoked Start, the newly Started thread and any
other threads in the program execute concurrently.
189
Pantech Academy of Technical Excellence
Figure: Thread Life Cycle
The highest priority Started thread enters the Running state (i.e., begins executing)
when the operating system assigns a processor to the thread. When a Started thread receives a
processor for the first time and becomes a Running thread, the thread executes its ThreadStart
delegate, which specifies the actions the thread will perform during its lifecyle. When a
program creates a new Thread, the program specifies the Thread's ThreadStart delegate as the
argument to the Thread constructor. The ThreadStart delegate must be a method that returns
void and takes no arguments.
A Running thread enters the Stopped (or Dead) state when its ThreadStart delegate
terminates. Note that a program can force a thread into the Stopped state by calling Thread
method Abort on the appropriate Thread object. Method Abort throws a
ThreadAbortException in the thread, normally causing the thread to terminate. When a thread
is in the Stopped state and there are no references to the thread object, the garbage collector
can remove the thread object from memory.
A thread enters the Blocked state when the thread issues an input/output request. The
operating system blocks the thread from executing until the operating system can complete
190
Pantech Academy of Technical Excellence
the I/O for which the thread is waiting. At that point, the thread returns to the Started state, so
it can resume execution. A Blocked thread cannot use a processor even if one is available.
There are three ways in which a Running thread enters the WaitSleepJoin state. If a
thread encounters code that it cannot execute yet (normally because a condition is not
satisfied), the thread can call Monitor method Wait to enter the WaitSleepJoin state. Once in
this state, a thread returns to the Started state when another thread invokes Monitor method
Pulse or PulseAll. Method Pulse moves the next waiting thread back to the Started state.
Method PulseAll moves all waiting threads back to the Started state.
A Running thread can call Thread method Sleep to enter the WaitSleepJoin state for a
period of milliseconds specified as the argument to Sleep. A sleeping thread returns to the
Started state when its designated sleep time expires. Sleeping threads cannot use a processor,
even if one is available.
Any thread that enters the WaitSleepJoin state by calling Monitor method Wait or by
calling Thread method Sleep also leaves the WaitSleepJoin state and returns to the Started
state if the sleeping or waiting Thread's Interrupt method is called by another thread in the
program.
If a thread cannot continue executing (we will call this the dependent thread) unless
another thread terminates, the dependent thread calls the other thread's Join method to "join"
the two threads. When two threads are "joined," the dependent thread leaves the
WaitSleepJoin state when the other thread finishes execution (enters the Stopped state). If a
Running Thread's Suspend method is called, the Running thread enters the Suspended state. A
Suspended thread returns to the Started state when another thread in the program invokes the
Suspended thread's Resume method.
191
Pantech Academy of Technical Excellence
WINDOWS SERVICES
192
Windows Services
Service Base Class
Service Process Installer
Service Installer
Creating a Windows Service
Install Util.exe
Pantech Academy of Technical Excellence
What is a Windows Service?
Windows Service applications are long-running applications that are ideal for use in
server environments. The applications do not have a user interface or produce any visual
output. Any user messages are typically written to the Windows Event Log. Services can be
automatically started when the computer is booted. They do not require a logged in user in
order to execute and can run under the context of any user including the system. Windows
Services are controlled through the Service Control Manager where they can be stopped,
paused, and started as needed.
Service Base Class
Provides a base class for a service that will exist as part of a service application.
ServiceBase must be derived from when creating a new service class.
Derive from ServiceBase when defining your service class in a service application.
Any useful service overrides the OnStart and OnStop methods. For additional functionality,
you can override OnPause and OnContinue with specific behavior in response to changes in
the service state.
A service is a long-running executable that does not support a user interface, and
which might not run under the logged-on user account. The service can run without any user
being logged on to the computer.
By default, services run under the System account, which is not the same as the
Administrator account. You cannot change the rights of the System account. Alternatively,
you can use a ServiceProcessInstaller to specify a user account under which the service will
run.
An executable can contain more than one service but must contain a separate
ServiceInstaller for each service. The ServiceInstaller instance registers the service with the
system. The installer also associates each service with an event log that you can use to record
service commands. The main() function in the executable defines which services should run.
193
Pantech Academy of Technical Excellence
The current working directory of the service is the system directory, not the directory in
which the executable is located.
When you start a service, the system locates the executable and runs the OnStart
method for that service, contained within the executable. However, running the service is not
the same as running the executable. The executable only loads the service. The service is
accessed (for example, started and stopped) through the Service Control Manager.
The executable calls the ServiceBase derived class's constructor the first time you call
Start on the service. The OnStart command-handling method is called immediately after the
constructor executes. The constructor is not executed again after the first time the service has
been loaded, so it is necessary to separate the processing performed by the constructor from
that performed by OnStart. Any resources that can be released by OnStop should be created
in OnStart. Creating resources in the constructor prevents them from being created properly if
the service is started again after OnStop has released the resources.
The Service Control Manager (SCM) provides a way to interact with the service. You
can use the SCM to pass Start, Stop, Pause, Continue, or custom commands into the service.
The SCM uses the values of CanStop and CanPauseAndContinue to determine whether the
service accepts Stop, Pause, or Continue commands. Stop, Pause, and Continue are enabled
in the SCM's context menus only if the corresponding property CanStop or
CanPauseAndContinue is true in the service class. If enabled, the command is passed to the
service, and OnStop, OnPause, or OnContinue is called. If CanStop, CanShutdown, or
CanPauseAndContinue is false, the corresponding command-handling method (such as
OnStop) will not be processed, even if you have implemented the method.
You can use the ServiceController class to do programmatically what the SCM does
using a user interface. You can automate the tasks available in the console. If CanStop,
CanShutdown, or CanPauseAndContinue is true but you have not implemented a
corresponding command-handling method (such as OnStop) the system throws an exception
and ignores the command.
You do not have to implement OnStart, OnStop, or any other method in ServiceBase.
However, the service's behavior is described in OnStart, so at minimum, this member should
194
Pantech Academy of Technical Excellence
be overridden. The main() function of the executable registers the service in the executable
with the Service Control Manager by calling the Run method. The ServiceName property of
the ServiceBase object passed to the Run method must match the ServiceName property of
the service installer for that service.
ServiceProcessInstaller
Installs an executable containing classes that extend ServiceBase. This class is called
by installation utilities, such as InstallUtil.exe, when installing a service application.
The ServiceProcessInstaller does work common to all services in an executable. It is
used by the installation utility to write registry values associated with services you want to
install.
To install a service, create a project installer class that inherits from Installer, and set
the RunInstallerAttribute on the class to true. Within your project, instantiate one
ServiceProcessInstaller instance per service application, and one ServiceInstaller instance for
each service in the application. Finally, add the ServiceProcessInstaller instance and the
ServiceInstaller instances to your project installer class.
When InstallUtil.exe runs, the utility looks for classes in the service assembly with the
RunInstallerAttribute set to true. Add classes to the service assembly by adding them to the
Installers collection associated with your project installer. If Run Installer Attribute is false,
the install utility ignores the project installer.
For an instance of ServiceProcessInstaller, properties you can modify include
specifying that a service application run under an account other than the logged-on user. You
can specify a particular Username and Password pair under which the service should run, or
you can use Account to specify that the service run under the computer's System account, a
local or network service account, or a user account.
195
Pantech Academy of Technical Excellence
Notes:
ServiceInstaller Class
Installs a class that extends ServiceBase to implement a service. This class is called
by the install utility when installing a service application.
The ServiceInstaller does work specific to the service with which it is associated. It is
used by the installation utility to write registry values associated with the service to a subkey
within the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services registry key.
The service is identified by its ServiceName within this subkey. The subkey also includes the
name of the executable or .dll to which the service belongs.
To install a service, create a project installer class that inherits from the Installer class,
and set the RunInstallerAttribute attribute on the class to true. Within your project, instantiate
one ServiceProcessInstaller instance per service application, and one ServiceInstaller instance
for each service in the application. Finally, add the ServiceProcessInstaller instance and the
ServiceInstaller instances to your project installer class.
196
The computer's System account is not the same as the Administrator account. We do not
call the methods on ServiceInstaller within your code; they are generally called only by the
install utility. The install utility automatically calls the ServiceProcessInstaller.Install and
ServiceInstaller.Install methods during the installation process. It backs out failures, if necessary,
by calling Rollback (or ServiceInstaller.Rollback) on all previously installed components.
An application's install routine maintains information automatically about the components
already installed, using the project installer's Installer.Context. This state information is
continuously updated as the ServiceProcessInstaller instance and each ServiceInstaller instance is
installed by the utility. It is usually unnecessary for your code to modify this state information
explicitly.Instantiating a ServiceProcessInstaller causes the base class constructor,
ComponentInstaller, to be called.
Pantech Academy of Technical Excellence
When the install utility is called, it looks for the RunInstallerAttribute attribute. If the
attribute is true, the utility installs all the services that were added to the Installers collection
that were associated with your project installer. If RunInstallerAttribute is false or does not
exist, the install utility ignores the project installer.
Notes:
You can modify other properties on the ServiceInstaller either before or after adding it
to the Installers collection of your project installer. For example, a service's StartType may be
set to start the service automatically at reboot or require a user to start the service manually.
Normally, you will not call the methods on ServiceInstaller within your code; they are
generally called only by the install utility. The install utility automatically calls the
ServiceProcessInstaller.Install and ServiceInstaller.Install methods during the installation
process. It backs out failures, if necessary, by calling Rollback (or ServiceInstaller.Rollback)
on all previously installed components.
The installation utility calls Uninstall to remove the object. An application's install
routine maintains information automatically about the components already installed, using
the project installer's Installer.Context. This state information is continuously updated as the
ServiceProcessInstaller instance, and each ServiceInstaller instance is installed by the utility.
It is usually unnecessary for your code to modify state information explicitly.
When the installation is performed, it automatically creates an EventLogInstaller to
install the event log source associated with the ServiceBase derived class. The Log property
for this source is set by the ServiceInstaller constructor to the computer's Application log.
When you set the ServiceName of the ServiceInstaller (which should be identical to the
ServiceBase.ServiceName of the service), the Source is automatically set to the same value.
197
It is crucial that the ServiceName be identical to the ServiceBase.ServiceName of the class
you derived from ServiceBase. Normally, the value of the ServiceBase.ServiceName property
for the service is set within the Main() function of the service application's executable. The
Service Control Manager uses the ServiceInstaller.ServiceName property to locate the service
within this executable.
Pantech Academy of Technical Excellence
In an installation failure, the source's installation is rolled-back along with previously
installed services.
The Uninstall method tries to stop the service if it is running. Whether this succeeds
or not, Uninstall undoes the changes made by Install. If a new source was created for event
logging, the source is deleted.
Installutil.exe
The Installer tool allows you to install and uninstall server resources by executing the
installer components in a specified assembly. This tool works in conjunction with classes in
the System.Configuration.Install Namespace.
installutil [/uninstall][option [...]]assemblyname ]
[option [...]]assemblyname
Here:
Argument Description
assemblyname The name of the assembly in which to execute the
installer components.
Option Description
/h[elp] Displays command syntax and options for the tool.
/help assemblypath Displays any additional options recognized by
individual installers within the specified assembly.
/? Displays command syntax and options for the tool.
/? assemblypath Displays any additional options recognized by
individual installers within the specified assembly.
/LogFile=[filename] Specifies the name of the log file where install
progress is recorded. The default is
assemblyname.InstallLog.
/AssemblyName assemblyName Specifies the name of an assembly. The assembly
198
Pantech Academy of Technical Excellence
[,Version=major.minor.build.revision]
[,Culture=locale]
[,PublicKeyToken=publicKeyToken]]
name must be fully qualified with the version,
culture, and public key token of the assembly. The
fully qualified name must be surrounded by quotes.
For example, "myAssembly, Culture=neutral,
PublicKeyToken=0038abc9deabfle5,
Version=2.0.0.0" is a fully qualified assembly name.
/LogToConsole={true|false} If true, displays output to the console. If false (the
default), suppresses output to the console.
/ShowCallStack Prints the call stack to the log if an exception occurs
at any point during installation.
/u[ninstall] Uninstalls an assembly. Unlike other options, /u
applies to all assemblies regardless of where it
appears on the command line.
Starting with the .NET Framework version 2.0, the 32-bit version of
the common language runtime (CLR) continues to ship with only the 32-bit version of the
Installer tool, but the 64-bit version of the CLR ships with both a 32-bit and a 64-bit version
of the Installer tool.
When using the 64-bit CLR, use the 32-bit Installer tool to install 32-bit assemblies,
and the 64-bit Installer tool to install 64-bit and Microsoft intermediate language assemblies.
Otherwise, both versions of the Installer tool behave the same.
Microsoft .NET Framework applications consist of traditional program files and
associated resources, such as message queues, event logs, and performance counters that
must be created when the application is deployed. You can use an assembly's installer
components to create these resources when your application is installed and to remove them
when your application is uninstalled. Installutil.exe detects and executes these installer
components.
You can specify multiple assemblies on the same command line. Any option that
occurs before an assembly name applies to that assembly's installation. Options specified for
199
Pantech Academy of Technical Excellence
one assembly apply to any subsequent assemblies unless the option is specified with a new
assembly name.
Installutil.exe uses reflection to inspect the specified assembly and find all Installer
types with the RunInstallerAttribute set to true. The tool then executes either the Install
Method or the Uninstall Method on each instance of the Installer type. Installutil.exe
performs installation in a transactional manner; if one of the assemblies fails to install, it rolls
back the installations of all other assemblies. Uninstall is not transactional.
Installutil.exe can not install or uninstall delay signed assemblies, but can install or
uninstall strong named assemblies.
Notes
Create a Windows Service
The service we will create does nothing really useful other than serve as a
demonstration. When the service is started we will log an entry in a database indicating that it
has started. The service will create a database entry on a specified interval during the time in
which it is running. The service will create a final database entry when stopping. The service
will also automatically log an entry in the Windows Application Log when it is successfully
started or stopped.\
Visual Studio .NET makes it relatively simple to create a Windows Service. The
instructions for starting our demo service are outlined below.
200
You cannot deploy a Windows service created using C++ with Installutil.exe.
Installutil.exe cannot recognize the embedded native code that is produced by the C++
compiler. If you attempt to deploy a C++ Windows service with Installutil.exe, an exception
such as BadImageFormatException will be thrown. To work with this scenario, move the
service code to a C++ module. Then, write the installer object in C# or Visual Basic.
Pantech Academy of Technical Excellence
1. Start a new project
2. Select Windows Service from the list of available project templates
3. The designer will open in design mode
4. Drag a Timer object from the Components tab in the Toolbox onto the design surface
(Warning: make sure you use the Timer from the Components tab and not the one
from the Windows Forms tab)
5. Through the Timer properties set the Enabled property to False and the Interval
property to 30000 milliseconds
6. Switch to the code behind view (F7) to add functionality to the service
Makeup of a Windows Service
In the code behind class you will notice that your Windows Service extends the
System.ServiceProcess.Service class. All Windows Services built in .NET must extend this
class. It requires your service to override the following methods which Visual Studio will
include by default.
Dispose - clean up any managed and unmanaged resources
OnStart - control the service startup
OnStop - control the service stoppage
Sample Database Table Script
The following T-SQL script can be used to create the database table used in the
example. I am using SQL Server as my database of choice. You can easily modify this
example to work with Access or any other database of your choice.
CREATE TABLE [dbo].[MyServiceLog] (
[in_LogId] [int] IDENTITY (1, 1) NOT NULL,
[vc_Status] [nvarchar] (40)
COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[dt_Created] [datetime] NOT NULL
) ON [PRIMARY]
201
Pantech Academy of Technical Excellence
Install the Windows Service
Windows Services are different than normal Windows based applications. It is not
possible to run a Windows Service by simply running an EXE. The Windows Service should
be installed by using the InstallUtil.exe provided with the .NET Framework or through a
deployment project such as a Microsoft Installer (MSI) file.
Add an Installer
Having created the Windows Service will not be enough for the InstallUtil program to be
able to install the service. You must also add an Installer to your Windows Service so that the
InstallUtil, or any other installation program, knows what configuration settings to apply to
your service.
1. Switch to the design view for the service
2. Right click and select Add Installer
3. Switch to the design view of the ProjectInstaller that is added
4. Set the properties of the serviceInstaller1 component
i. ServiceName = My Sample Service
ii. StartType = Automatic
5. Set the properties of the serviceProcessInstaller1 component
a. Account = LocalSystem
b. Build the Solution
Use InstallUtil to Install the Windows Service
Now that the service has been built you need to install it for use. The following
instructions will guide you in installing your new service.
1. Open a Visual Studio .NET Command Prompt
2. Change to the bin\Debug directory of your project location (bin\Release if you
compiled in release mode)
202
Pantech Academy of Technical Excellence
3. Issue the command InstallUtil.exe MyWindowsService.exe to register the service and
have it create the appropriate registry entries
4. Open the Computer Management console by right clicking on My Computer on the
desktop and selecting Manage
5. In the Services section underneath Services and Applications you should now see
your Windows Service included in the list of services
6. Start your service by right clicking on it and selecting Start
Each time you need to change your Windows Service it will require you to uninstall and
reinstall the service. Prior to uninstalling the service it is a good idea to make sure you have
the Services management console closed. If you do not you may have difficulty uninstalling
and reinstalling the Windows Service. To uninstall the service simply reissue the same
InstallUtil command used to register the service and add the /u command switch.
Debug the Windows Service
As with all other aspects, debugging a Windows Service is different than a normal
application. More steps are required to debug a Windows Service. The service cannot be
debugged by simply executing it in the development environment as you can with a normal
application. The service must first be installed and started, which we covered in the previous
section. Once it is started you attach Visual Studio to the running process in order to step
through and debug the code. Remember, each change you make to the Windows Service will
require you to uninstall and reinstall the service.
Attach to a Running Windows Service
Here are the directions for attaching to a Windows Service in order to debug the
application. These instructions assume that you have already installed the Windows Service
and it is currently running.
1. Load the project into Visual Studio
2. Click on the Debug menu
3. Click on the Processes menu item
4. Make sure the Show system processes is selected
203
Pantech Academy of Technical Excellence
5. Locate your process in the Available Processes list based on the name of your
executable and click on it
6. Click the Attach button
7. Click OK
8. Click Close
9. Set a break point in the timer1_Elapsed method and wait for it to execute
204
Pantech Academy of Technical Excellence
GUI APPLICATION DEVELOPMENT
205
GUI Application Development
Windows Forms and Controls
Creating Menus
Tool Bars, Image List
Tree View, List View
Pantech Academy of Technical Excellence
Tool bars
Represents a Windows toolbar. ToolBar controls are used to display ToolBarButton
controls that can appear as a standard button, a toggle-style button, or a drop-down style
button. You can assign images to the buttons by creating an ImageList, assigning it to the
ImageList property of the toolbar, and assigning the image index value to the ImageIndex
property each ToolBarButton. You can then assign text to be displayed underneath or to the
right of the image by setting the Text property of the ToolBarButton.
Set the Appearance property of the toolbar to Flat to give the toolbar and its buttons a
flat appearance. As the mouse pointer moves over the buttons, their appearance changes to
three-dimensional. Toolbar buttons can be divided into logical groups by using separators. A
separator is a toolbar button with the Style property set to ToolBarButtonStyle.Separator.
Button separators appear as lines rather than spaces between the buttons when the toolbar has
a flat appearance. If the Appearance property is set to Normal, the toolbar buttons appear
raised and three-dimensional.
If you specify a value for the ButtonSize property, all buttons in the tool bar are
restricted to the specified size. Otherwise, the buttons adjust their size depending on their
content, and the ButtonSize property returns the initial size of the largest button.
To create a collection of ToolBarButton controls to display on the ToolBar, add the
buttons individually by using the Add or Insert methods of the Buttons property.
.NET Compact Framework Platform Note: A Form supports only one ToolBar,
attempts to add an additional ToolBar throws a NotSupportedException.Adding a ToolBar to
any control besides a Form is not supported, such as to a Panel.
public class ToolBar : Control
The properties of the ToolBar class are listed here.
206
Pantech Academy of Technical Excellence
Tool bars Properties
Public Properties
AccessibilityObject Gets the AccessibleObject assigned to the control.
AccessibleDefaultActionDescriptio
n
Gets or sets the default action description of the
control for use by accessibility client applications.
AccessibleDescription Gets or sets the description of the control used by
accessibility client applications.
AccessibleName Gets or sets the name of the control used by
accessibility client applications.
AccessibleRole Gets or sets the accessible role of the control
AllowDrop Gets or sets a value indicating whether the control can
accept data that the user drags onto it.
Anchor Gets or sets which edges of the control are anchored
to the edges of its container.
Appearance Gets or set the value that determines the appearance
of a toolbar control and its buttons.
AutoSize
Protected Properties
Gets or sets a value indicating whether the toolbar
adjusts its size automatically, based on the size of the
buttons and the dock style.
Protected Properties
CreateParams Overridden. See Control.CreateParams.
DefaultImeMode Overridden. Gets the default Input Method Editor (IME) mode
supported by this control.
DefaultSize Overridden. See Control.DefaultSize.
DesignMode Gets a value that indicates whether the Component is currently in
207
Pantech Academy of Technical Excellence
design mode.
Events Gets the list of event handlers that are attached to this Component.
FontHeight Gets or sets the height of the font of the control.
ResizeRedraw Gets or sets a value indicating whether the control redraws itself
when resized.
ShowFocusCues Gets a value indicating whether the control should display focus
rectangles.
ShowKeyboardCues Gets a value indicating whether the control should display keyboard
shortcuts.
ToolBar Methods
The methods of the ToolBar class are listed here.
Public Methods
BeginInvoke Overloaded. Executes a delegate asynchronously on the thread that the
control's underlying handle was created on.
BringToFront Brings the control to the front of the z-order.
Contains Retrieves a value indicating whether the specified control is a child of
the control.
CreateControl Forces the creation of the control, including the creation of the handle
and any child controls.
Protected Methods
AccessibilityNotifyClients Notifies the accessibility client applications of the specified
AccessibleEvents for the specified child control.
208
Pantech Academy of Technical Excellence
CreateAccessibilityInstanc
e
Creates a new accessibility object for the control.
CreateControlsInstance Creates a new instance of the control collection for the control.
CreateHandle Overridden. See Control.CreateHandle.
DefWndProc Sends the specified message to the default window procedure.
ToolBar Events
The events of the ToolBar class are listed here.
Public Events
BindingContextChanged Occurs when the value of the BindingContext property changes.
ButtonClick Occurs when a ToolBarButton on the ToolBar is clicked.
ButtonDropDown Occurs when a drop-down style ToolBarButton or its down
arrow is clicked.
CausesValidationChanged Occurs when the value of the CausesValidation property
changes.
ChangeUICues Occurs when the focus or keyboard user interface (UI) cues
change
Image List
Provides methods to manage a collection of Image objects. This class cannot be
inherited.
public sealed class ImageList : Component
ImageList is typically used by other controls, such as the ListView, TreeView, or
ToolBar. You can add bitmaps, icons, or meta files to the ImageList, and the other controls
are able to use the images as they require.
209
Pantech Academy of Technical Excellence
ImageList uses a handle to manage the list of images. The Handle is not created until
certain operations, including getting the Count, getting the Handle, and calling Draw are
performed on the image list.
Image List Properties
The properties of the ImageList class are listed here.
Public Properties
ColorDepth Gets the color depth of the image list.
Container Gets the IContainer that contains the Component.
Handle Gets the handle of the image list object.
HandleCreated Gets a value indicating whether the underlying Win32 handle has
been created.
Images Gets the ImageList.ImageCollection for this image list.
ImageSize Gets or sets the size of the images in the image list.
ImageStream Gets the handle to the ImageListStreamer associated with this image
list.
Protected Properties
DesignMode Gets a value that indicates whether the Component is currently in
design mode.
210
Pantech Academy of Technical Excellence
Events Gets the list of event handlers that are attached to this Component.
ImageList Methods
The methods of the ImageList class are listed here.
Public Methods
CreateObjRef Creates an object that contains all the relevant information
required to generate a proxy used to communicate with a remote
object.
Dispose Overloaded. Releases the resources used by the Component.
Draw Overloaded. Draws the indicated image.
Equals Overloaded. Determines whether two Object instances are equal.
GetHashCode Serves as a hash function for a particular type, suitable for use in
hashing algorithms and data structures like a hash table.
GetLifetimeServic
e
Retrieves the current lifetime service object that controls the
lifetime policy for this instance.
Protected Methods
Dispose Overloaded. Overridden. See Component.Dispose.
211
Pantech Academy of Technical Excellence
Finalize Overridden. Releases unmanaged resources and performs other
cleanup operations before the Component is reclaimed by garbage
collection.
In C# and C++, finalizers are expressed using destructor syntax.
GetService Returns an object that represents a service provided by the
Component or by its Container.
MemberwiseClon
e
Creates a shallow copy of the current Object.
ImageList Events
The events of the ImageList class are listed here.
Public Events
Disposed (inherited from Component) Adds an event handler to listen to the
Disposed event on the component.
RecreateHandle Occurs when the Handle is recreated.
TreeView
Displays hierarchical data, such as a table of contents, in a tree structure.
Overview
The TreeView control is used to display hierarchical data, such as a table of
contents or file directory, in a tree structure and supports the following features:
Data binding that allows the nodes of the control to be bound to XML, tabular, or
relational data.
Site navigation through integration with the SiteMapDataSource control.
Node text that can be displayed as either plain text or hyperlinks.
Programmatic access to the TreeView object model to create trees, populate
nodes, set properties, and so on dynamically.
212
Pantech Academy of Technical Excellence
Client-side node population (on supported browsers).
The ability to display a check box next to each node.
Customizable appearance through themes, user-defined images, and styles.
Nodes
The TreeView control is made up of nodes. Each entry in the tree is called a node and is
represented by a TreeNode object. Node types are defined as follows:
A node that contains other nodes is called a parent node.
The node that is contained by another node is called a child node.
A node that has no children is called a leaf node.
The node that is not contained by any other node but is the ancestor to all the other
nodes is the root node.
A node can be both a parent and a child, but root, parent, and leaf nodes are mutually
exclusive. Several visual and behavioral properties of nodes are determined by whether a
node is a root, child, or leaf node.
Although a typical tree structure has only one root node, the TreeView control allows
you to add multiple root nodes to your tree structure. This is useful when you want to
display item listings without displaying a single root node, as in a list of product
categories.
Each node has a Text property and a Value property. The value of the Text property is
displayed in the TreeView, while the Value property is used to store any additional data
about the node, such as data that is passed to the postback event that is associated with the
node.
A node can be in one of two modes: selection mode and navigation mode. By default,
a node is in selection mode. To put a node into navigation mode, set the NavigateUrl
213
Pantech Academy of Technical Excellence
property for the node to a value other than an empty string (""). To put a node into
selection mode, set the NavigateUrl property for the node to an empty string ("").
Notes
Static Data
The simplest data model of the TreeView control is static data. To display static data
using declarative syntax, first nest opening and closing <Nodes> tags between the opening
and closing tags of the TreeView control. Next, create the tree structure by nesting
<asp:TreeNode> elements between the opening and closing <Nodes> tags. Each
<asp:TreeNode> element represents a node in the tree and maps to a TreeNode object. You
can set the properties of each node by setting the attributes of its <asp:TreeNode> element.
To create child nodes, nest additional <asp:TreeNode> elements between the opening and
closing <asp:TreeNode> tags of the parent node.
Binding to Data
The TreeView control can also be bound to data. You can use either of two methods
to bind the TreeView control to the appropriate data source type:
The TreeView control can use any data source control that implements the
IHierarchicalDataSource interface, such as an XmlDataSource control or a
214
Some Internet browsers have a limitation that can affect the performance of the TreeView
control. Microsoft Internet Explorer 6.0 has a URL character limit of 2067 characters that it
posts. If the number of characters in a URL of a node is larger than that number, expanding that
node will fail and no exception is thrown. A malicious user can create a callback request and get
data for the nodes of the TreeView control that the page developer is not displaying. Therefore,
security of the data must be implemented by the data source. Do not use the MaxDataBindDepth
property to hide data.
Pantech Academy of Technical Excellence
SiteMapDataSource control. To bind to a data source control, set the DataSourceID property
of the TreeView control to the ID value of the data source control. The TreeView control
automatically binds to the specified data source control. This is the preferred method to bind
to data.
The TreeView control can also be bound to an XmlDocument object or a DataSet
object with relations. To bind to one of these data sources, set the DataSource property of the
TreeView control to the data source, and then call the DataBind method.
When binding to a data source where each data item contains multiple properties
(such as an XML element with several attributes), a node displays the value that is returned
by the ToString method of the data item, by default. In the case of an XML element, the node
displays the element name, which shows the underlying structure of the tree but is not very
useful otherwise. You can bind a node to a specific data item property by specifying tree
node bindings using the DataBindings collection. The DataBindings collection contains
TreeNodeBinding objects that define the relationship between a data item and the node that it
is binding to. You can specify the criteria for binding and the data item property to display in
the node. For more information on tree node bindings, see TreeNodeBinding.
Dynamic Node Population
Sometimes, it is not practical to statically define the tree structure because the data
source returns too much data or because the data to display depends on information that you
get at run time. Because of this, the TreeView control supports dynamic node population.
When the PopulateOnDemand property for a node is set to true, that node gets populated at
run time when the node is expanded. To populate a node dynamically, you must define an
event-handling method that contains the logic to populate a node for the TreeNodePopulate
event.
Browsers that support callback scripts can also take advantage of client-side node
population. Client-side node population enables the TreeView control to populate a node
using client script when users expand the node, without requiring a round trip to the server.
For more information on client-side node population, see PopulateNodesFromClient.
215
Pantech Academy of Technical Excellence
Customizing the User Interface
There are many ways to customize the appearance of the TreeView control. First, you
can specify a different style (such as font size and color) for each of the node types.
If you use cascading style sheets (CSS) to customize the appearance of the control,
use either inline styles or a separate CSS file, but not both. Using both inline styles and a
separate CSS file could cause unexpected results. For more information on using style sheets
with controls, see ASP.NET Web Server Controls and CSS Styles.
The following table lists the available node styles.
Node style property Description
HoverNodeStyle The style settings for a node when the mouse
pointer is positioned over it.
LeafNodeStyle The style settings for the leaf nodes.
NodeStyle The default style settings for a node.
ParentNodeStyle The style settings for the parent nodes.
RootNodeStyle The style settings for the root node.
SelectedNodeStyle The style settings for a selected node.
You can also control the style of nodes at specific depths within the tree by using the
LevelStyles collection. The first style in the collection corresponds to the style of the nodes at
the first level in the tree. The second style in the collection corresponds to the style of the
nodes at the second level in the tree, and so on.
If a style is defined for a certain depth level using the LevelStyles collection, that style
overrides any root, parent, or leaf node style settings for the nodes at that depth.Another way
to alter the appearance of the control is to customize the images that are displayed in the
TreeView control. You can specify your own custom set of images for the different parts of
the control by setting the properties shown in the following table.
Image property Description
CollapseImageUrl The URL to an image displayed for the collapsible node indicator. This
216
Pantech Academy of Technical Excellence
image is usually a minus sign (-).
ExpandImageUrl The URL to an image displayed for the expandable node indicator. This
image is usually a plus sign (+).
LineImagesFolder The URL to the folder containing the line images used to connect parent
nodes to child nodes. The ShowLines property must also be set to true
for this property to have an effect.
NoExpandImageUrl The URL to an image displayed for the non-expandable node indicator.
Notes:
Each time the page is posted to the server, the CheckedNodes collection is
automatically populated with the selected nodes. When check boxes are displayed, you can
use the TreeNodeCheckChanged event to run a custom routine whenever the state of a check
box changes between posts to the server.
Events
The TreeView control provides several events that you can program against. This
allows you to run a custom routine whenever an event occurs. The following table lists the
events that are supported by the TreeView control.
Event Description
TreeNodeCheckChanged Occurs when the check boxes of the TreeView control change
217
Need to customize every image property. If an image property is not explicitly set, the built-in
default image is used.The TreeView control also allows you to display a check box next to a
node. When the ShowCheckBoxes property is set to a value other than TreeNodeTypes.
Pantech Academy of Technical Excellence
state between posts to the server.
SelectedNodeChanged
Occurs when a node is selected in the TreeView control.
TreeNodeExpanded Occurs when a node is expanded in the TreeView control.
TreeNodeCollapsed Occurs when a node is collapsed in the TreeView control.
TreeNodePopulate Occurs when a node with its PopulateOnDemand property set to
true is expanded in the TreeView control.
TreeNodeDataBound Occurs when a data item is bound to a node in the TreeView
control.
ListView Control
Represents a Windows list view control, which displays a collection of items that can
be displayed using one of four different views.
A ListView control allows you to display a list of items with item text and, optionally,
an icon to identify the type of item.
For example, the Windows Explorer list of files is similar in appearance to a ListView
control. It displays a list of the files and folders currently selected in the tree. Each file and
folder displays an icon associated with it to help identify the type of file or folder.
The ListViewItem class represents an item within a ListView control. The items that
are displayed in the list can be shown in one of four different views. Items can be displayed
as large icons, as small icons, or as small icons in a vertical list. Items can also have subitems
which contain information that is related to the parent item.
The fourth view style, details view, allows you to display the item and its subitems in
a grid with column headers that identify the information being displayed in a subitem.
ListView supports single or multiple selection. The multiple selection feature lets the user
select from a list of items in a way similar to a ListBox control. Additionally, the user can
218
Pantech Academy of Technical Excellence
activate selected items to perform a task. For example, you could use a ListView control to
display a list of files that the application can then open and utilize.
The user can select the files to open and then double-click them to activate the items
and open the files in the application. The ListView can also display check boxes, using the
CheckBoxes property, to allow the user to check the items that they want to perform an
action on. You can use the ListView control in a variety of ways. The control can be used to
display information from an application, a database, or a text file. The ListView can also be
used to obtain information from the user, such as selecting a set of files to process.
ListView provides a large number of properties that provide flexibility in appearance
and behavior. The View property allows you to change the way in which items are displayed.
The LargeImageList, SmallImageList, and StateImageList properties allow you to specify the
ImageList objects that contain the images displayed for items and, in the case of the
StateImageList, the check boxes that are displayed when the CheckBoxes property is set to
true.
To determine which items are checked, you can use the CheckedItems property to
access the ListView.CheckedListViewItemCollection collection. The Columns property
allows access to the ListView.ColumnHeaderCollection, which stores the column headers
that are displayed when the View property of the control is set to View.Details. Items are
added and removed from the ListView through the Items property.
The Items property allows you to access the ListView.ListViewItemCollection of the
control, which provides methods for manipulating the items in the control. If you want to
allow the user to edit the text of an item, you can use the LabelEdit property. When your
control contains a large number of items, it is often easier for the user to see them in a sorted
list. You can use the Sorting property to sort the items alphabetically.
Many of the properties of the ListView control are used when the View property of
the control is set to View.Details. The AllowColumnReorder property allows the user of your
ListView control to reconfigure the order of columns at run time. The FullRowSelect
property allows an item and its subitems to be selected instead of just the item. To display
grid lines in the details view to identify the boundaries of items and subitems in the ListView,
219
Pantech Academy of Technical Excellence
you can use the GridLines property. The HeaderStyle property allows you to specify the type
of column headers to display.
In addition to the many properties that are available for a ListView control, there are
methods and events that your application can use to provide additional capabilities to the
ListView. The BeginUpdate and EndUpdate methods allow you to add many items to a
ListView without displaying the repainting of the control each time an item is added,
improving performance. If your ListView control is displaying items and subitems, you may
want to provide functionality when the user right-clicks a subitem. To determine the item
whose subitem is being clicked, you can use the GetItemAt method. When performing
validation of the items after the user has edited them, you may want to display a specific item
to the user to change. The EnsureVisible method can be called to ensure that the specific item
is in the visible area of the control.
If the LabelEdit property set to true, you can perform tasks such as validating the text
being edited before and after the text changed by creating an event handler for the
BeforeLabelEdit and AfterLabelEdit events. To perform tasks such as opening a file or
displaying a dialog box to edit an item displayed in a ListView, you can create an event
handler for the ItemActivate event.
If you allow the user to sort the items in a ListView when they click a column header,
you can create an event handler for the ColumnClick event to perform the sorting. When the
CheckBoxes property is set to true, you can determine when a change in an item's check state
has occurred by handling the ItemCheck event.
DATA BASE PROGRAMMING (ADO.NET) WITH C#
220
Pantech Academy of Technical Excellence
Data Base Programming (ADO.NET) with C#
ADO .NET Introduction
Data Providers in .Net
Introduction to Managed and Unmanaged Providers
Data Readers, Dataset and Data Adapters
Datagrid View
SQL Datasource
Creating Relations and Transactions
ADO .NET INTRODUCTION
221
Pantech Academy of Technical Excellence
The first data access model, DAO (data access model) was created for local databases
with the built-in Jet engine which had performance and functionality issues. Next came RDO
(Remote Data Object) and ADO (Active Data Object) which were designed for Client Server
architectures but soon ADO took over RDO. ADO was a good architecture but as the
language changes so is the technology. With ADO, all the data is contained in a record set
object which had problems when implemented on the network and penetrating firewalls.
ADO was a connected data access, which means that when a connection to the database is
established the connection remains open until the application is closed. Leaving the
connection open for the lifetime of the application raises concerns about database security
and network traffic. Also, as databases are becoming increasingly important and as they are
serving more people, a connected data access model makes us think about its productivity.
For example, an application with connected data access may do well when connected to two
clients, the same may do poorly when connected to 10 and might be unusable when
connected to 100 or more. Also, open database connections use system resources to a
maximum extent making the system performance less effective.
Most applications need data access at one point of time making it a crucial component
when working with applications. Data access is making the application interact with a
database, where all the data is stored. Different applications have different requirements for
database access. C# .NET uses ADO .NET (Active X Data Object) as its data access and
manipulation protocol which also enables us to work with data on the Internet. Let's take a
look why ADO .NET came into picture replacing ADO.
Data Providers in .Net
The Data Provider is responsible for providing and maintaining the connection to the
database. A Data Provider is a set of related components that work together to provide data in
an efficient and performance driven manner. The .NET Framework currently comes with two
Data Providers: the SQL Data Provider which is designed only to work with Microsoft's SQL
Server 7.0 or later and the OleDb Data Provider which allows us to connect to other types of
databases like Access and SQL.
222
Pantech Academy of Technical Excellence
..NET Data ProvidersNET Data Providers
Namespaces
Namespaces is called as collection of class libraries.
• System. Data & System.Data.Common
• System.Data.SqlClient & System.Data.OleDB
• System.Data.SqlTypes
• System.XML & System.XML.Schema
C#
using System.Data;
using System.Data.SqlClient;
SqlDataAdapter sqlAdp= new SqlDataAdapter();
Introduction to Managed and Unmanaged Providers
Managed Providers
The UDA(Universal Data Access) managed .NET Data Provider delivers robust and
secure data connectivity across all Databases support in the Universal Data Access suite,
including all major databases - Microsoft SQL Server, Oracle, DB2, Sybase, Ingres II,
Client
SQL .NET
Data Provider
OLE DB .NET
Data Provider
ODBC .NET
Data Provider
OLE DB
Provider
ODBC
Driver
SQL SERVER
Other DB
Other DB
223
Pantech Academy of Technical Excellence
Informix and Progress. The .NET Data Provider is built with managed code, enabling it to
running completely within the .NET Framework runtime delivering better security and
performance. This Generic managed .Net Data Provider connects to the remote data source
via one of two forms currently:
Multi-Tier Database Agents
The OpenLink VDB (Virtual Database) layer has been ported to C# and linked into the
Provider, enabling it to communicate directly with the OpenLink Multi-Tier Database agents
installed on the remote machine.
Example:
using System.Data.SqlTypes;
TDS(Tabular Data Stream) Protocol
The TDS protocol has been ported to C# and linked into the Provider, enabling two
Managed Providers to be created capable of direct connectivity to Microsoft and Sybase
SQLServer Databases without the need for any additional components on the Server
Example:
using System.Data.OleDb;
Unmanaged Providers
The UDA (Universal Data Access) Unmanaged .NET Data Provider enables
connectivity to any ODBC Data Source by acting as a Bridge between ADO.Net and ODBC.
This Provider is provided as a stop-gap solution enabling connectivity to Data source for
which managed .Net Providers are not already available as indicated in the diagram below,
and as such does not provide the benefits of security and performance available from its
Managed counterpart:
Example:
using System.Data.Odbc;
224
Pantech Academy of Technical Excellence
Data Readers, Dataset and Data Adapters
The DataReader Object
The DataReader object provides a forward-only, read-only, connected stream
recordset from a database. Unlike other components of the Data Provider, DataReader objects
cannot be directly instantiated. Rather, the DataReader is returned as the result of the
Command object's ExecuteReader method. The SqlCommand.ExecuteReader method returns
a SqlDataReader object, and the OleDbCommand.ExecuteReader method returns an
OleDbDataReader object. The DataReader can provide rows of data directly to application
logic when you do not need to keep the data cached in memory. Because only one row is in
memory at a time, the DataReader provides the lowest overhead in terms of system
performance but requires the exclusive use of an open Connection object for the lifetime of
the DataReader.
C#.Net
SqlDataReader sqlReader;
sqlReader = sqlCommand.ExecuteReader();
while (sqlReader.Read())
{
// process, sqlReader("field")
}
sqlReader.Dispose();
The DataAdapter Object
The DataAdapter is the class at the core of ADO .NET's disconnected data access. It
is essentially the middleman facilitating all communication between the database and a
DataSet. The DataAdapter is used either to fill a DataTable or DataSet with data from the
database with it's Fill method. After the memory-resident data has been manipulated, the
DataAdapter can commit the changes to the database by calling the Update method.
225
Pantech Academy of Technical Excellence
C#.Net
SqlDataAdapter sqlDA= new SqlDataAdapter();
sqlDA. SelectCommand=new SqlCommand ("select * from authors“, sqlConnection);
DataSet sqlDS = new DataSet("authorsTable");
sqlDA.Fill(sqlDS, "authorsTable");
DataSets
The dataset is a disconnected, in-memory representation of data. It can be considered
as a local copy of the relevant portions of the database. The Dataset is persisted in memory
and the data in it can be manipulated and updated independent of the database. When the use
of this Dataset is finished, changes can be made back to the central database for updating.
The data in Dataset can be loaded from any valid data source like Microsoft SQL server
database, an SQL database or from a Microsoft Access database.
Creating DataSets
• Setup SqlConnection
• Setup a SqlDataAdapter
• Create a DataSet
• Call the .Fill() method on the DA
Syntax:
SqlConnection myConnection = new SqlConnection(connectionString);
SqlDataAdapter ad = new SqlDataAdapter("SELECT * FROM Categories", myConnection);
DataSet ds = new DataSet();
ad.Fill(ds, "Categories");
myGridView.DataSource = ds;
myGridView.DataBind();
226
Pantech Academy of Technical Excellence
Datagrid View
DataGrid requires you to write custom code for handling common operations like
sorting, paging and manipulation of data in DataGrid
DataGrid when bound to DataSource control can only support select operation on
DataSource. Updating DataSource through DataGrid can be done only through
custom ADO.NET code
DataGrid supports a restricted event model.
DataGrid does not support adaptive rendering on different platforms.
SQL Data source
DataSource controls enable programmers to declaratively connect data sources to the
user interface. The logic for retrieving the data from the source is inbuilt into the control. For
instance when a DropDownList is added to the form the developer is prompted to connect to
the DataSource.
Creating SQL DataSoutrce By using following steps:
Step1:
On clicking ‘connect to DataSource’, the wizard is started to guide the user through
the process of setting up the data source.
Step2:
Check whether the user views existing datasource or new datasource. The user has to
indicate whether an existing data source or a new data source has to be added.
Step3:
Create a new SQLDataSource. byChoose a DataSource drop down box, select < New
DataSource > The next screen of the wizard prompts the user select the type of data source
required. The user has to select the database and specify an ID for the DataSource. Click Ok
to continue
227
Pantech Academy of Technical Excellence
Step4:
A connection string will now have to be specified. The connection can be an existing
string or a new string. Since there are no existing connections a new connection will be
created. Click on New Connection to create the connection string. A number of options are
given to the user. Select Sql Server Database file to connect to the SQL Server Express
database.
Step5:
On clicking continue the user is prompted to select an existing data source or to click
change to change the data source or provider. The user has to also enter the name of the new
database or an existing data base. Name the new database as ExForSys and click Test
Connection (Succeeded).
Step6:
The next string prompts the user to save the connection string to the application
configuration file. The user has to click on the check box if ‘yes’ and then click Next to
create queries.
Query:
SqlConnection myConnection = new SqlConnection(connectionString);
SqlDataAdapter ad = new SqlDataAdapter("SELECT * FROM Categories", myConnection);
DataSet ds = new DataSet();
ad.Fill(ds, "Categories");
myGridView.DataSource = ds;
myGridView.DataBind();
Creating Relations and Transactions
A transaction is a group of operations combined into a logical unit of work that is
either guaranteed to be executed as a whole or rolled back. Transactions help the database in
satisfying all the ACID (Atomic, Consistent, Isolated, and Durable). Transaction processing
is an indispensable part of ADO.NET. It guarantees that a block of statements will either be
executed in its entirety or rolled back,( i.e., none of the statements will be executed).
228
Pantech Academy of Technical Excellence
Implementing Transactions in ADO.NET
ADO.NET, the transactions are started by calling the BeginTransaction method of the
connection class. This method returns an object of type SqlTransaction. Once you are done
executing the necessary statements within the transaction unit/block, make a call to the
Commit method of the given SqlTransaction object, or you can roll back the transaction using
the Rollback method, depending on your requirements (if any error occurs when the
transaction unit/block was executed).
Transactions are supported in ADO.NET by the SqlTransaction class that belongs to
the System.Data.SqlClient namespace.
The two main properties of this class are as follows:
Connection:
This indicates the SqlConnection instance that the transaction instance is
associated with
IsolationLevel:
This specifies the IsolationLevel of the transaction
The following are the methods of this class that are noteworthy:
Commit() This method is called to commit the transaction
Rollback() This method can be invoked to roll back a transaction. Note that a transaction
can only be rolled back after it has been committed.
Save() This method creates a save point in the transaction. This save point can be used to
rollback a portion of the transaction at a later point in time. The following are the steps to
implement transaction processing in ADO.NET.
Connect to the database
Create a SqlCommand instance with the necessary parameters
Open the database connection using the connection instance
Call the BeginTransaction method of the Connection object to mark the beginning
of the transaction
Execute the sql statements using the command instance
229
Pantech Academy of Technical Excellence
Call the Commit method of the Transaction object to complete the transaction, or
the Rollback method to cancel or abort the transaction
Close the connection to the database
The following code snippet shows how we can implement transaction processing using
ADO.NET in our applications.
Example:
string connectionString = ...; //Some connection string
SqlConnection sqlConnection = new SqlConnection(connectionString);
sqlConnection.Open();
SqlTransaction sqlTransaction = sqlConnection.BeginTransaction();
SqlCommand sqlCommand = new SqlCommand();
sqlCommand.Transaction = sqlTransaction;
try
{
sqlCommand.CommandText = "Insert into Employee (EmpCode, EmpName) VALUES (1,
'Joydip')";
sqlCommand.ExecuteNonQuery();
sqlCommand.CommandText = "Insert into Dept (DeptCode, DeptName, EmpCode)
VALUES (9, 'Software', 1)";
sqlCommand.ExecuteNonQuery();
sqlTransaction.Commit();
//Usual code
}
catch(Exception e)
{
sqlTransaction.Rollback();
//Usual code
}
230
Pantech Academy of Technical Excellence
finally
{
sqlConnection.Close();
}
231