- sharada vikas trust

advertisement
.Net Programming
Syllabus
Module 1
Unit 1:
Understanding previous states of affairs, The .Net Solution, The Building
Block of the .Net Platform (CLR, CTS and CLS), The Role of the .Net Base Class
Libraries, What C# Brings to the Table, An Overview of .Net Binaries (or Assemblies),
The Role of the Common Intermediate Language, The Role of .Net Type Metadata, The
Role of the Assembly Manifest, Compiling CIL to Platform Specific Instructions.
Unit 2:
Understanding the common type system, Intrinsic CTS Data Types,
Understanding the Common Languages Specification, Understanding the Common
Language Runtime, A tour of the .Net Namespaces, Increasing Your Namespace
Nomenclature, Deploying the .NET Runtime.
Unit 3:
Role of Command Line Compiler (csc.exe), Building C# Applications using
csc.exe, Working with csc.exe Response Files, Generating Bug Reports, Remaining C#
Compiler Options, The Command Line Debugger (cordbg.exe), Using the Visual
Studio.NET IDE, The C# “Pre-processor” Directives, An Interesting Aside:
System.Environment Class.
Unit 4:
The Anatomy of a Basic C# Class, Creating Objects: Constructor Basics, The
Composition of a C# Application, Default Assignment and Variable scope, The C#
Member Initialization Syntax, Basic Input and Output with the Console Class,
Defining Program Constants, C# Iteration Constructs, C# Controls Flow Constructs,
The Complete set of C# Operators.
Module 2
Unit 1:
Understanding Value Types and Reference Types, The Master Node:
System.Object, The System Data Types (and C# Aliases), Converting between value
types reference types: Boxing and Unboxing.
Unit 2:
Defining Custom Class Methods, Understanding Static Methods,
Methods Parameter Modifiers, Array Manipulation in C#, String Manipulation in C#,
C# Enumerations, Defining Structures in C#, Defining Custom Namespaces.
Unit 3:
Formal Definition of the C# Class, Definition the “Default Public Interface” of
a Type, Recapping the Pillars of OOP, The First Pillars: C#’s Encapsulation Services,
Pseudo Encapsulation: Creating Read-Only Fields.
Unit 4:
The Second Pillar: C#’s Inheritance Supports, Keeping family secrets: The
“Protected” Keyword, Nested Type Definitions, The Third Pillar: C#’s Polymorphic
Support, Casting Between.
Module 3
Unit 1:
Meaning of Errors and Exceptions, The Role of .NET Exception Handling,
The System. Exception Base Class, Throwing a Generic Exception,
Catching
Exception, CLR System – Level Exception (System.SystemException),
Custom
Application-Level Exception (System.ApplicationException),
Handling Multiple
Exceptions, The Finally Block, Dynamically Identifying Application and System Level
Exceptions.
Unit 2:
Understanding Object Lifetime, The CIL of new, The Basics of Garbage
Collection, Finalization a type, The Finalization Process, Building an AD Hoc
Destruction Method, Garbage Collection Optimizations, The System. GC Type.
Unit 3:
Defining Interfaces Using C#, Invoking Interface Members at the object level,
Exercising the shapes hierarchy, Understanding Explicit Interface Implementation,
Interfaces as Polymorphic Agents, Building Interface Hierarchies.
Unit 4:
Understanding the IConvertible Interface, Building a Custom Enumerator
(IEnummerable and Enumerator), Building Cloneable objects (ICloneable), Building
Comparable Objects (IComparable), Exploring the System.Collections Namespace.
Module 4
Unit 1:
Understanding Callback Interfaces, Understanding the .NET Delegate Type,
Members of System.Multicast Delegate, The Simplest Possible Delegate Example,
Building More Elaborate Delegate Example, Understanding Asynchronous Delegates,
Understanding (and Using) Events.
Unit 2:
The advanced keywords of C#, Building a Custom Indexer,
operators, The Internal Representation of Overloading Operators,
Custom Conversion Routines, Defining Implicit Conversion Routines.
Overloading
Creating
Unit 3:
An Overview of .NET Assemblies, Core Benefits of Assemblies, Building a
Single File Test Assembly, Exploring the Manifest, Building a Multifile Assembly.
Unit 4:
Understanding Private Assemblies, Probing for Private Assemblies (The
Basics), Private Assemblies and XML Configuration Files, Probing for Private
Assemblies (The Details), Understanding Shared Assembly, Understanding Strong
Names,
Building
a
Shared
Assembly,
Understanding
Delayed
Signing,
Installing/Removing Shared Assembly, Using a Shared Assembly.
.NET Programming
Module 1
Unit 1
CONTENTS:
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
1.10
1.11
1.12
1.13
1.14
1.15
Objectives
Introduction
Understanding previous states of affairs
The .Net Solution
The Building Block of the .Net Platform (CLR, CTS and CLS)
The Role of the .Net Base Class Libraries
What C# Brings to the Table
An Overview of .Net Binaries (or Assemblies)
The Role of the Common Intermediate Language
The Role of .Net Type Metadata
The Role of the Assembly Manifest
Compiling CIL to Platform Specific Instructions
Summary
Keywords
Exercises
1.1 Objectives
At the end of this lesson, the students will understand:





The problems with previous programming languages
The solution provided by .NET
Building blocks of .NET viz. CLR, CTS and CLS
The role of Base Class Libraries (BCL)
The role of CIL, Metadata and Assembly manifest
1.2 Introduction
However good a programming language is, it will be overshadowed by something better
in the course of time. The languages like C, C++, Visual Basic, Java, the frameworks
like MFC (Microsoft Foundation Classes), ATL(Active Template Library), STL(Standard
Template Library) and the architectures like COM (Component Object Model), CORBA
(Common Object Request Broker Architecture), EJB (Enterprise JavaBeans) etc. were
possessing technologies which is needed by the programmer. But still, the hunt for
better one will be going on even in future too.
1.3 Understanding previous states of affairs
Before examining the specifics of the .NET universe, it’s helpful to consider some of the
issues that motivated the genesis of Microsoft’s current platform. To get in the proper
idea, let us discuss some of the limitations of previous technologies.
Life as a C/Win32 API Programmer

Building windows applications using the raw API (Application Programming
Interface) and C is a complex task.

C is a very abrupt language.

C developers have to perform manual memory management.

C involves ugly pointer arithmetic, and ugly syntactical constructs.

C is a structured language and so lacks the benefits provided by the objectoriented approach.

When you combine the thousands of global functions and data types defined by
the Win32 API to a difficult language like C, bugs will increase rapidly.
Life as a C++/MFC Programmer

The improvement over raw C/API development is the use of the C++
programming language.

Though C++ provides OOPs concepts like encapsulation, inheritance and
polymorphism, it is not away from manual memory management and pointers.

Even with difficulty, many C++ frameworks exist today. For example, the
Microsoft Foundation Classes (MFC) provides the developer with a set of C++
classes that facilitate the construction of Win32 applications.

The main role of MFC is to wrap a “sane subset” of the raw Win32 API behind a
number of classes, magic macros, and numerous code-generation tools.

Regardless of the helpful assistance offered by the MFC framework, C++
programming remains a difficult and error-prone experience, given its historical
roots in C.
Life as a Visual Basic 6.0 Programmer

VB6 is popular due to its ability to build complex user interfaces, code libraries,
and simpler data access logic.

Even more than MFC, VB6 hides the complexities of the raw Win32 API from
view using a number of integrated code wizards, intrinsic data types, classes,
and VB-specific functions.

The major downfall of VB6 is that it is not a fully object-oriented language. For
example, VB6 does not allow the programmer to establish “is-a” relationships
between types.

VB6 has no intrinsic support for parameterized class construction.

VB6 doesn’t provide the ability to build multithreaded applications.
Life as a Java/J2EE Programmer

Java has greater strength because of its platform independence nature.

Java cleans up many unsavory syntactical aspects of C++.

Java provides programmers with a large number of predefined “packages” that
contain various type definitions.

Although Java is a very elegant language, one potential problem is that using
Java typically means that you must use Java front-to-back during the
development cycle.

So, language integration is difficult in Java, which is against its primary goal (a
single programming language for every need).

Pure Java is simply not appropriate for many graphically or numerically
intensive applications.

For graphics oriented product, Java works slowly and compared to this, C++ or
C would execute faster.

Java provides a limited ability to access non-Java APIs and hence, there is little
support for true cross-language integration.
Life as a COM Programmer

COM is a group of classes, which works as a block of reusable code.

The binary COM server can be accessed in a language-independent manner.
For example, COM classes written using C++ can be used by VB6.

But, COM’s language independence is somewhat limited as it will not support
inheritance. Rather, one must make use of “has-a” relationship to reuse COM
class types.

Although COM can be considered a very successful object model, it is extremely
complex under the internally.

To help simplify the development of COM binaries, numerous COM-aware
frameworks have come into existence.

Even if we choose a relatively simple COM-aware language such as VB6, we are
still forced to contend with fragile registration entries and numerous
deployment-related issues.
Life as a Windows DNA Programmer

The popularity of Web applications is ever expanding.

Sadly, building a web application using COM-based Windows DNA (Distributed
interNet Applications) is quite complex.

Some of this complexity is because Windows DNA requires the use of numerous
technologies and languages like ASP, HTML, XML, JavaScript, VBScript, ADO
etc.

Many of these technologies are completely unrelated from a syntactic point of
view.

Also each language and/or technology has its own type system. For example,
an “int” in JavaScript is not quite the same as an “Integer” in VB6.
1.4 The .NET Solution
For various problems faced in previous technologies, the .NET provides the solution.
The .NET framework is a completely new model for building systems on the Windows
family of operating systems, as well as on numerous non-Microsoft operating systems
such as Mac OS X and various Unix/Linux distributions. Some core features provided
by .NET are as follows:

Full interoperability with existing code: Existing COM binaries can
mingle/interop with newer .NET binaries and vice versa. Also, Platform
Invocation Services allows to call C-based libraries (including the underlying
API of the operating system) from .NET code.

Complete and total language integration: Unlike COM, .NET supports crosslanguage inheritance, cross-language exception handling, and cross-language
debugging.

A common runtime engine shared by all .NET-aware languages: One
aspect of this engine is a well-defined set of types that each .NET-aware
language “understands.”

A base class library: This library protects the programmer from the
complexities of raw API calls and offers a consistent object model used by all
.NET-aware languages.

No more COM plumbing: IClassFactory, IUnknown, IDispatch, IDL code, and
VARIANT-compliant data types (BSTR, SAFEARRAY, and so forth) have no place
in a native .NET binary.

A truly simplified deployment model: Under .NET, there is no need to
register a binary unit into the system registry. Furthermore, .NET allows
multiple versions of the same *.dll to exist on a single machine.
NOTE: The .NET platform has nothing to do with COM. In fact, the only way .NET and
COM types can interact with each other is using the interoperability layer.
1.5 The Building Blocks of .NET Platform
The entities that make .NET to provide several benefits are CLR, CTS, and CLS. The
.NET can be understood as a new runtime environment and a comprehensive base
class library.
CLR:

The runtime layer is properly referred to as the Common Language Runtime
(CLR).

The primary role of the CLR is to locate, load, and manage .NET types.

The CLR also takes care of a number of low-level details such as memory
management and performing security checks.
CTS:

The Common Type System (CTS) specification fully describes the entities like
all possible data types and programming constructs supported by the runtime.

It specifies how these entities can interact with each other.

It also specifies how they are represented in the .NET metadata format.
CLS:

A given .NET –aware language might not support each and every feature defined
by the CTS.

The Common Language Specification (CLS) is a related specification that
defines a subset of common types and programming constructs that all .NET
programming languages can agree on.

Thus, the .NET types that expose CLS-compliant features can be used by all
.NET-aware languages.

But, a data type or programming construct, which is outside the bounds of the
CLS, may not be used by every .NET programming language.
1.6 The Role of Base Class Libraries
In addition to the CLR and CTS/CLS specifications, the .NET platform provides a base
class library that is available to all .NET programming languages.
This base class
library encapsulates various primitives such as threads, file input/output (I/O),
graphical rendering, and interaction with various external hardware devices. It also
provides support for a number of services required by most real-world applications.
For example, the base class libraries define types
The Base Class Libraries
Data Access
GUI
Security
XML/SOAP
Threading
FILE I/O
Debugging
(et al.)
The Common Language Runtime
Common Type System
Common Language Specification
Figure 1.1 The CLR, CTS, CLS, and base class library relationship
that facilitate database access, XML manipulation, programmatic security, and the
construction of web-enabled, traditional desktop and console-based front ends. From
a high level, you can visualize the relationship between the CLR, CTS, CLS, and the
base class library, as shown in Figure 1.1.
1.7 What C# Brings to the Table
C# is a programming language that looks very similar to the syntax of Java. Many of
the syntactic constructs of C# are taken from various aspects of Visual Basic 6.0 and
C++. For example, like VB6, C# supports the notion of formal type properties, and the
ability to declare methods taking varying number of arguments. Like C++, C# allows
you to overload operators, as well as to create structures, enumerations, and callback
functions (via delegates).
The features of C# language can be put together as –

No pointers required! C# programs typically have no need for direct pointer
manipulation

Automatic memory management through garbage collection. Given this, C#
does not support a delete keyword.

Formal syntactic constructs for enumerations, structures, and class properties.

The C++-like ability to overload operators for a custom type, without the
complexity.

The syntax for building generic types and generic members is very similar to
C++ templates.

Full support for interface-based programming techniques.

Full support for aspect-oriented programming (AOP) techniques via attributes.
This brand of development allows you to assign characteristics to types and
their members to further qualify their behavior.
C# produces the code that can execute within the .NET runtime. The code targeting
the .NET runtime is called as managed code. The binary unit that contains the
managed code is termed as assembly. Conversely, code that cannot be directly hosted
by the .NET runtime is termed unmanaged code.
1.8 An Overview of .NET Assemblies (or Binaries)
The .NET binaries are not described using COM type libraries and are not registered
into the system registry.
The .NET binaries do not contain platform-specific
instructions, but rather platform-agnostic intermediate language (IL) and type
metadata. The relationship between .NET aware compilers and metadata are shown in
Fig. 1.2.
Note: The terms IL, MSIL (Microsoft Intermediate Language) and CIL (Common
Intermediate Language) are all describing the same exact entity.
C# Source Code
C# Compiler
Perl to Perl
.NET Source
Code
Perl to Perl .NET Compiler
MSIL
and
COBOL to
COBOL. NET
Source Code
COBOL to COBOL. NET
Compiler
C++ to MC++
Source Code
C++ to MC++ Compiler
Metadata
(DLL or EXE)
Figure 1.2 All .NET-aware compilers produce IL instructions and metadata.
Few points to be understood about binaries/assemblies are:

When a *.dll or *.exe has been created using a .NET-aware compiler, the
resulting module is bundled into an assembly.

An assembly contains CIL code, (which is conceptually similar to Java
bytecode). This is not compiled to platform-specific instructions until absolutely
necessary.

When a block of CIL instructions (such as a method implementation) is
referenced for use by the .NET runtime engine, CIL code will be compiled.

Assemblies also contain metadata that describes the characteristics of every
“type” living within the binary, in detail. For example, if you have a class named
SportsCar, the type metadata describes details such as SportsCar’s base class,
which interfaces are implemented by SportsCar (if any), as well as a full
description of each member supported by the SportsCar type.

Unlike COM, the .NET metadata is always present and is automatically
generated by a given .NET-aware compiler.

In addition to CIL and type metadata, assemblies themselves are also described
using metadata, which is officially termed as a manifest.

The manifest contains information about the current version of the assembly,
culture information (used for localizing string and image resources), and a list
of all externally referenced assemblies that are required for proper execution.
Single-File and Multifile Assemblies
If an assembly is composed of a single *.dll or *.exe module, you have a single-file
assembly. A single-file assembly contains the assembly manifest, the type metadata,
the CIL code, and the resources. Figure 1.3 shows a single-file assembly and its
contents.
Multifile assemblies are composed of numerous .NET binaries, each of which is termed
a module. When building a multifile assembly, one of these modules (termed the
primary module) must contain the assembly manifest (and possibly CIL instructions
and metadata for various types). The other related modules contain a module level
manifest, CIL, and type metadata. The primary module maintains the set of required
secondary modules within the assembly manifest.
MyApp.dll
Assembly Metadata
Resources
CIL Code
Type Metadata
Figure 1.3 Single-File Assembly
In other words, multifile assemblies are used when different modules of the
application are written in different languages.
Multifile assemblies make the
downloading process more efficient. They enable you to store the seldom used types in
separate modules and download them only when needed. The multifile assembly
shown in Figure 1.4 consists of three files. The MyApp.dll file contains the assembly
manifest for the multifile assembly. The MyLib.netmodule file contains the type
metadata and the MSIL code but not the assembly manifest. The Employee.gif is the
resource file for this multifile assembly.
MyApp.dll
MyLib.netmodule
Assembly Manifest
Employee.gif
CIL
CIL
Type Metadata
Type Metadata
Figure 1.4 MultiFile Assembly
Thus, an assembly is really a logical grouping of one or more related modules that are
intended to be initially deployed and versioned as a single unit.
1.9 The Role of Common Intermediate Language
CIL is a language that sits above any particular platform-specific instruction set.
Regardless of which .NET-aware language you choose (like C#, VB.NET, VC++.NET
etc), the associated compiler produces CIL instructions. Once the C# compiler (csc.exe)
compiles the source code file, you end up with a single-file *.exe assembly that
contains a manifest, CIL instructions, and metadata describing each aspect of the
program.
Benefits of CIL
The benefits of compiling source code into CIL rather than directly to a specific
instruction set are as given below:

Language integration: Each .NET-aware compiler produces nearly identical CIL
instructions. Therefore, all languages are able to interact within a well-defined
binary arena.

Given that CIL is platform-agnostic, the .NET Framework itself is platformagnostic. So a single code base can run on numerous operating systems, just
like Java.
There is an international standard for the C# language, and a large subset of the .NET
platform and implementations already exist for many non-Windows operating systems.
In contrast to Java, the .NET allows you to build applications using your language of
choice.
1.10 The Role .NET Type Metadata
In addition to CIL instructions, a .NET assembly contains full, complete, and accurate
metadata. Few points about metadata are described hereunder:

The metadata describes each and every type (class, structure, enumeration etc.)
defined in the binary, as well as the members of each type (properties, methods,
events etc.).

It is always the job of the compiler (not the programmer) to produce the latest
and greatest type metadata.

As .NET metadata is so careful, assemblies are completely self-describing
entities and so .NET binaries have no need to be registered into the system
registry.

Metadata is used by numerous aspects of the .NET runtime environment, as
well as by various development tools.

For example, the IntelliSense feature provided by Visual Studio 2005 is made
possible by reading an assembly’s metadata at design time.

Metadata is also used by various object browsing utilities, debugging tools, and
the C# compiler itself.

To be sure, metadata is the backbone of numerous .NET technologies including
remoting, reflection, late binding, XML web services, and object serialization.
1.11 The Role Assembly Manifest
The .NET assembly also contains metadata that describes the assembly itself,
technically termed a manifest.
Among other details, the manifest documents all
external assemblies required by the current assembly to function correctly, the
assembly’s version number, copyrig ht information, and so on. Like type metadata, it
is always the job of the compiler to generate the assembly’s manifest. The manifest
documents the list of external assemblies required by *.exe(via the .assembly extern
directive) as well as various characteristics of the assembly itself (version number,
module name, and so on).
1.12 Compiling CIL to Platform-Specific Instructions
Since assemblies contain CIL instructions, rather than platform-specific instructions,
CIL code must be compiled before use.
The entity that compiles CIL code into
meaningful CPU instructions is termed a just-in-time (JIT) compiler, which is also
known as Jitter. The .NET runtime environment forces a JIT compiler for each CPU
targeting the CLR, each of which is optimized for the platform it is targeting.
For example, if you are building a .NET application that is to be deployed to a
handheld device (such as a Pocket PC), the corresponding Jitter is well equipped to
run within a low-memory environment. On the other hand, if you are deploying your
assembly to a back-end server (where memory is seldom an issue), the Jitter will be
optimized to function in a high-memory environment. In this way, developers can write
a single body of code that can be efficiently JIT-compiled and executed on machines
with different architectures. Furthermore, as a given Jitter compiles CIL instructions
into corresponding machine code, it will cache the results in memory in a manner
suited to the target operating system. In this way, if a call is made to a method named
PrintDocument(), the CIL instructions are compiled into platform-specific instructions
on the first invocation and retained in memory for later use. Therefore, the next time
PrintDocument() is called, there is no need to recompile the CIL.
1.13 Summary
The point of this chapter was to lay out the conceptual framework necessary for
understanding the subject. We began by examining a number of limitations and
complexities found within the technologies prior to .NET and followed up with an
overview of how.NET and C# attempts to simplify the current state of affairs. The CLR
is able to host any .NET binary/assembly that abides by the rules of managed code.
As you have seen, assemblies contain CIL instructions (in addition to type metadata
and assembly manifest) that are compiled to platform-specific instructions using a
just-in-time (JIT) compiler.
1.14 Keywords

Common Language Runtime (CLR)

Common Language Specification (CLS)

Common Type System (CTS)

Base Class Library (BCL)

Assemblies/Binaries

Common Intermediate Language (CIL)

Assembly Manifest

Type Metadata

Just-in-Time Compiler (Jitter)
1.15 Exercises
1. With a neat diagram, explain the basic building block of .NET framework.
2. What do you mean by BCL? Explain.
3. Bring out the important differences between single and multifile assemblies.
4. What are the key features of C#?
5. Briefly discuss the state of affairs that eventually led to the .NET platform. What is the
.NET solution and what C# brings to the table?
6. Explain the concept of .NET binaries.
7. Explain the role of JIT compiler.
8. Explain the role of CIL and the benefits of CIL in .NET platform?
Module 1
Unit 2
CONTENTS:
1.16
2.2
2.3
2.4
2.5
2.6
2.7
Objectives
Introduction
Understanding the common type system
Intrinsic CTS Data Types
Understanding the Common Languages Specification
Understanding the Common Language Runtime
A tour of the .Net Namespaces
2.8
2.9
2.10
2.11
2.12
Increasing Your Namespace Nomenclature
Deploying the .NET Runtime
Summary
Keywords
Exercises
2.1 Objectives
At the end of this lesson, the students will understand the following tasks in detail:

The Common Type System

CTS Data types

The Common Language Specification

The Common Language Runtime

Namespaces
2.2 Introduction
In the previous unit, we have seen the basic building blocks of .NET framework. Here,
we will elaborate each of these building blocks in detail. For the thorough
understanding of .NET framework and the languages that can be worked on the
framework, one should understand the concepts of CLR, CTS and CLS in detail.
2.3 Understanding the Common Type System

A given assembly may contain any number of distinct “types.”

In the world of .NET, “type” is simply a generic term used to refer to a member
from the set {class, structure, interface, enumeration, delegate}.

When you build solutions using a .NET-aware language, you will most likely
interact with each of these types.

For example, your assembly may define a single class that implements some
number of interfaces. Perhaps one of the interface methods takes an
enumeration type as an input parameter and returns a structure to the caller.

The Common Type System (CTS) is a formal specification that documents how
types must be defined in order to be hosted by the CLR.

Usually, the people who will be building tools and/or compilers that target the
.NET platform are concerned with the inner workings of the CTS.

However, for all .NET programmers it is important to learn about how to work
with the five types defined by the CTS in their language of choice.
Now, we will discuss the usage of five types defined by CTS.
CTS Class Types
Every .NET-aware language supports, the notion of a class type, which is the basis of
object-oriented programming (OOP). A class may be composed of any number of
members (such as properties, methods, and events) and data points. In C#, classes are
declared using the class keyword. For example,
// A C# class type
public class Calc
{
public int Add(int x, int y)
{
return x + y;
}
}
CTS allow a given class to support virtual and abstract members that define a
polymorphic interface for derived classes. But CTS classes may only derive from a
single base class (multiple inheritance is not allowed fro class).
Following table lists number of characteristics pertaining to class types.
Class Characteristic
Is the class “sealed” or
not?
Meaning
Sealed classes cannot function as a base class to other
classes.
Does the class implement
any interfaces?
An interface is a collection of abstract members that provide a
contract between the object and object user. The CTS allows a
class to implement any number of interfaces.
Is the class abstract or
concrete?
Abstract classes cannot be directly created, but are intended to
define common behaviors for derived types. Concrete classes
can be created directly.
What is the “visibility” of
this class?
Each class must be configured with a visibility attribute.
Basically, this feature defines if the class may be used by
external assemblies, or only from within the defining assembly
(e.g., a private helper class).
CTS Structure Types
The concept of a structure is also formalized under the CTS.

Structure is a user-defined type (UDT), which can be thought of as a lightweight
class type having value-based semantics.

CTS structures may define any number of parameterized constructors.

CTS structures are derived from a common base class: System.ValueType

This base class configures a type to behave as a stack-allocated entity rather
than a heap-allocated entity.

The CTS permits structures to implement any number of interfaces; but,
structures may not become a base type to any other classes or structures.
Therefore structures are explicitly sealed.

Typically, structures are best suited for modeling geometric and mathematical
data, and are created in C# using the struct keyword.
Consider an example:
// A C# structure type
struct Point
{
// Structures can contain fields.
public int xPos, yPos;
// Structures can contain parameterized constructors.
public Point(int x, int y)
{
xPos = x;
yPos = y;
}
// Structures may define methods.
public void Display()
{
Console.WriteLine("({0}, {1})", xPos, yPos);
}
}
CTS Interface Types

Interfaces are nothing more than a named collection of abstract member
definitions, which may be supported (i.e., implemented) by a given class or
structure.

Interfaces are the only .NET type that does not derive from a common base
type.

This indicates that interfaces are pure protocol and do not provide any
implementation.

On their own, interfaces are of little use. However, when a class or structure
implements a given interface, you are able to request access to the supplied
functionality using an interface reference in a polymorphic manner.

When you create custom interfaces using a .NET aware programming language,
the CTS permits a given interface to derive from multiple base interfaces.

This allows to build some interesting behaviors.

In C#, interface types are defined using the interface keyword, for example:
// A C# interface type.
public interface IDraw
{
void Draw();
}
CTS Enumeration Types

Enumerations are a handy programming construct that allows you to group
name/value pairs under a specific name.

By default, the storage used to hold each item within enumeration type is a 32bit integer (System.Int32).

However, it is possible to alter this storage slot if needed (e.g., when
programming for a low-memory device such as a Pocket PC).

The CTS demands that enumerated types derive from a common base class,
System.Enum. This base class defines a number of interesting members that
allow you to extract, manipulate, and transform the underlying name/value
pairs programmatically.

In C#, enumerations are defined using the keyword enum.
Consider an example of creating a video-game application that allows the player to
select one of three character categories (Wizard, Fighter, or Thief). Rather than keeping
track of raw numerical values to represent each possibility, you could build a custom
enumeration as:
// A C# enumeration type
public enum CharacterType
{
Wizard = 100,
Fighter = 200,
Thief = 300
}
CTS Delegate Types

Delegates are the .NET equivalent of a type-safe C-style function pointer.

The key difference is that a .NET delegate is a class that derives from
System.MulticastDelegate, rather than a simple pointer to a raw memory
address.

Delegates are useful when you wish to provide a way for one entity to forward a
call to another entity.

Delegates provide intrinsic support for multicasting, i.e. forwarding a request to
multiple recipients.

They also provide asynchronous method invocations.

They provide the foundation for the .NET event architecture.

. In C#, delegates are declared using the delegate keyword as shown in the
following example:
// This C# delegate type can 'point to' any method
// returning an integer and taking two integers as input.
public delegate int BinaryOp(int x, int y);
CTS Type Members
We have seen till now that most types in CTS can take any number of members.
Formally speaking, a type member is constrained by the set {constructor, finalizer,
static constructor, nested type, operator, method, property, indexer, field, read only
field, constant, event}. The CTS defines various “adornments” that may be associated
with a given member. For example, each member has a given visibility feature (e.g.,
public, private, protected). Some members may be declared as abstract to enforce a
polymorphic behavior on derived types as well as virtual to define a canned (but
overridable) implementation. Also, most members may be configured as static (bound
at the class level) or instance (bound at the object level). The construction of type
members is examined later in detail.
2.4 Intrinsic CTS Data Types
CTS provide a well-defined set of intrinsic data types used by all .NET aware
languages. Although a given language typically has a unique keyword used to declare
an intrinsic CTS data type, all language keywords ultimately resolve to the same type
defined in an assembly named mscorlib.dll.
Following table lists how key CTS data types are expressed in various .NET languages.
.NET Base Type
(CTS Data Type)
System.Byte
VB.NET Keyword
C# Keyword
Byte
byte
Managed Extensions
for C++ Keyword
unsigned char
System.SByte
System.Int16
System.Int32
System.Int64
SByte
Short
Integer
Long
sbyte
short
int
long
signed char
short
int or long
__int64
System.UInt16
System.UInt32
UShort
UInteger
ushort
uint
System.UInt64
ULong
ulong
unsigned
unsigned
unsigned
unsigned
System.Single
System.Double
System.Object
System.Char
Single
Double
Object
Char
float
double
object
char
Float
Double
Object^
wchar_t
System.String
System.Decimal
System.Boolean
String
Decimal
Boolean
string
decimal
bool
String^
Decimal
Bool
short
int or
long
__int64
2.5 Understanding Common Language Specification
We know that, different languages express the same programming constructs in
unique, language specific terms. For example, in C# you denote string concatenation
using the plus operator (+), while in VB .NET we use the ampersand (&). Even when
two distinct languages express the same programmatic idiom (e.g., a function with no
return value), the chances are very good that the syntax will appear quite different on
the surface:
' VB .NET method returning nothing.
Public Sub MyMethod()
' Some code...
End Sub
// C# method returning nothing.
public void MyMethod()
{
// Some code...
}
Since the respective compilers (like vbc.exe and csc.exe) produce similar CIL
instructions, the variation in syntax is minor for .NET runtime. However, languages
can also differ with regard to their overall level of functionality. For example, C# may
allow to overload some type of operator which VB.NET may not. Given these possible
variations, it would be ideal to have a baseline to which all .NET-aware languages are
expected to conform.
The Common Language Specification (CLS) is a set of rules that describe the small and
complete set of features. These features are supported by a .NET-aware compiler to
produce a code that can be hosted by CLR. Also, this code can be accessed by all
languages in the .NET platform.
In many ways, the CLS can be viewed as a subset of the full functionality defined by
the CTS. The CLS is ultimately a set of rules that compiler builders must follow, if they
plan their products to function properly within the .NET universe. Each rule describes
how this rule affects those who build the compilers as well as those who interact with
them. For example, the CLS Rule 1 says:
Rule 1: CLS rules apply only to those parts of a type that are exposed outside the
defining assembly.
Given this rule, we can understand that the remaining rules of the CLS do not apply
to the logic used to build the inner workings of a .NET type. The only aspects of a type
that must match to the CLS are the member definitions themselves (i.e., naming
conventions, parameters, and return types). The implementation logic for a member
may use any number of non-CLS techniques, as the outside world won’t know the
difference.
To illustrate, the following Add() method is not CLS-compliant, as the parameters and
return values make use of unsigned data (which is not a requirement of the CLS):
public class Calc
{
// Exposed unsigned data is not CLS compliant!
public ulong Add(ulong x, ulong y)
{
return x + y;
}
}
We can make use of unsigned data internally as follows:
public class Calc
{
public int Add(int x, int y)
{
// As this ulong variable is only used internally,
// we are still CLS compliant.
ulong temp;
temp= x+y;
return temp;
}
}
Now, we have a match to the rules of the CLS, and can assured that all .NET
languages are able to invoke the Add() method.
In addition to Rule 1, the CLS defines numerous other rules. For example, the CLS
describes how a given language must represent text strings, how enumerations should
be represented internally (the base type used for storage), how to define static
members, and so on. But the internal understanding of the CTS and CLS
specifications is for tool/compiler builders.
Ensuring CLS Compliance
C# does define a number of programming constructs that are not CLS-compliant. But,
we can instruct the C# compiler to check the code for CLS compliance using a single
.NET attribute:
// Tell the C# compiler to check for CLS compliance.
[assembly: System.CLSCompliant(true)]
This statement must be placed outside the scope of any namespace. The
[CLSCompliant] attribute will instruct the C# compiler to check each and every line of
code against the rules of the CLS. If any CLS violations are discovered, we will receive
a compiler error and a description of the offending code.
2.6 Understanding Common Language Runtime
Programmatically speaking, the term runtime can be understood as a collection of
external services that are required to execute a given compiled unit of code. For
example, when developers make use of the Microsoft Foundation Classes (MFC) to
create a new application, they are aware that their program requires the MFC runtime
library (i.e., mfc42.dll). Other popular languages also have a corresponding runtime.
VB6 programmers are also tied to a runtime module (e.g., msvbvm60.dll). Java
developers are tied to the Java Virtual Machine (JVM) and so on.
The .NET platform offers a runtime system, which can be described as follows:

The key difference between the .NET runtime and the various other runtimes is
that the .NET runtime provides a single well-defined runtime layer that is shared
by all languages and platforms that are .NET-aware.

The root of the CLR is physically represented by a library named mscoree.dll
(Common Object Runtime Execution Engine).

When an assembly is referenced for use, mscoree.dll is loaded automatically, which
in turn loads the required assembly into memory.

The runtime engine is responsible for a number of tasks.

First and foremost, it is the entity in-charge of resolving the location of an
assembly and finding the requested type within the binary by reading the
contained metadata.

The CLR then lays out the type in memory, compiles the associated CIL into
platform-specific instructions, performs any necessary security checks, and then
executes the code in question.

In addition to loading your custom assemblies and creating your custom types, the
CLR will also interact with the types contained within the .NET base class libraries
when required.
Although the entire base class library has been broken into a number of discrete
assemblies, the key assembly is mscorlib.dll. The mscorlib.dll contains a large number
of core types that encapsulate a wide variety of common programming tasks as well as
the core data types used by all .NET languages. When you build .NET solutions, you
automatically have access to this particular assembly.
Figure 2.1 illustrates the workflow that takes place between the source code (which is
making use of base class library types), a given .NET compiler, and the .NET execution
engine.
.NET source
code written
in some
.NET-aware
Language
Some .NET
compiler
DLL or EXE
Assembly
(CIL, Metadata and
Manifest)
Base Class
Libraries
(mscorlib.dll
and so on)
.NET Execution Engine (mscoree.dll)
Class Loader
Jitter
PlatformSpecific
Instructions
Execute the
application
Figure 2.1 mscoree.dll in action
2.7 A Tour of the .NET Namespaces
Unlike C, C++ and Java, the C# language does not provide any language-specific code
library.
Instead, C# provides the language-neutral .NET libraries.
To keep all the
types within the base class libraries, the .NET platform makes extensive use of the
namespace concept.
A namespace is a grouping of related types contained in an
assembly. In other words, namespace is a way to group semantically related types
(classes, enumerations, interfaces, delegates and structures) under a single umbrella.
For example, the System.IO namespace contains file I/O related types, the
System.Data namespace defines basic database types, and so on. It is very important
to point out that a single assembly (such as mscorlib.dll) can contain any number of
namespaces, each of which can contain any number of types.
The advantage of having namespaces is that, any language targeting the .NET runtime
makes use of same namespaces and same types as a C# developer.
consider following programs written in C#, VB.NET and MC++.
// C# code
For example,
using System;
public class Test
{
public static void Main()
{
Console.WrtieLine(“Hello World”);
}
}
// VB.NET code
Imports System
Public Module Test
Sub Main()
Console.WrtieLine(“Hello World”)
End Sub
End Module
// MC++ code
#using <mscorlib.dll>
using namespace System;
void Main()
{
Console::WrtieLine(“Hello World”);
}
Note that each language is making use of Console class defined in the System
namespace. Apart from minor syntactic variations, three programs look very similar,
both physically and logically.
There are numerous base class namespaces within .NET. The most fundamental
namespace is System. The System namespace provides a core body of types that the
programmer need to control time. In fact, one cannot build any sort of functional C#
application without at least making a reference to the System namespace. Following
table shows some of the namespaces:
.NET Namespace
System
System.Collections
System.Collections.Generic
System.Data
System.Data.Odbc
System.Data.OracleClient
System.Data.OleDb
System.Data.SqlClient
System.Diagnostics
System.Drawing
System.Drawing.Drawing2D
System.Drawing.Printing
System.IO
System.IO.Compression
System.IO.Ports
System.Net
System.Reflection
System.Reflection.Emit
System.Runtime.InteropServices
System.Runtime.
Remoting
Meaning
System contains numerous useful types dealing with
intrinsic data, mathematical computations, random
number generation, environment variables, and garbage
collection, as well as a number of commonly used
exceptions and attributes.
These namespaces define a number of stock container
objects
(ArrayList, Queue etc), as well as base types and
interfaces that allow you to build customized collections.
As of .NET 2.0, the collection types have been extended
with generic capabilities.
These namespaces are used for interacting with databases
using ADO.NET.
Here, you find numerous types that can be used to
programmatically debug and trace your source code.
Here, you find numerous types wrapping graphical
primitives such as bitmaps, fonts, and icons, as well as
printing capabilities.
These namespaces include file I/O, buffering, and so
forth. As of .NET 2.0, the IO namespaces now include
support compression and port manipulation.
This namespace (as well as other related namespaces)
contains types related to network programming
(requests/responses, sockets, end points, and so on).
These namespaces define types that support runtime type
discovery as well as dynamic creation of types.
This namespace provides facilities to allow .NET types to
interact with “unmanaged code” (e.g., C-based DLLs and
COM servers) and vice versa.
This namespace (among others) defines types used to
build solutions that incorporate the .NET remoting layer.
System.Security
System.Threading
System.Web
System.Windows.Forms
System.Xml
Security is an integrated aspect of the .NET universe. In
the security-centric namespaces you find numerous types
dealing with permissions, cryptography, and so on.
This namespace defines types used to build multithreaded
applications.
A number of namespaces are specifically geared toward
the development of .NET web applications, including
ASP.NET and XML web services.
This namespace contains types that facilitate the
construction of traditional desktop GUI applications.
The XML-centric namespaces contain numerous types
used to interact with XML data.
Accessing a Namespace Programmatically
A namespace is a convenient way to logically understand and organize related types.
Consider the System namespace. From programmer’s perspective, System.Console
represents a class named Console that is contained within a namespace called System.
However, in the eyes of the .NET runtime, it is a single entity named System.Console.
In C#, the using keyword simplifies the process of referencing types defined in a
particular namespace. In a traditional desktop application, we can include any
number of namespaces like –
using System;
// General base class library types.
using System.Drawing; // Graphical rendering types.
Once we specify a namespace, we can create instances of the types they contain. For
example, if we are interested in creating an instance of the Bitmap class, we can write:
using System;
using System.Drawing;
class MyApp
{
public void DisplayLogo()
{
// create a 20x20 pixel bitmap.
Bitmap bm = new Bitmap(20, 20);
...
}
}
As this application is referencing System.Drawing, the compiler is able to resolve the
Bitmap class as a member of this namespace. If we do not specify the System.Drawing
namespace, we will get a compiler error. However, we can declare variables using a
fully qualified name as well:
using System;
class MyApp
{
public void DisplayLogo()
{
// Using fully qualified name.
System.Drawing.Bitmap bm =new System.Drawing.Bitmap(20, 20);
...
}
}
Remember that both the approaches (a short-hand method with using and making use
of fully qualified name) results in the exact same underlying CIL and has no effect on
performance or the size of the assembly.
2.8 Increasing Your Namespace Nomenclature
There are thousands of namespaces in .NET framework. But what makes a namespace
unique is that the types it defines are all somehow semantically related. So, depending
on our requirement, we can make use of only required namespaces. For example, if we
are
building
console
applications,
then
we
need
not
worry
about
System.Windows.Forms and System.Drawing namespaces. To know more about
specific namespaces, we can use any one of the following:

.NET SDK online documentation (MSDN – Microsoft Developer Network)

The ildasm.exe utility

The Class Viewer Web application

The wincv.exe desktop application

The Visual Studio.NET integrated Object Browser
2.9 Deploying the .NET Runtime
The .NET assemblies can be executed only on a machine that has the .NET Framework
installed. As an individual who builds .NET software, this should never be an issue, as
your development machine will be properly configured at the time you install the freely
available .NET Framework 2.0 SDK or a commercial .NET development environments
such as Visual Studio 2005.
But, we can not copy and run a .NET application in a computer in which .NET is not
installed. However, if you deploy an assembly to a computer that does not have .NET
installed, it will fail to run. For this reason, Microsoft provides a setup package named
dotnetfx.exe that can be freely shipped and installed along with your custom
software. This installation program is included with the .NET Framework 2.0 SDK,
and it is also freely downloadable from www.microsoft.com.
Once dotnetfx.exe is installed, the target machine will now contain the .NET base class
libraries, .NET runtime (mscoree.dll), and additional .NET infrastructure (such as the
GAC, Global Assembly Cashe).
Note that if you are building a .NET web application, the end user’s machine does not
need to be configured with the .NET Framework, as the browser will simply receive
generic HTML and possibly client-side JavaScript.
2.10 Summary
In this Unit, we have explored the CTS, CLR and CLS in detail. We have examined a
number of tools that allow you to check out the types within a given .NET namespace.
We wrapped up with a brief examination of the process of configuring a given machine
to host the .NET runtime. With this necessary preamble complete, you can now begin
to build .NET assemblies using C#.
2.11 Keywords







Structure
Enumeration
Class
Interface
Delegate
Microsoft Common Object Runtime Execution Engine (mscoree)
Namespaces

using
2.12 Exercises
1. What are namespaces? List and explain the purpose of at least five namespaces.
2. Explain with a neat diagram, the workflow that takes place between your source code, a
given .NET complier and the .NET execution engine.
3. Explain common type system in detail.
4. What are basic CTS data types? Explain.
Module 1
Unit 3
CONTENTS:
1.17
3.2
Objectives
Introduction
3.3 Role of Command Line Compiler (csc.exe)
3.4 Building C# Applications using csc.exe
3.5 Working with csc.exe Response Files
3.6 Generating Bug Reports
3.7 Remaining C# Compiler Options
3.8 The Command Line Debugger (cordbg.exe)
3.9 Using the Visual Studio.NET IDE
3.10
The C# “Pre-processor” Directives
3.11
An Interesting Aside: System.Environment Class
3.12
Summary
3.13
Keywords
3.14
Exercises
3.1 Objectives
At the end of this lesson, the students will understand the following tasks in detail:

The Role of Command Line Compiler

Building Applications using csc.exe

Response files

Bug Reports

Compiler Options

Command Line Debugger
3.2 Introduction
C# is one of many possible languages which may be hosted by Visual Studio.NET
(VS.NET). Unlike previous editions of Microsoft IDEs (Integrated Development
Environments), the professional and enterprise versions of VS.NET may be used to
build various types of projects like C#, J#, VB.NET, MC++ and so on. In this unit, we
will examine raw C# compiler. We will see how to set up environment variables to run
C# applications using command line compiler, how to generate bug reports, how to
generate response files, what are preprocessor directives and so on. This unit also
offers a grand tour of the key features of the VS.NET IDE within the context of C#.
3.3 The Role of Command Line Compiler (csc.exe)
There are a number of techniques to compile C# source code. One way is to use the C#
command-line compiler, csc.exe (where csc stands for C-Sharp Compiler). This tool is
included with the .NET Framework 2.0 SDK. Though we may not build a large-scale
application using the command-line compiler, it is important to understand the basics
of how to compile *.cs files by hand. Few reasons for having a grip on the process are:

The most obvious reason is the simple fact that one may not have a copy of
Visual Studio 2005. But only free .NET SDK.

One may plan to make use of automated build tools such as MSBuild or NAnt.

One may want to understand C# deeply. When you use graphical IDEs to build
applications, you are ultimately instructing csc.exe how to manipulate your C#
input files. In this light, it is better to see what takes place behind the scenes.

Another benefit of working with csc.exe is that you become that much more
comfortable in manipulating other command-line tools included with the .NET
Framework 2.0 SDK. Moreover, a number of important utilities are accessible
only from the command line.
Configuring the C# Command-Line Compiler
Before starting the C# command-line compiler, we need to ensure that our
development machine recognizes the existence of csc.exe. If the machine is not
configured correctly, we have to specify the full path to the directory containing
csc.exe before compiling C# files. We have to set the path by following the steps:
1. Right-click the My Computer icon and select Properties from the pop-up
menu.
2. Select the Advanced tab and click the Environment Variables button.
3. Double-click the Path variable from the System Variables list box.
4. Add the following line to the end of the current Path value:
C:\Windows\Microsoft.NET\Framework\v2.0.50215
Note that, the above line may not be exactly the same in every computer. We must
enter the current version and location of .NET Framework SDK. To check whether the
setting has been done properly or not, open a command prompt and type
csc -?
Or
csc /?
If settings were correct, we will see a list of options supported by the C# compiler.
Configuring Additional .NET Command-Line Tools
We have to set Path variable with one more line as
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin
This directory contains additional command-line tools that are commonly used during
.NET development. With these two paths established, we will be able to run any .NET
utility from any command window. To confirm this new setting, enter the following
command in a command prompt to view the options of the GAC (global assembly
cashe) utility, gacutil.exe:
gacutil -?
Note that the above explained method is to compile the C# program in any command prompt. In
stead of that, we can directly use the SDK in the Visual Studio Tools menu.
3.4 Building C# Applications Using csc.exe
To build a simple C# application, open a notepad and type the following code:
// A simple C# application.
using System;
class Test
{
public static void Main()
{
Console.WriteLine("Hello World!!!");
}
}
Now, save this file in any convenient location as Test.cs. Now, we have to provide the
option for which type of output file we want. The options for output are given in the
following table:
File Output
Option
/out
/target:exe
/target:library
/target:module
/target:winexe
Meaning
This option is used to specify the name of the assembly to be created.
By default, the assembly name is the same as the name of the initial
input *.cs -file (in the case of a *.dll) or the name of the type
containing the program’s Main() method (in the case of an *.exe).
This option builds an executable console application. This is the
default file output type, and thus may be omitted when building this
application type.
This option builds a single-file *.dll assembly.
This option builds a module.Modules are elements of multifile
assemblies.
Although you are free to build Windows-based applications using the
/target:exe flag, the /target:winexe flag prevents a console window
from appearing in the background.
To compile TestApp.cs, use the command:
csc /target:exe Test.cs
Most of the C# compilers support an abbreviated version, such as /t rather than
/target. Moreover, /target is a default option. So, we can give simply,
csc Test.cs
Now an exe file will be created with the name Test.exe. Now the program can be run by
typing –
Test.exe
at the command prompt.
Referencing External Assemblies
Here we will see how to compile an application that makes use of types defined in a
separate .NET assembly. Note that mscorlib.dll is automatically referenced during the
compilation process. To illustrate the process of referencing external assemblies,
consider an example –
using System;
using System.Windows.Forms;
class Test
{
public static void Main()
{
MessageBox.Show("Hello...");
}
}
Since
we
have
made
use
of the
MessageBox
class, we
must specify
the
System.Windows.Forms.dll assembly using the /reference flag which can be
abbreviated to /r as –
csc /r:System.Windows.Forms.dll Test.cs
Now, running the application will give the output as –
Compiling Multiple Source Files with csc.exe
When we have more than one *.cs source file, then we can compile them together. For
illustration, consider the following example –
File Name: MyTest.cs
using System;
using System.Windows.Forms;
class MyTest
{
public void display()
{
MessageBox.Show("Hello...");
}
}
File Name: Test.cs
using System;
class Test
{
public static void Main()
{
MyTest t = new MyTest ();
t.display();
}
}
We can compile C# files by listing each input file explicitly:
csc /r:System.Windows.Forms.dll Test.cs MyTest.cs
As an alternative, the C# compiler allows you to make use of the wildcard character (*)
to inform csc.exe to include all *.cs files contained in the project directory:
csc /r:System.Windows.Forms.dll *.cs
Referencing Multiple External Assemblies
If we want to reference numerous external assemblies, then we can use a semicolondelimited list. For example:
csc /r:System.Windows.Forms.dll; System.Drawing.dll *.cs
3.5 Working with csc.exe Response Files
When we are building complex C# applications, we may need to use several flags that
specify numerous referenced assemblies and *.cs input files. To reduce the burden of
typing those flags every time, the C# compiler provides response files. C# response
files contain all the instructions to be used during the compilation of a program. These
files end in a *.rsp (response) extension.
Consider the following file, which is a response file for the example Test.cs discussed
in the previous section.
# This is the response file for the Test.exe app
# External assembly references.
/r:System.Windows.Forms.dll
# output and files to compile (using wildcard syntax).
/target:exe /out:Test.exe *.cs
Note that # symbol is used for comment line. If we have saved this file in the same
directory as the C# source code files to be compiled, we have to use following
command to run the program.
csc @Test.rsp
If needed, we can specify multiple *.rsp files as input (e.g., csc @FirstFile.rsp
@SecondFile.rsp @ThirdFile.rsp). In case of multiple response files, the compiler
processes the command options as they are encountered. Therefore, command-line
arguments in a latter *.rsp file can override options in a previous response file.
Also note that flags listed explicitly on the command line before a response file will be
overridden by the specified *.rsp file. Thus, if we use the statement,
csc /out:MyCoolApp.exe @Test.rsp
the name of the assembly would still be Test.exe (rather than MyCoolApp.exe), given
the /out:Test.exe flag listed in the Test.rsp response file. However, if we list flags after
a response file, the flag will override settings in the response file.
Note The /reference flag is cumulative. Regardless of where you specify external
assemblies (before, after, or within a response file) the end result is a summation of
each reference assembly.
The Default Response File (csc.rsp)
C# compiler has an associated default response file (csc.rsp), which is located in the
same
directory
as
csc.exe
itself
(e.g.,
C:\Windows\Microsoft.NET\Framework\v2.0.50215). If we open this file using
Notepad, we can see that numerous .NET assemblies have already been specified
using the /r: flag. When we are building our C# programs using csc.exe, this file will
be automatically referenced, even if we provide a custom *.rsp file. Because of default
response file, the current Test.exe application could be successfully compiled using
the following command set
csc /out:Test.exe *.cs
If we wish to disable the automatic reading of csc.rsp, we can specify the /noconfig
option:
csc @Test.rsp /noconfig
3.6 Generating Bug Reports
The C# compiler provides a flag named /bugreport to specify a file that will be
populated by csc.exe with various statistics regarding current build; including any
errors encountered during the compilation. The syntax for using this flag is–
csc /bugreport:bugs.txt *.cs
We can enter any corrective information for possible errors in the program, which will
be saved to the specified file. For example, consider the program –
using System;
class Test
{
public static void Main()
{
Console.WriteLine(“Hello”) //note that ; is not given
}
}
When we compile this file using /bugreport flag, the error message will be displayed
and corrective action is expected as shown –
Test.cs (23, 11): Error CS1002: ; expected
----------------Please describe the compiler problem: _
Now if we enter the statement like
FORGOT TO TYPE SEMICOLON
then, the same will be stored in the bugs.txt file.
3.7 Remaining C# Compiler Options
C# compiler has many flags that can be used to control how the .NET assembly to be
generated. Following table lists some of the flags and their meaning.
Command Line
flags of csc.exe
@
/? Or /help
/addmodule
/baseaddress
/bugreport
/checked
/codepage
/debug
/define
/doc
/filealign
/fullpaths
/incremental
/lib
/linkresource
/main
/nologo
/nostdlib
/noconfig
/nowarn
/optimize
/out
Meaning
Allows to specify a response file used during compilation
Prints the list of all command line flags of csc.exe
Used to specify the modules to add to a multifile assembly
Used to specify the preferred base address at which to load a *.dll
Used to build text-based bug reports for the current compilation
Used to specify whether integer arithmetic that overflows the
bounds of the data type will cause an exception at run time
Used to specify the code page to use for all source code files in the
compilation
Forces csc.exe to emit debugging information
Used to define preprocessor symbols
Used to construct an XML documentation file
Specifies the size of sections in the output file
Specifies the absolute path to the file in compiler output
Enables incremental compilation of source code files
Specifies the location of assemblies referenced via /reference
Used to create a link to a managed resource
Specifies which Main() method to use as the program’s entry point if
multiple Main() methods have been defined in the current *.cs file
set.
Suppresses compiler banner information when compiling the file
Prevents the automatic importing of the core .NET library,
mscorlib.dll
Prevents the use of *.rsp files during the current compilation
Suppress the compiler’s ability to generate specified warnings
Enable or disable optimization
Specifies the name of the output file
/recurse
/reference
/resource
/target
/unsafe
/utf8output
/warn
/warnaserror
/win32icon
/win32res
Searches subdirectories for source files to compile
Used to reference an external assembly
Used to embed .NET resources into the resulting assembly
Specifies the format of the output file.
Compiles code that uses the C# “unsafe” keyword
Displays compiler output using UTF-8 encoding
Used to sent warning level for the compilation cycle
Used to automatically promote warnings to errors
Inserts an .ico file into the output file
Inserts a Win32.resource into the output file
3.8 The Command-Line Debugger (cordbg.exe)
The .NET Framework SDK provides a command-line debugger named cordbg.exe.
This tool provides dozens of options that allow you to debug your assembly. You may
view them by specifying the /? flag:
cordbg /?
Following table lists some of the flags recognized by cordbg.exe (with the alternative
shorthand notation) once you have entered a debugging session.
Command Line
Flag of cordbg.exe
b[reak]
del[ete]
ex[it]
g[o]
o[ut]
p[rint]
si
so
Meaning
Set or display current breakpoints.
Remove one or more breakpoints.
Exit the debugger
Continue debugging the current process until hitting next
breakpoint.
Step out of the current function.
Print all loaded variables (local, arguments, etc.).
Step into the next line.
Step over the next line.
Usually, we will make use of the VS.NET integrated debugger. Hence, the cordbg.exe is
rarely used in reality.
Debugging at the Command Line
Before you can debug your application using cordbg.exe, the first step is to generate
debugging symbols for your current application by specifying the /debug flag of
csc.exe. For example, to generate debugging data for TestApp.exe, enter the following
command set:
csc @testapp.rsp /debug
This generates a new file named testapp.pdb. If you do not have an associated *.pdb
file, it is still possible to make use of cordbg.exe; however, you will not be able to view
your C# source code during the process (which is typically no fun whatsoever, unless
you wish to complicate matters by reading CIL code). Once you have generated a *.pdb
file, open a session with cordbg.exe by specifying your .NET assembly as a commandline argument (the *.pdb file will be loaded automatically):
cordbg.exe testapp.exe
At this point, you are in debugging mode and may apply any number of cordbg.exe
flags at the (cordbg) command prompt.
3.9 Using the Visual Studio .NET IDE
Till now, we have discussed how to compile C# programs in command prompt. Visual
Studio .NET provides an IDE (Integrated Development Environment) to build
applications using any number of .NET-aware languages like C#, VB.NET, J#, MFC
etc. Here, we will examine some of the core features of VS.NET IDE.
The VS .NET Start Page
When we launch VS .NET, we can see start page as –
This will provide an option to open existing projects or to create new projects.
Creating a VS .NET Project Solution
Once we opt for creating new project, then, we can see the following window:
This window will allow us to choose the language (C#, VB.NET etc), then type of the
application (windows, console etc). Then we can provide the name of the application
and the location where the application to be stored. Following is a list of project types
provided by C#.
Project Type
Windows Application
Class Library
Windows Control Library
ASP.NET Web Application
ASP.NET Web Service
Web Control Library
Console Application
Windows Services
Meaning
Represents Windows Form application
Used to build a single file assembly (*.dll)
Allows to build a single file assembly (*.dll) that contains
custom Windows Forms Controls. (ActiveX controls)
Used to build a web application
Used to build a .NET Web Service. Web Service is a block
of code, reachable using HTTP requests.
Used to build customized Web controls. These GUI
widgets are responsible for emitting HTML to a
requesting browser.
Just like command window.
Used to build NT/2000 services. Services are
background worker applications that are launched
during OS boot process.
3.10 C# Preprocessor Directives
Like C and C++, C# provides various symbols to interact with the compilation process.
But, the term preprocessor doesn’t exactly mean what it implies in C or C++. There is
no separate preprocessing step in C#. Rather, preprocessor directives are processed as
a part of the lexical analysis phase of the compiler. However, the syntax of C#
preprocessor is similar to that of C and C++. Following is a list of preprocessors
supported by C#.
C# Preprocessor Symbol
#define, #undef
#if, #elif, #else, #endif
#line
#error, #warning
#region, #endregion
Meaning
Used to define and un-define conditional compilation
symbol.
Used to conditionally skip sections of source code.
Used to control the line numbers emitted for errors and
warnings
Used to issue errors and warnings for the current build
Used to explicitly mark sections of source code. Under
VS.NET, regions may be expanded and collapsed within
the code window. Other IDEs (including text editors) will
ignore these symbols.
Specifying Code Regions
Using #region and #endregion tags, we can specify a block of code that may be hidden
from view and identified by a textual marker. The use of regions helps to keep lengthy
*.cs files more manageable. For example, we can create a region for defining a type’s
constructor (may be class, structure etc), type’s properties and so on.
Consider the following code:
class Test
{
…….
#region Unwanted
public class sub1
{
…….
}
public interface sub2
{
…………
}
#endregion
}
Now, when we put mouse curser on that region, it will be shown as –
Conditional Code Compilation:
The preprocessor directives #if, #elif, #else and #endif allows to conditionally compile a
block of code based on predefined symbols. Consider the following example –
#define MAX
using System;
class Test
{
public static void Main()
{
#if(MAX)
Console.WriteLine("MAX is defined");
#else
Console.WriteLine("MAX is not defined");
#endif
}
}
The output will be: MAX is defined
Issuing Warnings and Errors:
The #warning and #error directives allows to instruct the C# compiler to generate a
warning or error. For example, we wish to issue a warning when a symbol is defined in
the current project. This is helpful when a big project is handled by many people.
Consider the following code:
#define MAX
using System;
class Test
{
public static void Main()
{
#if(MAX)
#warning MAX is defined by me!!!
………..
#endif
}
}
When we compile, the program will give warning message –
Test.cs(7,21): warning CS1030: #warning: 'MAX is defined by me!!!'
Altering Line Numbers:
The directive #line is rarely used in any project. This directive allows to alter the
compiler’s recognition of #line numbers during its recording of compilation warnings
and errors. To reset the default line numbering, we can specify default tag. Consider
the following code:
#define MAX
using System;
class Test
{
public static void Main()
{
#line 300
//next line will take the number 300
#warning MAX is defined by me!!!
………..
#line default // default numbering will continue
}
}
When we compile, the program will give warning message –
Test.cs(300,21): warning CS1030: #warning: 'MAX is defined by me!!!'
Note that the line number appeared as 300, though the actual line was 7.
3.11 An Interesting Aside: The System.Environment
Class
The System.Environment class allows to obtain a number of details regarding the
context of the operating system hosting .NET application by using various static
members. Consider the following example –
using System;
class Test
{
public static void Main(string[] args)
{
Console.WriteLine("OS: {0}", Environment.OSVersion);
Console.WriteLine("Current Directory: {0}",
Environment.CurrentDirectory);
string[] s = Environment.GetLogicalDrives();
for(int i=0;i<s.Length;i++)
Console.WriteLine("Drive {0}: {1} ", i, s[i]);
Console.WriteLine("Version of .NET: {0}", Environment.Version);
}
}
The Output will be (depending on OS and version of .NET, it may vary) –
OS: Microsoft Windows NT 5.1.2600 Service Pack 3
Current Directory: C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0
Drive 0: A:\
Drive 1: C:\
Drive 2: D:\
Drive 3: E:\
Drive 4: F:\
Version of .NET: 2.0.50727.3082
3.12 Summary
The point of this chapter was to introduce you to the process of building C#
applications. As you have seen, the ultimate recipient of your *.cs files is the C#
compiler, csc.exe. You have learnt how to use csc.exe in the raw and during the
process, you examined a number of compiler options including error reporting/bug
report, response files etc. You have also explored the key details of the VS.NET IDE.
Truly speaking, VS.NET IDE is nothing more than an elaborate wrapper around the
raw C# compiler. That is, anything that can be done with VS.NET can be done with
the raw C# compiler too.
3.13 Keywords

csc.exe

Response Files

Global Assembly Cache (GAC)

gacutil.exe

Bug Reports

Command Line debugger or cordbg.exe

Preprocessor Directives

#define, #undef

#line

#region, #endregion

#if, #elif, #endif, #else

#error, #warning

System.Environment Class
3.14 Exercises
1. Explain the role of csc.exe.
2. List and explain the basic output options available with C# complier.
3. Illustrate with an example, how do you compile multiple source files?
4. What are response files? Explain with an example.
5. How do you generate bug reports? Illustrate with an example.
6. What are C# preprocessor directives? Explain.
7. Explain the use of System.Environment class with a program.
Module 1
Unit 4
CONTENTS:
1.18
4.2
Objectives
Introduction
4.3 The Anatomy of a Basic C# Class
4.4 Creating Objects: Constructor Basics
4.5 The Composition of a C# Application
4.6 Default Assignment and Variable scope
4.7 The C# Member Initialization Syntax
4.8 Basic Input and Output with the Console Class
4.9 Defining Program Constants
4.10
C# Iteration Constructs
4.11
C# Controls Flow Constructs
4.12
The Complete set of C# Operators
4.13
Summary
4.14
Keywords
4.15
Exercises
4.1 Objectives
At the end of this lesson, the students will understand:

The structure of C# class

Constructors

Composition of C# Application

Variables, Input/Output with Console Class

Constants

Iterative and Repetitive Constructs

Operators
4.2 Introduction
In this unit, we will discuss basic concepts about C# programming language. The
syntax of the C# program, key words, variable initialization, declaration of constants
etc. are discussed here. The reading of data from the keyboard, writing the data onto
the monitor have to be revealed. The basic programming conditional constructs like if,
if-else, switch statements, the looping constructs like for, while, do/while etc are
revealed. Then various operators like arithmetic, logical, relational operators
supported by C# are discussed.
4.3 The Anatomy of a Basic C# Class
In C#, all program logic must be contained within a type definition (type may be a
member of the set {class, interface, structure, enumeration, delegate}). Unlike C or
C++, in C# it is not possible to create global functions or global points of data. In its
simplest form, a C# program can be written as follows:
Program 4.1 Test.cs
using System;
class Test
{
public static int Main(string[] args)
{
Console.WriteLine("Hello World!");
return 0;
}
}
Every executable C# application must contain a class defining a Main() method, which
is used to signify the entry point of the application. Main() is used with the public and
static keywords.
The public members are accessible from other types, while static
members are scoped at the class level (rather than the object level) and can thus be
invoked without the need to first create a new class instance (or object). In addition to
the public and static keywords, this Main() method has a single parameter, which is
an array of strings (string[] args). This parameter may contain any number of incoming
command-line arguments.
The program Test.cs makes use of the Console class, which is defined within the
System namespace. WriteLine() is a static member of Console class, which is used to
pump a text string to the standard output.
NOTE: We can use Console.ReadLine() to ensure the command prompt launched by
Visual Studio 2005 remains visible during a debugging session until we press the
Enter key.
Variations on the Main() Method
The Main() function can be take any of the following forms based on our requirement:
// No return type, array of strings as argument.
public static void Main(string[] args)
{
//some code
}
// No return type, no arguments.
public static void Main()
{
//some code
}
// Integer return type, no arguments.
public static int Main()
{
//some code
//return a value to OS
}
NOTE: The Main() method may also be defined as private. Doing so ensures other
assemblies cannot directly invoke an application’s entry point. Visual Studio 2005
automatically defines a program’s Main() method as private. Obviously, your choice of
how to construct Main() will be based on two questions. First, do you need to process
any user-supplied command-line parameters? If so, they will be stored in the array of
strings. Second, do you want to return a value to the system when Main() has
completed? If so, you need to return an integer data type rather than void.
Processing Command-Line Arguments
Assume that we need some arguments to be passed as input to the Main() funtion
before starting the program execution. This is done through command-line
parameters. Consider an example –
Program 4.2
using System;
class Test
{
public static int Main(string[] args)
{
Console.WriteLine("***** Command line args *****");
for(int i = 0; i < args.Length; i++)
Console.WriteLine("Arg: {0} ", args[i]);
}
}
Here, we are checking to whether the array of strings contains some number of items
using the Length property of System.Array. We can provide command line arguments
while running the program as –
Test.exe Good Bad Ugly
As an alternative to the standard for loop, we may iterate over incoming string arrays
using the C# foreach keyword. For example –
// Notice you have no need to check the size of the array //when
using 'foreach'.
public static int Main(string[] args)
{
...
foreach(string s in args)
Console.WriteLine("Arg: {0} ", s);
...
}
It is also possible to access command-line arguments using the static GetCommandLineArgs() method of the System.Environment type. The return value of this method is
an array of strings. The first index identifies the current directory containing the
application itself, while the remaining elements in the array contain the individual
command-line arguments. If we are using this technique, we need not define the
Main() method with string array parameter. Consider an example –
public static int Main()
//no arguments
{
// Get arguments using System.Environment.
string[] Args = Environment.GetCommandLineArgs();
Console.WriteLine("Path to this app is: {0}", Args[0]);
for(int i=1; i<Args.Length;i++)
Console.WriteLine("Arguments are: {0}", Args[i]);
}
4.4 Creating Objects: Constructor Basics
All object-oriented (OO) languages make a clear distinction between classes and
objects. A class is a definition (or, if you will, a blueprint) for a user-defined type (UDT).
An object is simply a term describing a given instance of a particular class in memory.
In C#, the new keyword is used to create an object. Unlike other OO languages (such
as C++), it is not possible to allocate a class type on the stack; therefore, if you attempt
to use a class variable that has not been “new-ed,” you are issued a compile-time
error. Thus the following C# code is illegal:
using System;
class Test
{
public static int Main(string[] args)
{
Test c1;
c1.SomeMethod();
//error!! Must use “new”
...
}
}
To illustrate the proper procedures for object creation, consider the following code:
using System;
class Test
{
public static int Main(string[] args)
{
// You can declare and create a new object in a single line...
Test c1 = new Test();
// ...or break declaration and creation into two lines.
Test c2;
c2 = new Test();
...
}
}
The new keyword will calculate the correct number of bytes for the specified object
and acquires sufficient memory from the managed heap. Here, we have allocated two
objects c1 and c2, each of which points to a unique instance of Test type. Note that C#
object variables are really a reference to the object in memory, not the actual object
itself. Thus, c1 and c2 each reference a distinct Test object allocated on the managed
heap.
In the previous program, the objects have been constructed using the default
constructor, which by
definition never takes
arguments. Every
C# class is
automatically provided with a free default constructor, which you may redefine if
needed. The default constructor ensures that all member data is set to an appropriate
default value. But in C++, un-initialized data gets garbage value.
Usually, classes provide additional constructors also. Using this, we can initialize the
object variables at the time of object creation. Like in Java and C++, in C#
constructors are named identically to the class they are constructing, and they never
provide a return value (not even void).
Consider the following example –
Program 4.3
using System;
class Test
{
public string userMessage;
// Default constructor.
public Test()
{
Console.WriteLine("Default constructor called!");
}
public Test (string msg)
{
Console.WriteLine("Custom ctor called!");
userMessage = msg;
}
public static int Main(string[] args)
{
// Call default constructor.
Test c1 = new Test ();
Console.WriteLine("Value of userMessage: {0}\n", c1.userMessage);
// Call parameterized constructor.
Test c2;
c2 = new Test ("Hello World");
Console.WriteLine("Value of userMessage: {0}", c2.userMessage);
Console.ReadLine();
return 0;
}
}
NOTE:
1. Technically speaking, when a type defines identically named members
(including constructors) that differ only in the number of—or type of—
parameters, the member in question is overloaded.
2. As soon as we define a custom constructor for a class type, the free default
constructor is removed. If we want to create an object using the default
constructor, we need to explicitly redefine it as in the preceding example.
Is That a Memory Leak?
Note that, in previous program, we have not written any code to explicitly destroy the
c1 and c2 references. In C++, if we do like this, it will lead to memory leak. But, the
.NET garbage collector frees the allocated memory automatically, and therefore C#
does not support a delete keyword.
4.5 The Composition of a C# Application
We have seen in the previous example that the Main() function creates instances of the
very own class in which it is defined. However, a more natural way to write an
application using distinct classes is as shown in the following example. In OO
terminology, this way of writing an application is known as Separation of concerns.
Program 4.4 MyTest.cs
using System;
class Test
{
public Test()
{
Console.WriteLine("Default constructor called!");
}
public void disp()
{
Console.WriteLine("Hi");
}
}
class MyTest
{
public static void Main(string[] args)
{
Test c1 = new Test ();
c1.disp();
}
}
The type (like class, structure etc) containing Main() function (that is, entry point for
the application) is called as the application object. Every C# application will be
having one application object and numerous other types. On the other hand, we can
create an application object that defines any number of members called from the
Main() method.
4.6 Default Assignments and Variable Scope
All intrinsic .NET data types have a default value. When we create custom types, all
member variables are automatically assigned to their respective default values.
Consider an example –
Program 4.5
using System;
class Test
{
public int a;
//default value is 0
public byte b;
//default value is 0
public char c;
public string s;
public static void Main()
{
Test t=new Test();
//default value is null
//default value is null
Console.WriteLine(“{0}, {1}, {2}, {3}”, a, b, c, s);
}
}
All the variables will be initialized to their default values when we start debugging.
However, we can not expect the same within method scope. For example, the following
program will generate an error.
using System;
class Test
{
public static void Main()
{
int a;
//need to assign some value to ‘a’
Console.WriteLine(“{0}”, a); //Error:Unassigned local variable ‘a’
}
}
NOTE: There is an exception for the mandatory assignment of a local variable. If the
local variable is used as an output parameter, then it need not be initialized before
use. Output parameters are discussed later in detail. However, note that, they are
used to receive the value from a function.
4.7 The C# Member Variable Initialization Syntax
Consider a class (or any other type) having more than one constructor with different
set (may be different in type or number) of arguments. Now, if any member variable
needs to be initialized with same value in all the situations, we need to write the code
repeatedly in all the constructors. For example,
class Test
{
private int a;
Test()
{
a=5;
}
Test(int k)
{
a=5;
…….
}
Test(string s)
{
a=5;
…….
}
}
Though the above code is valid, there is redundancy in code. We can write a function
for such initialization like –
class Test
{
private int a;
public void init()
{
a=5;
}
Test()
{
init();
}
Test(int k)
{
init()
…….
}
Test(string s)
{
init()
…….
}
}
Here we have function call instead of assignment. Thus, redundancy still exists. To
avoid such situation, C# provides the initialization of member variables directly within
a class as shown –
class Test
{
private int a =5;
private string s= “Hello”;
…………..
}
This facility was not available in C++. Note that, such an initialization will happen
before the constructor gets called. Hence, if we provide any other value to a variable
through constructor, the previous member assignment will be overwritten.
4.8 Basic Input and Output with the Console Class
In many of the programs what we have seen till now, made use of System.Console
class. As the name suggests, Console class is defined in System namespace and it
encapsulates input, output and error stream manipulations. This class is widely used
for console applications but not for Windows or Web applications.
There are four important static methods in Console class:

Read() Used to capture a single character from the input stream.

ReadLine()
Used to receive information from the input stream until the
enter key is pressed.

Write() This method pumps text to the output stream without
carriage return (enter key)

WriteLine()
This pumps a text string including carriage return to the
output stream.
Following table gives some important member of Console class:
Member
BackgroundColor
ForegroundColor
BufferHeight
BufferWidth
Clear()
Title
WindowHeight
WindowWidth
WindowTop
WindowLeft
Meaning
These properties set the background/foreground colors for the
current output. They may be assigned any member of the
ConsoleColor enumeration.
These properties control the height/width of the console’s buffer
area.
This method clears the buffer and console display area.
This property sets the title of the current console.
These properties control the dimensions of the console in relation
to the established buffer.
To illustrate the working of Console class methods, consider the following example –
Program 4.6
using System;
class BasicIO
{
public static void Main()
{
Console.Write("Enter your name: ");
string s = Console.ReadLine();
Console.WriteLine("Hello, {0} ", s);
Console.Write("Enter your age: ");
s = Console.ReadLine();
Console.WriteLine("You are {0} years old", s);
}
}
The output would be –
Enter your name: Ramu
Hello, Ramu
Enter your age: 25
You are 25 years old
Formatting Textual Output
In the previous examples, we have seen numerous occurrences of the tokens {0}, {1}
etc. embedded within a string literal. .NET introduces a new style of string formatting.
A simple example follows –
static void Main(string[] args)
{
...
int i = 90;
double d = 9.99;
bool b = true;
Console.WriteLine("Int is: {0}\nDouble is: {1}\nBool is: {2}", i, d, b);
}
The first parameter to WriteLine() represents a string literal that contains optional
placeholders designated by {0}, {1}, {2}, and so forth (curly bracket numbering always
begins with zero). The remaining parameters to WriteLine() are simply the values to be
inserted into the respective placeholders.
Note that WriteLine() has been overloaded to allow the programmer to specify
placeholder values as an array of objects. Thus, we can represent any number of items
to be plugged into the format string as follows:
// Fill placeholders using an array of objects.
object[] stuff = {"Hello", 20.9, 1, "There", "83", 99.99933} ;
Console.WriteLine("The Stuff: {0} , {1} , {2} , {3} , {4} , {5} ", stuff);
It is also permissible for a given placeholder to repeat within a given string. For
example, if we want to build the string "9, Number 9, Number 9" we can write
Console.WriteLine("{0}, Number {0}, Number {0}", 9);
Note If any mismatch occurs between the number of uniquely numbered curlybracket placeholders and fill arguments, an exception viz. FormatException is popped
up at runtime.
.NET String Formatting Flags
If we require more detailed formatting, each placeholder can optionally contain various
format characters (in either uppercase or lowercase), as shown –
C# Format
Character
C or c
D or d
E or e
F or f
G or g
N or n
X or x
Meaning
Used to format currency. By default, the flag will prefix the local
cultural symbol (a dollar sign [$] for U.S. English). However, this can be
changed using a System.Globalization.NumberFormatInfo object
Used to format decimal numbers. This flag may also specify the
minimum number of digits used to pad the value.
Used for exponential notation.
Used for fixed-point formatting.
Stands for general. This character can be used to format a number to
fixed or exponential format.
Used for basic numerical formatting (with commas).
Used for hexadecimal formatting. If you use an uppercase X, your hex
format will also contain uppercase characters.
These format characters are suffixed to a given placeholder value using the colon
token (e.g., {0:C}, {1:d}, {2:X}, and so on). Consider an example:
Program 4.7
using System;
class Test
{
public static void Main(string[] args)
{
Console.WriteLine("C format: {0:C}", 99989.987);
Console.WriteLine("D9 format: {0:D9}", 99999);
Console.WriteLine("E format: {0:E}", 99999.76543);
Console.WriteLine("F3 format: {0:F3}", 99999.9999);
Console.WriteLine("N format: {0:N}", 99999);
Console.WriteLine("X format: {0:X}", 99999);
Console.WriteLine("x format: {0:x}", 99999);
}
}
The output would be –
C format: $99,989.99
D9 format: 000099999
E format: 9.999977E+004
F3 format: 100000.000
N format: 99,999.00
X format: 1869F
x format: 1869f
Note that the use of .NET formatting characters is not limited to console applications.
These same flags can be used within the context of the static String.Format() method.
This can be helpful when we need to create a string containing numerical values in
memory for use in any application type (Windows Forms, ASP.NET, XML web services,
and so on):
Program 4.8
using System;
class Test
{
static void Main(string[] args)
{
string Str;
Str = String.Format("You have {0:C} in your account", 99989.987);
Console.WriteLine(Str);
}
}
The output would be –
You have $99,989.99 in your account
4.9 Defining Program Constants
C# provides a const keyword to defined variables with a fixed, unalterable value. The
value of a constant data is computed at compile time and hence, a constant variable
can not be assigned to an object reference (whose value is computed at runtime). That
is, boxing (will discuss this in next module) is not possible for constant variables. We
can define either class-level constants or local-level (within a method) constants. For
example,
Program 4.9
using System;
abstract class ConstClass
{
public const int p=10;
public const string s="I am a constant";
}
class Test
{
public const int x=5;
public static void Main()
{
const int y=20;
Console.WriteLine("{0},{1},{2},{3}", ConstClass.p, ConstClass.s, x, y);
}
}
The output would be –
10, I am a constant, 5, 20
4.10 C# Iteration Constructs
Similar to any other programming language, C# provides following iteration
constructs:

for Loop

foreach/in Loop

while Loop

do/while Loop
The for Loop
The for loop in C# is just similar to that in C, C++ or Java. This loop will allow to
repeat a set of statements for fixed number of times. For example –
for(int i=0; i<10;i++)
Console.WriteLine(“{0}”, i);
We
can
use
any
type
of
complex
//0 to 9 will be printed.
terminating
conditions,
incrementation/
decrementation, continue, break, goto etc. while using for loop.
The foreach/in Loop
This loop is used to iterate over all items within an array. The following example shows
how each element of an integer array is considered within a loop.
Program 4.10
using System;
class Test
{
public static void Main()
{
int[] arr=new int[]{5, 32, 10, 45, 63};
foreach(int i in arr)
Console.WriteLine("{0}",i);
}
}
Apart from iterating over simple arrays, foreach loop is used to iterate over system
supplied or user-defined collections. This will be discussed in later chapters.
The while and do/while Loop
When we don’t know the exact number of times a set of statements to be executed, we
will go for while loop. That is, a set of statements will be executed till a condition
remains true. For example,
string s;
while((s=Console.ReadLine())!=null)
Console.WriteLine(“{0}”, s);
Some times, we need a set of statements to be executed at least once, irrespective of
the condition. Then we can go for do/while loop. For example,
string opt;
do
{
Console.Write(“Do you want to continue?(Yes/No):”);
opt=Console.ReadLine();
}while(opt!=”Yes”);
4.11 C# Control Flow Constructs
There are two control flow constructs in C# viz. if/else and switch/case. The if/else
works only on boolean expressions. So we can not use the values 0, 1 etc. within if as
we do in C/C++. Thus, the if statement in C# typically involve the relational operators
and/or conditional operators.
The following two tables lists out the relational and logical operators respectively.
Relational Operator
==
!=
<
>
<=
>=
Meaning
To check equality of two operands
Not equal to
Less than
Greater than
Less than or equal to
Greater than or equal to
Conditional/Logical Operator
&&
||
!
The general form of if can be given as –
if(condition)
{
//true block
}
else
{
//false block
Meaning
Logical AND
Logical OR
Logical NOT
}
The general from of switch can be given as –
switch(variable/expression)
{
case val1:
//statements;
break;
case val2:
//statements;
break;
--------------------------case val2:
//statements;
break;
}
4.12 Complete set of C# Operators
Following is a set of operators provided by C#.
Operator Category
Unary
Multiplicative
Additive
Shift
Relational
Equality
Logical
Conditional
Operators
+, -, !, ~, ++, -*, /, %
+, <<, >>
<, >, <=. >=, is, as
==, !=
& (AND), ^ (XOR), | (OR)
&&, ||, ?: (ternary operator)
Indirection/Address
Assignment
*, ->, &
=, *=, -=, +=, /=, %=, <<=, >>=, &=, ^=, |=
The relational operator is is used to verify at runtime whether an object is compatible
with a given type or not. The as operator is used to downcast between types. We will
discuss this later in detail. As C# supports inter-language interaction, it supports the
C++ pointer manipulation operators like *, -> and &. But if we use any of these
operators, we are going bypass the runtime memory management scheme and writing
code in unsafe mode.
4.13 Summary
This unit has exposed you to the numerous core aspects of the C# programming
Language. The focus was to examine the constructs that will be commonplace in any
application you may be interested in building. First, every C# program must have a
class defining a static Main() method, which serves as the entry point of the program.
Within the scope of Main(), you typically create any number of objects which work
together to breathe life into your application. Then you have explored basic
requirements like I/O operations, variable declaration and scope, control flow etc. of
the programming language.
4.14 Keywords











Command Line arguments
The keyword new
Reference to an object
Constructor: Default and Parameterized
Separation of concerns
Application object
System.Console class
Formatting outputs
The Keyword const
The loops for, foreach/in, while, do/while
The conditional constructs if/else, switch/case

The Operators is, as
4.15 Exercises
1. Explain variations in Main() function.
2. What are basic input and output functions with Console class? Explain.
3. Write a C# program to illustrate command line arguments.
4. Write a note on formatting textual input.
5. List out string formatting flags? Explain with example.
6. Write a C# program to illustrate foreach/in Loop.
7. Write a C# application which defines a class Shape, with four data members
length, breadth, height and radius, appropriate constructors and methods to
calculate volume of cube, cone and sphere. Also write a class ShapeApp, which
creates these 3 objects i.e. cube, cone and sphere using appropriate constructors
and calculates their volume with the help of above class methods.
Module 2
Unit 1
CONTENTS:
1.19
1.20
1.21
1.22
1.23
1.24
Objectives
Introduction
Understanding Value Types and Reference Types
The Master Node: System.Object
The System Data Types (and C# Aliases)
Converting between value types reference types: Boxing and
Unboxing
1.25
1.26
1.27
Summary
Keywords
Exercises
1.16 Objectives
At the end of this lesson, the students will understand:

Two types in .NET viz. value types and reference types

The topmost base class System.Object

Various methods of System.Object class, overriding these methods

Various data types available in C#

Boxing and Unboxing
1.17 Introduction
We have seen from the first module that .NET provides common language runtime for
all the programming languages supported on its platform. As a part of CLR, any type
in .NET or more specifically C#, can be either of value type or of reference type. These
two types have their own way of usage and properties. Moreover, in C#, everything
that we use in a program is derived from a topmost base class called System.Object. In
this unit, we are going discuss all these factors in detail.
1.18 Understanding Value Types and Reference Types
Like any programming language, C# defines a number of keywords that represent
basic data types such as whole numbers, character data, floating-point numbers, and
Boolean values. These intrinsic types are fixed constants. That is, all .NET-aware
languages understand the fixed nature of these intrinsic types, and all agree on the
range it is capable of handling.
A .NET data type may be value-based or reference-based. Value-based types, which
include all numerical data types (int, float, etc.) as well as enumerations and
structures, are allocated on the stack. So, value types can be quickly removed from
memory once they are out of the defining scope:
public void SomeMethod()
{
int i = 0;
Console.WriteLine(i);
}
// 'i' is removed from the stack here
When we assign one value type to another, a member-by-member (or a bit-wise) copy
is achieved by default. In terms of numerical or Boolean data types, the only “member”
to copy is the value of the variable itself:
public void SomeMethod()
{
int i = 99;
int j = i;
j = 8732;
// j will be 8732 and i remains to be 99
}
This example may not seem special. But, now we will have a look at other value types
like structures and enumerations. Structures provide a way to achieve the benefits of
object orientation (i.e., encapsulation) while having the efficiency of stack-allocated
data. Like a class, structures can take constructors (only parameterized constructors)
and define any number of members. All structures are implicitly derived from a class
named System.ValueType. The purpose of System.ValueType is to “override” the
virtual methods defined by System.Object to support value-based v/s reference-based
semantics. In fact, the instance methods defined by System.ValueType are identical to
those of System.Object.
Consider an example to illustrate the working of value types –
Program 1.1
File Name: MyClass.cs
struct MyStruct
{
public int x;
}
class MyClass
{
public static void Main()
{
// the keyword new is optional while creating structures.
MyStruct ms1 =new MyStruct();
ms1.x = 100;
MyStruct ms2 = ms1;
Console.WriteLine("ms1.x = {0}", ms1.x); //100
Console.WriteLine("ms2.x = {0}", ms2.x); //100
ms2.x = 900;
Console.WriteLine("ms1.x = {0}", ms1.x); //100
Console.WriteLine("ms2.x = {0}", ms2.x); //900
}
}
The Output will be –
ms1.x = 100
ms2.x = 100
ms1.x = 100
ms2.x = 900
Note that, to allocate a structure type, we can use the ‘new’ keyword. This will create
an impression that memory is allocated in heap. But, this is not true. CLR makes the
programmer to feel everything is an object and new value types. However, when the
runtime encounters a type derived from System.ValueType, stack allocation is
achieved.
In the above program, we have created a variable ms1 and then it is assigned to ms2.
Since MyStruct is a value type, we will have two copies of the MyStruct type on the
stack, each of which can be independently manipulated. Therefore, when we change
the value of ms2.x, the value of ms1.x is unaffected.
On the other hand, the reference types (classes) are allocated on the managed heap.
These objects stay in memory until the .NET garbage collector destroys them. By
default, assignment of reference types results in a new reference to the same object on
the heap. To illustrate, let us change the above example:
Program 1.2
File Name: MyClass1.cs
class MyClass
//struct has been changed to class
{
public int x;
}
class MyClass1
{
public static void Main()
{
MyClass mc1 =new MyClass (); // new is compulsory
mc1.x = 100;
MyClass mc2 = mc1;
Console.WriteLine("mc1.x = {0}", mc1.x); //100
Console.WriteLine("mc2.x = {0}", mc2.x); //100
ms2.x = 900;
Console.WriteLine("mc1.x = {0}", mc1.x); //900
Console.WriteLine("mc2.x = {0}", mc2.x); //900
}
}
The Output will be –
mc1.x = 100
mc2.x = 100
mc1.x = 900
mc2.x = 900
In this example, we have two references pointing to the same object on the managed
heap. Therefore, when we change the value of x using the mc2 reference, mc1.x
reports the same value.
Value Types Containing Reference Types
Assume that we have a reference (class) type MyClass containing a constructor to
assign value to a member variable of type string. Assume also that we have included
object of this class within a structure MyStruct. We have included the Main() function
in another class and the total application is as given below –
Program 1.3
Name of file: Test.cs
using System;
class MyClass
{
public string s;
public MyClass(string str) //constructor for class
{
s=str;
}
}
struct MyStruct
{
public MyClass mc;
public int a;
public MyStruct(string str) //constructor for structure
{
mc=new MyClass(str);
//calling constructor of class
a=10;
}
}
class Test
{
public static void Main(string[] args)
{
MyStruct ms1=new MyStruct("Initial Value");
ms1.a=15;
MyStruct ms2=ms1;
ms2.mc.s="New Value!!!";
ms2.a=20;
Console.WriteLine("ms1.mc.s is {0}", ms1.mc.s);
Console.WriteLine("ms2.mc.s is {0}", ms2.mc.s);
Console.WriteLine("ms1.a is {0}", ms1.a);
Console.WriteLine("ms2.a is {0}", ms2.a);
}
}
The Output would be –
ms1.mc.s is New Value!!!
ms2.mc.s is New Value!!!
ms1.a is 15
ms2.a is 20
Now compare the programs 1.1, 1.2 and 1.3.

In Program 1.1, we have used the structure, which is of value type. So, when
we use the statement
MyStruct ms2= ms1;
there will be separate memory allocation for two objects (Memory will be
allocated from stack). Thus, the assignment statement ms2=ms1 will copy the
contents of ms1 to the corresponding variables of ms2. Since memory allocation
is different for these two objects, the change in one object doesn’t affect the
other object.

In Program 1.2, we have used the class, which is of reference type. So, when
we use the statement
MyClass mc2 = mc1,
mc2 will be just reference to mc1 sharing the same memory location in the
heap. Also, the contents of mc1 will be copied to the corresponding variables of
mc2. Since memory location is same for both the objects, the change in one
object will affect the other object.

In Program 1.3, we have a value type MyStruct containing an object of reference
type MyClass. The object ms1 of MyStruct contains an object mc of MyClass and
an integer variable a. Here, a will be allocated memory from stack and mc will
get memory from heap. Now, when we use the statement
MyStruct ms2=ms1;
the object ms2 gets created. Note that, ms1.a and ms2.a will have different
memory allocation as a is member of structure. But, ms1.mc.s and ms2.mc.s
will point to same location in heap memory as s is basically a member of class.
This can be diagrammatically represented as–
ms1
a
ms2
s
a
Thus, when a value type contains other reference types, assignment results in a copy
of the references. In this way, we have two independent structures ms1 and ms2 each
of which contains a reference pointing to the same object in memory (i.e., a “shallow
copy”). When we want to perform a “deep copy,” where the state of internal references
is fully copied into a new object, we need to implement the ICloneable interface, which
we will see later in detail.
Value and Reference Types: Final Details
Following table summarizes the core distinctions between value types and reference
types.
Intriguing Question
Value Type
Reference Type
Where is this type
allocated?
Allocated on the stack
Allocated on the managed
heap.
How is a variable
represented?
Value type variables are
local copies.
What is the base type?
Must derive from
System.ValueType.
Can this type function as a
base to other types?
No. Value types are always
sealed and cannot be
extended.
Variables are passed by
value (i.e., a copy of the
variable is passed into the
called function).
No. Value types are never
placed onto the heap and
therefore do not need to be
finalized.
Yes, but the default
constructor is reserved
When they fall out of the
defining scope.
Reference type variables are
pointing to the memory
occupied by the allocated
instance.
Can derive from any other
type (except
System.ValueType), as long as
that type is not “sealed”
Yes. If the type is not sealed, it
may function as a base to
other types.
Variables are passed by
reference (e.g., the address of
the variable is passed into the
called function).
Yes, indirectly
What is the default
parameter passing
behavior?
Can this type override
System.Object.Finalize()?
Can we define
constructors?
When do variables of this
type die?
Yes
When the managed heap is
garbage collected.
Despite their differences, value types and reference types both have the ability to
implement interfaces and may support any number of fields, methods, overloaded
operators, constants, properties, and events.
1.19 The Master Node: System.Object
In .NET, every data type is derived from a common base class: System.Object. The
Object class defines a common set of members supported by every type in the .NET
framework. When we create a class, it is implicitly derived from System.Object. For
example, the following declaration is common way to use.
class Test
{
...
}
But, internally, it means that,
class Test : System.Object
{
...
}
System.Object defines a set of instance-level (available only after creating objects) and
class-level (static) members. Note that some of the instance-level members are
declared using the virtual keyword and can therefore be overridden by a derived class:
// The structure of System.Object class
namespace System
{
public class Object
{
public Object();
public virtual Boolean Equals(Object obj);
public virtual Int32 GetHashCode();
public Type GetType();
public virtual String ToString();
protected virtual void Finalize();
protected Object MemberwiseClone();
public static bool Equals(object objA, object objB);
public static bool ReferenceEquals(object objA, object objB);
}
}
Following table shows some of the functionality provided by each instance-level
method.
Instance Method
of Object Class
Equals()
Meaning
By default, this method returns true only if the items being
compared refer to the exact same item in memory. Thus,
Equals() is used to compare object references, not the state of
the object. Typically, this method is overridden to return true
only if the objects being compared have the same internal state
values. Note that if you override Equals(), you should also
override GetHashCode().
GetHashCode()
GetType()
This method returns an integer that identifies a specific object
in memory. If you intend your custom types to be contained in a
System.Collections.Hashtable type, you are well-advised to
override the default implementation of this member.
This method returns a System.Type object that fully describes
the details of the current item. In short, this is a Runtime Type
Identification (RTTI) method available to all objects.
ToString()
This method returns a string representation of a given object,
using the namespace.typename format (i.e., fully qualified
name). If the type has not been defined within a namespace,
typename alone is returned. This method can be overridden by
a subclass to return a tokenized string of name/value pairs that
represent the object’s internal state, rather than its fully
qualified name.
Finalize()
This protected method (when overridden) is invoked by the .NET
runtime when an object is to be removed from the heap (during
garbage collection).
MemberwiseClone()
This protected method exists to return a new object that is a
member-by-member copy of the current object. Thus, if the
object contains references to other objects, the references to
these types are copied (i.e., it achieves a shallow copy). If the
object contains value types, full copies of the values are
achieved. (Example, Program 1.3)
The Default Behavior of System.Object
To illustrate some of the default behavior provided by the System.Object base class,
consider the following example –
Program 1.4
using System;
class Person
{
public string Name, SSN;
public byte age;
public Person(string n, string s, byte a)
{
Name = n;
SSN = s;
age = a;
}
public Person(){ }
static void Main(string[] args)
{
Console.WriteLine("***** Working with Object *****\n");
Person p1 = new Person("Ram", "111-11-1111", 20);
Console.WriteLine("p1.ToString: {0}", p1.ToString());
Console.WriteLine("p1.GetHashCode: {0}", p1.GetHashCode());
Console.WriteLine("p1’s base class: {0}", p1.GetType().BaseType);
Person p2 = p1;
object o = p2;
if(o.Equals(p1) && p2.Equals(o))
Console.WriteLine("p1, p2 and o are same objects!");
}
}
The Output would be –
***** Working with Object *****
p1.ToString: Person
p1.GetHashCode: 58225482
p1's base class: System.Object
p1, p2 and o are same objects!
We can notice from the above program that the default implementation of ToString()
simply returns the fully qualified name of the type. GetType() retrieves a System.Type
object, which defines a property named . Here, new Person object p1 is referencing
memory in the heap. p2 is also of type Person, however, we are not creating a new
instance of the Person class, but assigning p2 to p1. Therefore, p1 and p2 are both
pointing to the same object in memory. Similarly the variable o (of type object) also
refers to the same memory. Thus, when we compare p1, p2 and o, it says that all are
same.
Overriding Some Default Behaviors of System.Object
In many of our applications, we may want to override some of the behaviors of
System.Object. Overriding is the process of redefining the behavior of an inherited
virtual member in a derived class. We have seen that System.Object class has some
virtual methods like ToString(), Equals() etc. These can be overridden by the
programmer.
Overriding ToString()
Consider the following example to override System.Object.ToString().
Program 1.5
using System;
using System.Text;
class Person
{
public string Name, SSN;
public byte age;
public Person(string n, string s, byte a)
{
Name = n;
SSN = s;
age = a;
}
public Person(){ }
// Overriding System.Object.ToString()
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("[Name={0}", this. Name);
sb.AppendFormat(" SSN={0}", this.SSN);
sb.AppendFormat(" Age={0}]", this.age);
return sb.ToString();
}
public static void Main()
{
Person p1 = new Person(“Ram”, “11-12”, 25);
Console.WriteLine(“p1 is {0}”, p1.ToString());
}
}
The Output would be –
p1 is [Name=Ram SSN=11-12 Age=25]
In the above example, we have overridden ToString() method to display the contents of
the object in the form of tuple. The System.Text.StringBuilder is class which allows
access to the buffer of character data and it is a more efficient alternative to C# string
concatenation (Discussed later in detail).
Overriding Equals()
By default, System.Object.Equals() returns true only if the two references being
compared are referencing same object in memory. But in many situations, we are
more interested if the two objects have the same content. Consider an example –
Program 1.6
using System;
class Person
{
public string Name, SSN;
public byte age;
public Person(string n, string s, byte a)
{
Name = n;
SSN = s;
age = a;
}
public Person(){ }
public override bool Equals(object ob)
{
if (ob != null && ob is Person)
{
Person p = (Person)ob;
if (p.Name == this.Name && p.SSN == this.SSN && p.age == this.age)
return true;
}
return false;
}
public static void Main()
{
Person p1 = new Person("Ram", "11-12", 25);
Person p2 = new Person("Shyam", "11-10", 20);
Person p3 = new Person("Ram", "11-12", 25);
Person p4 = p2;
if(p1.Equals(p2))
Console.WriteLine("p1 and p2 are same");
else
Console.WriteLine("p1 and p2 are not same");
if(p1.Equals(p3))
Console.WriteLine("p1 and p3 are same");
else
Console.WriteLine("p1 and p3 are not same");
if(p2.Equals(p4))//compares based on content, not on reference
Console.WriteLine("p4 and p2 are same");
else
Console.WriteLine("p4 and p2 are not same");
}
}
The Output would be –
p1 and p2 are not same
p1 and p3 are same
p4 and p2 are same
While overriding the Equals() method, first we are checking whether the passed object
is of class Person or not. Also, we need to check whether the object has been allocated
memory or it is having null. Note that Equals() method takes the parameter of type
object. Thus, we need to type-cast it to Person type before using it. When we override
Equals(), we need to override GetHashCode() too.
Overriding System.Object.GetHashCode()

The GetHashCode() method is suitable for use in hashing algorithms and
data structures such as a hash table.

The GetHashCode() method returns a numerical value used to identify an
object in the memory.

The default implementation of the GetHashCode() method does not
guarantee unique return values for different objects.

Furthermore, the .NET Framework does not guarantee the default
implementation of the GetHashCode() method, and the value it returns
will be the same between different versions of the .NET Framework.

So, the default implementation of this method must not be used as a
unique object identifier for hashing purposes.

If
we
place
a
custom
(user-defined)
object
into
a
System.Collections.Hashtable type, its Equals() and GetHashCode()
members will determine the correct type to return from the container.

So, user-defined types should redefine the hashing algorithm used to
identify itself within such a type.
There are many algorithms that can be used to create a hash code. In the
simplest case, an object’s hash value will be generated by taking its data into
consideration and building a unique numerical identifier for the type. For
example, SSN will be unique for each individual. So, we can override
GetHashCode() method as below –
Program 1.7
using System;
class Person
{
public string Name, SSN;
public byte age;
public Person(string n, string s, byte a)
{
Name = n;
SSN = s;
age = a;
}
public Person(){ }
public override int GetHashCode()
{
return SSN.GetHashCode();
}
public static void Main()
{
Person p1 = new Person("Ram", "11-12", 25);
Person p2 = new Person("Shyam", "11-10", 20);
Person p3 = new Person("Ram", "11-12", 25);
Person p4 = p2;
if(p1.Equals(p3))
//comparison based on reference
Console.WriteLine("p1 and p3 are same");
else
Console.WriteLine("p1 and p3 are not same");
if(p1.GetHashCode()==p3.GetHashCode()) //comparison based on SSN
Console.WriteLine("p1 and p3 are same");
else
Console.WriteLine("p1 and p3 are not same");
}
}
The output would be –
p1 and p3 are not same
p1 and p3 are same
1.20 The System Data Types (and C# Aliases)
Every intrinsic C# data type is an alias to an existing type defined in the System
namespace. Specifically, each C# data type aliases a well-defined structure type in the
System namespace. Following table lists each system data type, its range, the
corresponding C# alias and the type’s compliance with the CLS.
C#
Alias
sbyte
byte
short
ushort
int
uint
long
ulong
char
CLS
Compliant?
No
Yes
Yes
No
Yes
No
Yes
No
Yes
System Type
System.SByte
System.Byte
System.Int16
System.UInt16
System.Int32
System.UInt32
System.Int64
System.UInt64
System.Char
float
Yes
System.Single
double
Yes
System.Double
bool
decimal
string
Yes
Yes
Yes
System.Boolean
System.Decimal
System.String
object
Yes
System.Object
Range
Meaning
-128 to 127
0 to 255
-216 to 216-1
0 to 232-1
-232 to 232-1
0 to 264-1
-264 to 264-1
0 to 2128-1
U10000
to
U1ffff
1.5 x 10-45 to
3.4 x 1038
5.0 x 10-324
to
1.7 x 10308
True or False
1 to 1028
Limited
by
system
memory
Anything(all
types
excluding
interfaces)
derive from
object
Signed 8-bit number
Unsigned 8-bit number
Signed 16-bit number
Unsigned 16-bit number
Signed 32-bit number
Unsigned 32-bit number
Signed 64-bit number
Unsigned 64-bit number
A Single 16-bit Unicode
character
32-bit
floating
point
number
64-bit
floating
point
number
Represents truth or falsity
96-bit signed number
Represents
a
set
of
Unicode characters
The base class of all types
in the .NET universe.
The relationship between the core system types is shown in the figure 1.1. From this
diagram, we can see that all the types are ultimately derived from System.Object.
Since the data types like int are simply shorthand notations for the corresponding
system type (like System.Int32), the following statements are valid –
Console.WriteLine(25.GetHashCode());
Console.WriteLine(32.GetType().BaseType()); etc.
We can see that, though C# defines a number of data types, only a subset of the whole
set of data types are compliant with the rules of CLS. So, while building user-defined
types (like class, structures), we should use only CLS-compliant types. And also, we
should avoid using unsigned types as public member of user-defined type. By doing
this, our user-defined type (class, enumeration, structure etc) can be understood by
any language in .NET framework.
Experimenting with the System Data Types
From the Figure 3.1, it is clear that C# data types are alias names for a related
structure in the System namespace, and hence derived from System.ValueType. The
purpose of System.ValueType is to override the virtual methods defined by
System.Object to work with value-based v/s reference-based semantics. Since data
types are value types, the comparison of two variables will be based on their internal
value, but not the reference:
System.Int32 a=20;
int b=20;
if(a==b)
//comparison based on value
Console.WriteLine(“Same!!”);
Object
Boolean
UInt16
Type
Byte
UInt32
String
Char
UInt64
Array
Decimal
Void
Exception
Double
DateTime
Delegate
Int16
Fig. 1.1 The hierarchy of System Types
Basic Numerical Members
To understand the numerical types in C#, consider the following example –
Program 1.8
using System;
class Test
{
public static void Main()
{
System.UInt16 a=30000;
Console.WriteLine("Max value for UInt16: {0}", UInt16.MaxValue);
//65535
Console.WriteLine("Min value for UInt16: {0}", UInt16.MinValue);
//0
Console.WriteLine("value of UInt16: {0}", a);
//30000
Console.WriteLine("The type is: {0}", a.GetType().ToString());
//System.UInt16
ushort b=12000;
Console.WriteLine("Max value for ushort: {0}", ushort.MaxValue); //65535
Console.WriteLine("Min value for ushort: {0}", ushort.MinValue); //0
Console.WriteLine("value of ushort: {0}", b);
//12000
Console.WriteLine("The type is: {0}", b.GetType().ToString());
//System.UInt16
Console.WriteLine("double.Epsilon: {0}", double.Epsilon);
//4.94065645841247E-324
Console.WriteLine("double.PositiveInfinity: {0}", double.PositiveInfinity);
//Infinity
Console.WriteLine("double.NegativeInfinity: {0}", double.NegativeInfinity);
//-Infinity
Console.WriteLine("double.MaxValue: {0}", double.MaxValue);
// 1.79769313486232E+308
Console.WriteLine("double.MinValue: {0}", double.MinValue);
//-1.79769313486232E+308
}
}
Members of System.Boolean
In C#, for Boolean data, we have to assign any one value true or false. We can not use
the numbers like -1, 0, 1 etc. as we do in C++. The usage of bool data is shown in
Program 1.9.
Members of System.Char
All the .NET-aware languages map textual data into the same underlying types viz
System.String and System.Char, both are Unicode. The System.Char type provides
several methods as shown in the following example –
Program 1.9
using System;
class Test
{
public static void Main()
{
bool b1=true;
bool b2=false;
Console.WriteLine("{0}", bool.FalseString);
//False
Console.WriteLine("{0}", bool.TrueString);
Console.WriteLine("{0}, {1}", b1, b2);
//True
//True, False
Console.WriteLine("{0}", char.IsDigit('P'));
//False
Console.WriteLine("{0}", char.IsDigit('9'));
//True
Console.WriteLine("{0}", char.IsLetter("10", 1));
//False
Console.WriteLine("{0}", char.IsLetter("1a", 1));
//True
Console.WriteLine("{0}", char.IsLetter('p'));
//True
Console.WriteLine("{0}", char.IsWhiteSpace("Hello World", 5)); //True
Console.WriteLine("{0}", char.IsWhiteSpace("Hello World", 6)); //False
Console.WriteLine("{0}", char.IsLetterOrDigit('?'));
//False
Console.WriteLine("{0}", char.IsPunctuation('!'));
//True
Console.WriteLine("{0}", char.IsPunctuation('<'));
//False
Console.WriteLine("{0}", char.IsPunctuation(','));
//True
}
}
Parsing Values from String Data
.NET data types provide the ability to generate a variable of their underlying type given
a textual equivalent. This technique is called as parsing. This is helpful when we need
to convert user input data into a numerical value. Consider the following program –
Program 1.10
using System;
class Test
{
public static void Main()
{
bool b=bool.Parse("True");
Console.WriteLine("Value of bool is:{0}", b);
//True
double d=double.Parse("99.457");
Console.WriteLine("Value of double is:{0}", d);
//99.457
int i=int.Parse("8");
Console.WriteLine("Value of int is:{0}", i);
//8
char c=char.Parse("w");
Console.WriteLine("Value of char is:{0}", c);
//w
}
}
1.21 Boxing and Un-boxing
We know that .NET defines two broad categories of types viz. value types and reference
types. Sometimes, we may need to convert variables of one category to the variables of
other category. For doing so, .NET provides a mechanism called boxing.

Boxing can be defined as the process of explicitly converting a value type into a
reference type.

When we box a variable, a new object is allocated in the heap and the value of
variable is copied into the object.
For example,
int p=20;
object ob=p;
//box the value type p into an object reference

The operation just opposite to boxing is called as unboxing.

Unboxing is the process of converting the value held in the object reference
back into a corresponding value type.

When we try to unbox an object, the compiler first checks whether is the
receiving data type is equivalent to the boxed type or not.

If yes, the value stored in the object is copied into a variable in the stack.

If we try to unbox an object to a data type other than the original type, an
exception called InvalidCastException is generated.
For example,
int p=20;
object ob=p;
-------------int b=(int)ob;
// unboxing successful
string s=(string)ob; // InvalidCastException
Generally, there will few situations in which we need boxing and/or unboxing. In most
of the situations, C# compiler will automatically boxes the variables. For example, if
we pass a value type data to a function having reference type object as a parameter,
then automatic boxing takes place. Consider the following program –
Program 1.11
using System;
class Test
{
public static void MyFunc(object ob)
{
Console.WriteLine(ob.GetType());
Console.WriteLine(ob.ToString());
Console.WriteLine(((int)ob).GetTypeCode());
}
public static void Main()
{
int x=20;
MyFunc(x);
}
}
The output would be –
System.Int32
20
Int32
NOTE:
1. Boxing and unboxing takes some processing time. So, it must be used only
when needed.
2. When we pass custom (user defined) structures/enumerations into a method
taking generic System.Obejct parameter, we need to unbox the parameter to
interact with the specific members of the structure/enumeration. We will study
this later in-detail.
1.22 Summary
This section revealed the value types and reference types in .NET framework. We have
seen how value types and reference types behave in various situations. We have also
discussed the basic methods of System.Object class and how to override them
according to our need in the program. The boxing and un-boxing techniques allow the
programmer to switch between value types and reference types in the program.
1.23 Keywords

Value Type and Reference Type

Stack area and Heap area

Shallow Copy and Deep Copy

Members of System.Boolean, System.Char and numeric members

Parsing values from Sting type

Boxing and unboxing
1.24 Exercises
1. Write a program to illustrate the difference between passing reference types by reference
and by value.
2. Bring out the difference between value types and reference types.
3. How do you override ToString() method of System.Object class? Explain with a program.
4. Explain the concept of GetHashCode() method.
5. Draw a diagram to depict the hierarchy of System types and explain.
6. What is boxing and unboxing? Explain with an example for each.
7. What is the output printed by the following code segment and why?
struct Point
{
public int x, y;
public Point(int x, int y)
{
this.x=x ;
this.y=y ;
}
}
Point p=new Point(10,10) ;
object obj=p ;
p.x=20;
Console.WriteLine(“p.x={0}”, p.x);
Console.WriteLine("obj.x={0}", ((Point)obj).x) ;
Module 2
Unit 2
CONTENTS:
2.13
2.14
2.15
2.16
2.17
2.18
2.19
2.20
2.21
2.22
2.23
2.24
2.25
Objectives
Introduction
Defining Custom Class Methods
Understanding Static Methods
Methods Parameter Modifiers
Array Manipulation in C#
String Manipulation in C#
C# Enumerations
Defining Structures in C#
Defining Custom Namespaces
Summary
Keywords
Exercises
2.1 Objectives
At the end of this lesson, students will understand the following tasks in detail:

Defining User-defined/Custom methods for a class

Static Members (Data and Method) of a class

The parameter modifiers like out, params, ref

Members of Array and String class

Manipulation of Arrays and Strings

C# enumeration and structures

Concept of user-defined/custom namespaces
2.13 Introduction
In all the previous units, we have discussed basic concepts of C# programming
language. Here, we will continue with understanding how to define own methods for a
class, the concept of static members of a class, how to define our own namespace
based on the requirements and so on. Also, we will elaborate the value types of C# viz.
structure and enumeration types.
2.14 Defining Custom Class Methods
In C#, every data and a method must be a member of a class or structure. That is, we
can not have global data or method. The methods in C# may or may not take
parameters and they may or may not return a value. Also, custom methods (user
defined methods) may be declared non-static (instance level) or static (class level).
Method Access Modifiers
Every method in C# specifies its level of accessibility using following access modifiers:
Access Modifiers
public
private
protected
internal
Meaning
Method is accessible from an object or any
subclass.
Method is accessible only by the class in which it is
defined. private is a default modifier in C#.
Method is accessible by the defining class and all
its sub-classes.
Method is publicly accessible by all types in an
protected internal
assembly, but not outside the assembly.
Method’s access is limited to the current assembly
or types derived from the defining class in the
current assembly.
2.15 Understanding Static Methods
A method can be declared as static. When a method is static, it can be invoked directly
from the class level, without creating an object. This is the reason for making Main()
function to be static. The another example is WriteLine() method. We will directly use
the statement Console.WriteLine() without creating an object of Console class. Consider
the following program:
Program 2.1
using System;
class Test
{
public static void disp()
{
Console.WriteLine(“hello”);
}
public static void Main()
{
Test.disp();
}
}
//calling method using class name itself
Defining static Data
Normally, we will define a set of data members for a class. Then every object of that
class will have separate copy of each of those data members. For example,
class Test
{
public int p;
}
-----------Test t1=new Test();
t1.p=10;
Test t2= new Test();
t2.p=15;
Here, the objects t1 and t2 will be having separate copy of the variable p.
On the other hand, static data is shared among all the objects of that class. That is, for
all the objects of a class, there will be only one copy of static data. For example,
Program 2.2
using System;
class Test
{
public static int p=0;
public int incr()
{
return ++p;
}
public static void Main()
{
Test t1=new Test();
Test t2=new Test();
Console.WriteLine("p= {0}", t1.incr());
//1
Console.WriteLine("p= {0}", t2.incr());
//2
Console.WriteLine("p= {0}", t1.incr());
//3
}
}
2.16 Method Parameter Modifiers
Normally methods will take parameter. While calling a method, parameters can be
passed in different ways. C# provides some parameter modifiers as shown –
Parameter Modifier
(none)
out
Meaning
If a parameter is not attached with any modifier,
then parameter’s value is passed to the method.
This is the default way of passing parameter. (callby-value)
The output parameters are assigned by the called
method.
Reference to a parameter is passed to a method.
(call-by-reference)
This modifier will allow to send many number of
parameters as a single parameter. Any method can
have only one params modifier and it should be the
last parameter for the method.
ref
params
The Default Parameter Passing Behavior
By default, the parameters are passed to a method by value. So, the changes made for
parameters within a method will not affect the actual parameters of the calling
method. Consider the following example –
Program 2.3
using System;
class Test
{
public static void swap(int x, int y)
{
int temp=x;
x=y;
y=temp;
}
public static void Main()
{
int x=5,y=20;
Console.WriteLine("Before: x={0}, y={1}", x, y);
swap(x,y);
Console.WriteLine("After: x={0}, y={1}", x, y);
}
}
The output would be –
Before: x=5, y=20
After : x=5, y=20
The out Keyword
In some of the methods, we need to return a value to a calling method. Instead of
using return statement, C# provides a modifier for a parameter as out. The usage of out
can be better understood by the following example –
Program 2.4
using System;
class Test
{
public static void add(int x, int y, out int z)
{
z=x+y;
}
public static void Main()
{
int x=5,y=20, z;
add(x, y, out z);
Console.WriteLine("z={0}", z);
//z=25
}
}
The out parameter is certainly useful when we need more values to be returned from a
method. Consider the following example –
Program 2.5
using System;
class Test
{
public static void MyFun(out int x, out string s)
{
x=5;
s="Hello, how are you?";
}
public static void Main()
{
int a;
string str;
MyFun(out a, out str);
Console.WriteLine("a={0}, str={1}", a, str);
}
}
The output would be –
a=5,
str=”Hello, how are you?
The C# ref Keyword
Whenever we want the changes made in method to get affected in the calling method,
then we will go for call by ref. Following are the differences between output and
reference parameters:

The output parameters do not need to be initialized before sending to called
method. Because it is assumed that the called method will fill the value for
such parameter.

The reference parameters must be initialized before sending to called method.
Because, we are passing a reference to an existing type and if we don’t assign
an initial value, it would be equivalent to working on NULL pointer.
Program 2.6
using System;
class Test
{
public static void Main()
{
string s="hello";
Console.WriteLine("Before:{0}",s);
MyFun(ref s);
Console.WriteLine("After:{0}",s);
}
public static void MyFun(ref string s)
{
s=s.ToUpper();
}
}
The output would be –
Before: hello
After: HELLO
The C# params Keyword
The params keyword of C# allows us to send many numbers of arguments as a single
parameter. To illustrate the use of params, consider the following example –
Program 2.7
using System;
class Test
{
public static void MyFun(params int[] arr)
{
for(int i=0; i<arr.Length; i++)
Console.WriteLine(arr[i]);
}
public static void Main()
{
int[] a=new int[3]{5, 10, 15};
int p=25, q=102;
MyFun(a);
MyFun(p, q);
}
}
The output would be –
5
10
15
25
102
From the above example, we can observe that for params parameter, we can pass an
array or individual elements.
We can use params even when the parameters to be passed are of different types, as
shown in the following program –
Program 2.8
using System;
class Test
{
public static void MyFun(params object[] arr)
{
for(int i=0; i<arr.Length; i++)
{
if(arr[i] is Int32)
Console.WriteLine("{0} is an integer", arr[i]);
else if(arr[i] is string)
Console.WriteLine("{0} is a string", arr[i]);
else if(arr[i] is bool)
Console.WriteLine("{0} is a boolean", arr[i]);
}
}
public static void Main()
{
int x=5;
string s="hello";
bool b=true;
MyFun(b, x, s);
}
}
The output would be –
True is a Boolean
5 is an integer
hello is a string
Passing Reference Types by Value and Reference
Till now, we have seen how to pass a parameter to methods by value and by using ref.
In the previous examples, we have passed value type variables as parameters to
methods (Just recollect that there are two types of variables value type like int, char,
string, structure etc. and reference type like class, delegates). Now we will see what
happens when reference type variables (i.e. objects of class) are passed as parameters.
Consider the program –
Program 2.9
using System;
class Person
{
string name;
int age;
public Person(string n, int a)
{
name=n;
age=a;
}
public static void CallByVal(Person p)
{
p.age=66;
p=new Person("Shyamu", 25);
}
public static void CallByRef(ref Person p)
{
p.age=55;
p=new Person("Shyamu", 20);
}
public void disp()
{
Console.WriteLine("{0}
{1}", name, age);
}
public static void Main()
{
Person p1=new Person("Ramu", 30);
p1.disp();
CallByVal(p1);
p1.disp();
CallByRef(ref p1);
p1.disp();
}
}
The output would be –
Ramu
30
Ramu
66
Shyamu 20
In the Main() function, we have created an object p1 of Person class. Memory will be
allocated to p1 from heap as p1 is of reference type (object of a class). Now, the display
will be –
Ramu
30
Now we are passing p1 to the function using call-by-value method. But, by nature, p1
is of reference type. So, in the receiving end, the object p takes the reference of p1.
That means, the memory location for both p1 and p will be same. Thus, the statement
p.age = 66;
will affect the original object p1. And hence the output will be –
Ramu
66
But, when we try to allocate new memory for p, the compiler will treat p as different
object and new memory is allocated from heap. Now onwards, p and p1 are different.
Next, we are passing p1 using the modifier ref. This is nothing but call-by-reference.
Here also, the receiving object p will be a reference to p1. Since the parameter passing
technique used is call-by-reference, as per the definition, any changes in method
should affect the calling method. Thus, the statement
p= new Person(“Shyamu”, 20);
will affect the original object p1 and hence the output is –
Shyamu 20
NOTE: The important rule to be remembered is: “If a class type (reference type) is
passed by reference, the called method will change the values of the object’s
data and also the object it is referencing”.
2.17 Array Manipulation in C#
C# arrays look like that of C/C++. But, basically, they are derived from the base class
viz. System.Array. Array is a collection of data elements of same type, which are
accessed using numerical index. Normally, in C#, the array index starts with 0. But it
is possible to have an array with arbitrary lower bound using the static method
CreateInstance() of System.Array. Arrays can be single or multi-dimensional. The
declaration of array would look like –
int[ ] a= new int[10];
a[0]= 5;
a[1]= 14;
……….
string[ ] s= new string[2]{“Ramu”, “Shymu”};
int[ ] b={15, 25, 31, 78};
//new is missing. Still valid
In .NET, the members of array are automatically set to their respective default value.
For example, in the statement,
int[ ] a= new int[10];
all the elements of a are set to 0. Similarly, string array elements are set to null and so
on.
Array as Parameters and Return Values
Array can be passed as parameter to a method and also can be returned from a
method. Consider the following example –
Program 2.10
using System;
class Test
{
public static void disp(int[ ] arr) //taking array as parameter
{
for(int i=0;i<arr.Length;i++)
Console.WriteLine("{0} ", arr[i]);
}
public static string[ ] MyFun()
//returning an array
{
string[ ] str={"Hello", "World"};
return str;
}
public static void Main()
{
int[ ] p=new int[ ]{20, 54, 12, -56};
disp(p);
string[ ] strs=MyFun();
foreach(string s in strs)
Console.WriteLine(s);
}
}
The output would be –
20
54
12
-56
Hello World
Working with Multidimensional Arrays
There are two types of multi-dimensional arrays in C# viz. rectangular array and
jagged array. The rectangular array is an array of multiple dimensions and each row
is of same length. For example –
Program 2.11
using System;
class Test
{
public static void Main()
{
int[,] arr=new int[2,3]{{5, 7, 0}, {3, 1, 8}};
int sum=0;
for(int i=0;i<2; i++)
for(int j=0;j<3;j++)
sum=sum+arr[i,j];
Console.WriteLine("Sum is {0}", sum);
}
}
The output would be –
Sum is 24
Jagged array contain some number of inner arrays, each of which may have unique
size. For example –
Program 2.12
using System;
class JaggedArray
{
public static int[][] JArr=new int[3][];
public static void Main()
{
int m, n, p, sum=0;
Console.WriteLine("Enter the sizes for 3 inner arrays:");
m=int.Parse((Console.ReadLine()).ToString());
n=int.Parse((Console.ReadLine()).ToString());
p=int.Parse((Console.ReadLine()).ToString());
JArr[0]=new int[m];
JArr[1]=new int[n];
JArr[2]=new int[p];
Console.WriteLine("Enter the elements for array:");
for(int i=0;i<3;i++)
for(int j=0;j<JArr[i].Length;j++)
{
JArr[i][j]=int.Parse((Console.ReadLine()).ToString());
sum=sum+JArr[i][j];
}
Console.WriteLine("\nThe Sum is: {0}",sum);
}
}
The output would be –
Enter the sizes for 3 inner arrays:
2
3
2
Enter the elements for array:
1
2
3
4
5
6
7
The Sum is: 28
The System.Array Base Class
Every array in C# is derived from the class System.Array. This class defines number of
methods to work with arrays. Few of the methods are given below –
Member
BinarySearch()
Meaning
This static method searches a (previously sorted) array for a given
item. If the array is composed of user-defined data types, the type in
question must implement the IComparer interface to engage in a
binary search.
Clear()
This static method sets a range of elements in the array to empty
values (0 for value types; null for reference types).
CopyTo()
This method is used to copy elements from the source array into the
destination array.
Length
This read-only property is used to determine the number of elements
in an array.
Rank
This property returns the number of dimensions of the current array.
Reverse()
This static method reverses the contents of a one-dimensional array.
Sort()
This method sorts a one-dimensional array of intrinsic types. If the
elements in the array implement the IComparer interface, we can
also sort an array of user-defined data type .
Consider the following example to illustrate some methods and/or properties of
System.Array class –
Program 2.13
using System;
class Test
{
public static void Main()
{
int[] arr=new int[5]{12, 0, 45, 32, 67};
Console.WriteLine("Array elements are :");
for(int i=0;i<arr.Length;i++)
Console.WriteLine("{0}\t", arr[i]);
Array.Reverse(arr);
Console.WriteLine("Reversed Array elements are :");
for(int i=0;i<arr.Length;i++)
Console.WriteLine("{0}\t", arr[i]);
Array.Clear(arr, 1, 3);
Console.WriteLine("Cleared some elements :");
for(int i=0;i<arr.Length;i++)
Console.WriteLine("{0}\t", arr[i]);
}
}
The Output would be –
Array elements are:
12
0
45
32
67
Reversed Array elements are:
67
32
45
0
12
Cleared some elements:
67
0
0
0
12
2.18 String Manipulation in C#
Till now, we have used string key word as a data type. But truly speaking, string is an
alias type for System.String class. This class provides a set of methods to work on
strings. Following is a list of few such methods –
Member
Length
Meaning
This property returns the length of the current string.
Contains()
This method is used to determine if the current string object contains a
specified string.
Concat()
This static method of the String class returns a new string that is composed
of two discrete strings.
Compares two strings.
Returns a fresh new copy of an existing string.
CompareTo()
Copy()
Format()
This static method is used to format a string literal using other primitives
(i.e., numerical data and other strings) and the {0} notation examined earlier
in this chapter.
Insert()
This method is used to receive a copy of the current string that contains
newly inserted string data.
PadLeft()
PadRight()
These methods return copies of the current string that has been padded
with specific data.
Remove()
Replace()
Use these methods to receive a copy of a string, with modifications
(characters removed or replaced).
Substring()
This method returns a string that represents a substring of the current
string.
ToCharArray()
This method returns a character array representing the current string.
ToUpper()
ToLower()
These methods create a copy of a given string in uppercase or lowercase.
Consider the following example –
Program 2.14
using System;
class Test
{
public static void Main()
{
System.String s1="This is a string";
string s2="This is another string";
if(s1==s2)
Console.WriteLine("Same strings");
else
Console.WriteLine("Different strings");
string s3=s1+s2;
Console.WriteLine("s3={0}",s3);
for(int i=0;i<s1.Length;i++)
Console.WriteLine("Char {0} is {1}\n",i, s1[i]);
Console.WriteLine("Cotains 'is'?: {0}", s1.Contains("is"));
Console.WriteLine(s1.Replace('a',' '));
}
}
The output would be –
Different strings
s3=This is a stringThis is another string
Char 0 is T
Char 1 is h
Char 2 is i
Char 3 is s
Char 4 is
Char 5 is I
Char 6 is s
Char 7 is
Char 8 is a
Char 9 is
Char 10 is s
Char 11 is t
Char 12 is r
Char 13 is I
Char 14 is n Char 15 is g
Cotains 'is'?: True
This is string
Escape Characters and “Verbatim Strings”
Just like C, C++ and Java, C# also provides some set of escape characters as shown –
Character
\’
\"
\\
Meaning
Inserts a single quote into a string literal.
Inserts a double quote into a string literal.
Inserts a backslash into a string literal. This can be quite helpful when
defining file paths.
Triggers a system alert (beep). For console applications, this can be an audio
clue to the user.
Triggers a backspace.
Triggers a form feed.
Inserts a new line (on Win32 platforms).
Inserts a carriage return.
Inserts a horizontal tab into the string literal
Inserts a Unicode character into the string literal.
Inserts a vertical tab into the string literal
Represents NULL character.
\a
\b
\f
\n
\r
\t
\u
\v
\0
In addition to escape characters, C# provides the @-quoted string literal notation
named as verbatim string. Using this, we can bypass the use of escape characters
and define our literals. Consider the following example –
Program 2.15
using System;
class Test
{
public static void Main()
{
string s1="I said, \"Hi\"";
Console.WriteLine("{0}",s1);
s1="C:\\Notes\\DotNet\\Chapter3.doc";
Console.WriteLine("{0}",s1);
string s2=@"C:\Notes\DotNet\Chapter3.doc";
Console.WriteLine("{0}",s2);
}
}
The output would be –
I said, "Hi"
C:\Notes\DotNet\Chapter3.doc
C:\Notes\DotNet\Chapter3.doc
Using System.Text.StringBuilder
In the previous examples, we tried to change the content of strings using various
methods like Replace(), ToUpper() etc. But, the value of a string cannot be modified
once it is established. The methods like Replace() may seems to change the content of
the string, but actually, those methods just output a copy of the string and the
original string remains the same. For example –
string s1=”Hello”;
Console.WriteLine(“s1={0}”, s1);
//Hello
string s2=s1.ToUpper();
Console.WriteLine(“s2={0}”, s2);
//HELLO
Console.WriteLine(“s1={0}”, s1);
//Hello
Thus, whenever we want to modify a string, we should have a new string to store the
modified version. That is, every time we have to work on a copy of the string, but not
the original. To avoid this in-efficiency, C# provides a class called StringBuilder
contained in the namespace System.Text.
Any modification on an instance of
StringBuilder will affect the underlying buffer itself. Consider the following example –
Program 2.16
using System;
using System.Text;
class Test
{
public static void Main()
{
StringBuilder s1= new StringBuilder("Hello");
s1.Append(" World");
Console.WriteLine("{0}",s1);
string s2=s1.ToString().ToUpper();
Console.WriteLine("{0}",s2);
}
}
The output would be –
Hello World
HELLO WORLD
2.19 C# Enumerations
When number of values taken by a type is limited, it is better to go for symbolic names
rather than numeric values. For example, the marital status of a person can be any
one of Married, Widowed, Unmarried, Divorced. To have such symbolic names, C#
provides enumerations –
enum M_Status
{
Married,
//0
Widowed,
//1
Unmarried,
//2
Divorced
//3
}
In enumeration, the value for first symbolic name is automatically initialized to 0 and
second to 1 etc. If we want to give any specific value, we can use –
enum M_Status
{
Married =125,
Widowed,
//126
Unmarried,
//127
Divorced
//128
}
Or
enum M_Status
{
Married =125,
Widowed=0,
Unmarried=23,
Divorced=12
}
By default, the storage type used for each item of enumeration is System.Int32. We
can change it, if we wish –
enum M_Status: byte
{
Married =125,
Widowed=0,
Unmarried=23,
Divorced=12
}
Enumerations can be used as shown below –
Program 2.17
using System;
class Test
{
enum M_Status: byte
{
Married =125,
Widowed=0,
Unmarried=23,
Divorced=12
}
public static void Main()
{
M_Status p1, p2;
p1=M_Status.Married;
p2=M_Status.Divorced;
if(p1==M_Status.Married)
Console.WriteLine("p1 is married"); // p1 is married
if(p2==M_Status.Divorced)
Console.WriteLine("p2 is {0}", M_Status.Divorced); //p2 is Divorced
}
}
The System.Enum Base Class
The C# enumerations are derived from System.Enum class. This base class defines
some methods for working with enumerations.
Member
Format()
GetName()
GetNames()
GetUnderlyingType()
GetValues()
IsDefined()
Parse()
Meaning
Converts a value of a specified enumerated type to its
equivalent string representation according to the specified
format
Retrieves a name (or an array containing all names) for the
constant in the specified enumeration that has the specified
value
Returns the underlying data type used to hold the values for a
given enumeration
Retrieves an array of the values of the constants in a specified
enumeration
Returns an indication of whether a constant with a specified
value
exists in a specified enumeration
Converts the string representation of the name or numeric
value of one or more enumerated constants to an equivalent
enumerated object
Consider the following example to illustrate some of the methods of Enum class.
Program 2.18
using System;
class Test
{
enum M_Status
{
Married ,
Widowed,
Unmarried,
Divorced
}
public static void Main()
{
Console.WriteLine(Enum.GetUnderlyingType(typeof(M_Status)));
Array obj =Enum.GetValues(typeof(M_Status));
Console.WriteLine("This enum has {0} members", obj.Length);
foreach(M_Status p in obj)
{
Console.WriteLine("String name: {0}", p.ToString());
Console.WriteLine("int: ({0}),", Enum.Format(typeof(M_Status), p, "D"));
Console.WriteLine("hex: ({0}),", Enum.Format(typeof(M_Status), p, "X"));
}
if(Enum.IsDefined(typeof(M_Status), "Widowed"))
Console.WriteLine("Widowed is defined");
M_Status p1 = (M_Status)Enum.Parse(typeof(M_Status), "Divorced");
Console.WriteLine("p1 is {0}", p2.ToString());
M_Status p2=M_Status.Married;
if(p1<p2)
Console.WriteLine(“p1 has less value than p2”);
else
Console.WriteLine(“p1 has more value than p2”);
}
}
The output would be –
System.Int32
This enum has 4 members
String name: Married
int: (0) hex: (00000000)
String name: Widowed
int: (1) hex: (00000001)
String name: Unmarried
int: (2) hex: (00000002)
String name: Divorced
int: (3) hex: (00000003)
Widowed is defined
p1 is Divorced
p1 has more value than p2
2.20 Defining Structures in C#
In C#, structures behave similar to class, except that memory structures will be
allocated in stack area, whereas for class memory will be allocated from heap area.
Structures
can
have
member
data,
member
methods,
constructors
(only
parameterized) and they can implement interfaces. Structure in C# is directly derived
from System.ValueType. We can implement boxing and unboxing on structures just
like as we do for any intrinsic data types.
Consider the following example –
Program 2.19
using System;
struct EMP
{
public int age;
public string name;
public EMP(int a, string n)
{
age=a;
name=n;
}
public void disp()
{
Console.WriteLine("Name ={0}, Age ={1}", name, age);
}
}
class Test
{
public static void Main()
{
EMP e=new EMP(25, "Ramu");
e.disp();
object ob=e;
//boxing
MyFun(ob);
}
public static void MyFun(object obj)
{
EMP t=(EMP)obj;
//unboxing
Console.WriteLine("After boxing and un-boxing:");
t.disp();
}
}
The output would be –
Name =Ramu, Age =25
After boxing and un-boxing:
Name =Ramu, Age =25
2.21 Defining Custom Namespaces
In the programs we discussed till now, we have used namespaces like System,
System.Text etc. These are existing namespaces in the .NET framework. We can define
our own namespace i.e. user-defined namespace (or custom namespace). Whenever we
want to group similar classes into a single entity, we can define a namespace.
Assume we need to develop a program to show the features of several vehicles like car,
bus and bike. Then, the classes for all these vehicles can be put under a namespace
like –
namespace Vehicle
{
public class Car
{
//members of Car class
}
public class Bus
{
//members of Bus class
}
public class Bike
{
//members of Bike class
}
}
Now, the namespace Vehicle acts as a container for all these classes. If we want to
create an object of any of these classes in any other application, we can simply write –
using System;
using Vehicle;
//note this
class Test
{
public static void Main()
{
Car c=new Car();
------------}
}
Resolving Name Clashes Across Namespaces
There may be situation where more than one namespace contains the class with same
name. For example, we may have one more namespace like –
namespace MyVehicle
{
public class Car
{
//members of Car class
}
-------}
When we include the namespaces MyVehicle and Vehicle, and try to create an object of
Car class, we will get an error. To avoid this, we will use dot operator for combining
namespace name and class name. For example –
using System;
using Vehicle;
using MyVehicle;
class Test
{
public static void Main()
{
// Car c=new Car();
Error!!! name conflict
Vehicle.Car c1=new Vehicle.Car();
MyVehicle.Car c2=new MyVehicle.Car();
--------}
}
Defining Namespace Aliases
The ambiguity in the namespaces can also be resolved using alias names as shown –
using System;
using Vehicle;
using MyVehicle;
using MyCar=MyVehicle.Car;
class Test
{
public static void Main()
{
Car c1=new Car();
MyCar c2=new MyCar();
--------}
}
Nested Namespaces
We can nest one namespace within the other also. For example –
namespace Vehicle
{
namespace MyVehicle
{
--------}
}
Or
namespace Vehicle.MyVehicle
{
------}
2.22 Summary
This section brought out the salient features of C# value types like structures and
enumerations. We have discussed the concept of static methods, the accessspecifiers/modifiers for the methods of a class, the different keywords used for passing
parameters to methods etc. Also, we understood how to define our own namespace
rather than using the in-build namespaces.
2.23 Keywords

Access Modifiers: public, private, protected, internal, protected internal

Static Data and Static Method

Parameter modifiers: out, ref, params

Passing reference types by value and by reference

System.Array class and its members

Multidimensional Array: Rectangular and Jagged arrays

System.String class and its members

Escape characters and Verbatim Strings

System.Text.StringBuilder class and its members

System.Enum base class and its members

Structures (struct)

Namespaces: Aliases and nested
2.24 Exercises
8. What do you understand by ‘params’ method of parameter passing? Give an
example.
9. Explain the C# static methods and static data with suitable examples.
10. Using the methods in System.String class, design a C# method which replaces all
occurrences of the word “computer” with “COMPUTER”.
11. What is the difference between System.String and System.Text.StringBuilder?
Explain with relevant code some of the features of StringBuilder class.
12. Write a C# program to design a structure Student<USN, Name, Marks, Branch>.
Here, Branch is of type Enum with members MCA, MBA, MTech. Add appropriate
constructor and also a method to hike the marks by 10% to only MCA students.
Show creation of some Student objects and the way to call these methods.
13. Explain various method parameter modifiers used in C#.
14. Design a C# class called Matrix containing an integer matrix as a member and
methods to
(i)
read the elements of the matrix from the keyboard
(ii)
find the product of two matrices
(iii)
to print the elements in the matrix form.
Write a Main() method to call all these methods and to print all the three matrices.
15. Write a program to count the number of objects created for a class.
16. What are jagged arrays? Write a program to find sum of elements in a jagged array
of 3 inner arrays.
Module 2
Unit 3
CONTENTS:
1.28
3.2
Objectives
Introduction
3.3 Formal Definition of the C# Class
3.4 Definition the “Default Public Interface” of a Type
3.5 Recapping the Pillars of OOP
3.6 The First Pillars: C#’s Encapsulation Services
3.7 Pseudo Encapsulation: Creating Read-Only Fields
3.8 Summary
3.9
Keywords
3.10
Exercises
3.1 Objectives
At the end of this lesson, the students will understand the following tasks in detail:

The basic concepts of oops

The method overloading, calling base class constructor through derived class
constructor

The two ways of encapsulation: traditional accessors and mutators and class
properties

The read-only/write-only/static properties

Read only fields
3.15 Introduction
The previous units revealed the programming fundamentals of C#. Since C# is an
object oriented programming (OOP) language, we need to explore the OOPs properties
(that is encapsulation, inheritance and polymorphism) with respect to C#. In this unit,
we will discuss the basic concepts of OOPs in brief, and the encapsulation in detail.
The other two concepts viz. inheritance and polymorphism will be explored in next
unit.
3.16 Formal Definition of C# class
Class is a basis of OOPs. A class can be defined as a user-defined data type (UDT) that
is composed of data (or attributes) and functions (or methods) that act on this data. In
OOPs, we can group data and functionality into a single UDT to model a real-world
entity.
A C# class can define any number of constructors. Constructor is a special type of
method called automatically when object gets created. They are used to provide initial
values for some attributes. The programmer can define default constructor to initialize
all the objects with some common state. Custom or parameterized constructors can be
used to provide different states to the objects when they get created. The general form
of a C# class may look like –
class class_name
{
//data members
//constructors
//methods
}
Understanding Method Overloading
A class can have more than one method with a same name only if number and/or type
of parameters are different. Such methods are known as overloaded methods. For
example –
// Example for overloaded constructors
class Employee
{
public Employee()
{
//default constructor
}
public Employee(string name, int EmpID, float BasicSal)
{
//some code
}
}
// overloaded member-methods: number of arguments are different
class Triangle
{
public float Draw(float height, float base)
{
//some code
}
//two arguments
public float Draw(float sideA, float sideB, float sideC) //3 args
{
//some code
}
}
// overloaded member-methods: type of arguments are different
class Shape
{
public float Area(float height, float base)
{
//some code
}
public int Area(int a, int b)
{
//some code
}
}
// overloading can not be done only based on return-type
class Shape
{
//error!!!
public float Area(float height)
{
//one parameter of type float
//some code
}
public int Area(float a )
//one parameter of type float
{
//some code
}
}
Self-Reference in C#
The key-word this is used to make reference to the current object i.e. the object
which invokes the function-call. For example –
class Employee
{
string name;
int eId;
public Employee(string name, int EmpID)
{
this.name=name;
this.eId=EmpID;
}
}
Note that the static member functions of a class can not use this keyword as static
methods are invoked using class name but not the object.
Forwarding Constructor Calls using “this”
Using this keyword, we can force one constructor to call another constructor during
constructor call. This will help us to avoid redundancy in member initialization logic.
Consider that there is a class called IDGenerator containing a static method to
generate employee id based on some pre-defined criteria like department and
designation of employee. The application may be having two situations: (a) provide an
employee id at the time of object creation, and (b) the employee id must be generated
from IDGenerator class. But the parameter like name of the employee is provided
during object creation itself. In such a situation, to avoid code-redundancy, we may
use this keyword to forward one constructor call to other as shown below –
class IDGenerator
{
static int id;
public static int GetEmpID()
{
//code for generating an employee id viz id
return id;
}
}
class Employee
{
string name;
int eID;
float BasicSal;
public Employee(string n, int EmpID, float b)
{
this.name=n;
//either use this
this.eID=EmpID;
BasicSal=b;
//or not use this
}
/ * GetEmpID() method is called first to generate employee-id and then the
construct call is forwarded to the above constructor */
public Employee(string n): this(n, IDGenerator.GetEmpID(), 0.00)
{
}
public static void Main()
{
//direct call for constructor with three arguments
Employee e1=new Employee(“Ramu”, 111, 12000.00);
/* call for constructor with single arguments which in-turn calls
constructor with three arguments later */
Employee e2=new Employee(“Shyamu”);
------------------}
}
In the above example, if we would have not forwarded the constructor call, then we
would need to write redundant code in the second constructor like –
public Employee(string n)
{
this.name=n;
this.eID=IDGenerator.GetEmpID();
this.BasicSal=0.00;
}
Thus, using this keyword to forward constructor call, we are avoiding code
redundancy.
3.17 Defining the “Default Public Interface” of a Type
The term default public interface refers to the set of public members (data or method)
that are directly accessible from an object. That means, the default public interface is
any item declared in the class with the keyword public. In C#, the default public
interface of a class may be any of the following:

Methods
: Named units that model some behavior of a class

Properties
: Accessor and mutator functions

Public data
: A data member which is public (though it is not good
to use, C# provides if programmer needs)
Apart from above items, default public interface may include user defined event,
delegates and nested types.
Specifying Type Visibility: Public and Internal Types
We know that any member of a class can be declared for its level of visibility (access
specifier) using the keyword public, private, protected, internal and protected internal.
Just like members, the type (class, structure, interface, delegate, enumeration) itself
can be specified for its level of visibility.
The method/member visibility is used to know which members can be accessed from
an object of the type, where as, the type visibility is used to know which parts of the
system can create the object of that type.
A non-nested C# type can be marked by either public or internal and nested types can
be specified with any of public, private and internal. The public types can be created by
any other objects within the same assembly or by other external assemblies. But, the
internal types can be created by the types within the same assembly and are not
accessible outside the assembly. By default, the visibility level of a class is internal.
For example,
//this class can be used outside this assembly also
public class Test
{
//body of the class
}
//this class can be accessed only within its assembly
internal class Test
{
//body of the class
}
Or
class Test
//by default, it is internal
{
//body of the class
}
3.18 Recapping the Pillars of OOP
All the object oriented languages possess three principals (considered to be pillars of
OOP) viz.

Encapsulation
: How the languages hide an object’s internal
implementation?

Inheritance
: How the languages promote code reuse?

Polymorphism
: How the languages let the programmer to treat
related objects in a similar way?
Here, we will discuss the basic role of each of these pillars and then move forward for
detailed study.
Encapsulation Services
Encapsulation is the ability to hide unnecessary implementation details from the
object user. For example, assume we have created a class named DBReader having
methods open() and close():
DBReader d=new DBReader();
d.open(“C:\MyDatabase.mdf”);
……..
d.close();
The class DBReader has encapsulated the inner details of locating, loading,
manipulating and closing data file. But, the object user need not worry about all these.
Closely related to the notion of encapsulation is data hiding. We do this by making
data members as private. The private data can be modified only through the public
member functions of that class.
Inheritance: The is-a and has-a Relationships
Inheritance is the ability to create new class definitions based on existing class
definitions. In other words, inheritance allows us to extend the behavior of base class
by inheriting core functionality into a derived class.
For example, we know that System.Object is the topmost class in .NET. We can create
a class called Shape which defines some properties, fields, methods and events that
are common to all the shapes. The Hexagon class extends Shape and inherits
properties of Shape and Object. It contains properties of its own. Now, we can say
Hexagon is a Shape, which is an object. Such kind of relationship is called as is-a
relationship and such inheritance is termed as Classical inheritance.
Object
Shape
Hexagon
There is another type of code reuse in OOP viz. the containment/delegation model or
has-a relationship. This type of reuse is not used for base class/child class
relationships. Rather, a given class can define a member variable of other class and
make use of that either fully or partly.
For example, a Car can contain Radio. The containing type (Car) is responsible for
creating the inner object (Radio). If the Car wishes to make the Radio’s behavior
accessible from a Car instance, it must provide some set of public functions that
operate on the inner type.
Car
//Method of Car
Radio
void TurnOnRadio(bool on)
{
//Delegate to inner Radio
radioObj.Power(on);
}
Polymorphism: Classical and Ad Hoc
Polymorphism can be of two types viz. classical and Ad Hoc. A Classical
polymorphism takes place in the languages that support classical inheritance. The
base class can define a set of members that can be overridden by a subclass. When
subclass overrides the behavior defined by a base class, they are essentially redefining
how they respond to the same message. For example, assume Shape class has defined
a function named Draw() without any parameter and returning nothing. As every
shape has to be drawn in its own manner, each subclass like Hexagon, Circle etc. can
redefine the method Shape() as shown –
Object
void Draw()
Shape
Hexagon
Circle
Draw()
Draw()
Classical polymorphism allows a base class to enforce a given behavior on all
subclasses.
Ad hoc polymorphism allows objects that are not related by classical inheritance to
be treated in a similar manner, provided that every object has a method of the exact
signature. Languages that support ad hoc polymorphism possess a technique called
late binding to find the underlying type of given object at runtime.
Consider the following situation:
Circle
Hexagon
Draw()
Draw()
Rectangle
Draw()
Note that the three classes Circle, Hexagon and Rectangle are not from same base.
Still each class supports an identical Draw() method. This is possible by implementing
Draw() method on generic types. When a particular type is used for calling Draw(), it
will act accordingly.
3.19 The First Pillar: C#’s Encapsulation Services
The concept of encapsulation says that the object’s data should not be directly
accessible from a method. If the data is to be manipulated, it has to be done indirectly
using accessor (get) and mutator(set) method. C# provides following two techniques to
manipulate private data members –

Define a pair of traditional accessor and mutator methods

Defined a named property.
It is good programming practice to make all the data members or fields of a class as
private. This kind of programming is known as black box programming.
Enforcing Encapsulation Using Traditional Accessors and Mutators
If we want to access any private data, we can write a traditional accessor (get method)
and when we want to provide value to data, we can write a mutator (set method). For
example,
class Emp
{
string Name;
public string GetName()
//accessor
{
return Name;
}
public void SetName(string n)
{
Name=n;
}
}
class Test
{
public static void Main()
{
Emp p=new Emp();
p.SetName(“Ramu”);
//mutator
Console.WriteLine(“{0}”,p.GetName());
}
}
In this example, using the public methods, we could able access private data.
Another Form of Encapsulation: Class Properties
Apart from traditional accessors and mutators, we can make use of properties
provided by .NET class (and structures, interfaces). Properties resolve to a pair of
hidden internal methods. The user need not call two separate methods to get and set
the data. Instead, user is able to call what appears to be a single named field. For
illustration, consider the following example –
using System;
class Emp
{
string Name;
public string EmpName
// EmpName is name of property
{
get
{
return Name;
// Name is field-name
Name=value;
// value is a keyword
}
set
{
}
}
}
class Test
{
public static void Main()
{
Emp p=new Emp();
p.EmpName="Ramu";
//use name of the property
Console.WriteLine("{0}",p.EmpName);
}
}
A C# property is composed using a get block (accessor) and set block (mutator). The
value keyword represents the right-hand side of the assignment. Though, value is an
object, the underlying type of the object depends on which kind of data it represents.
For example, in the previous program, the property EmpName is operating on a private
string, which maps to System.String. Unlike traditional accessors and mutators, the
properties make our types easier to manipulate.
Properties are able to respond to the intrinsic operators in C#. For example,
using System;
class Emp
{
int Sal;
public int Salary
{
get
{
return Sal;
}
set
{
Sal=value;
}
}
}
class Test
{
public static void Main()
{
Emp p=new Emp();
p.Salary=12000;
p.Salary++;
Console.WriteLine("{0}",p.Salary);
//12001
p.Salary -=400;
Console.WriteLine("{0}",p.Salary);
}
//11601
}
Read-only and Write-only Properties
In some of the situations, we just want to set a value to a data member and don’t want
to return it. In such a situation, we can omit get block. A property with only a set
block is known as write-only property. In the same manner, we can just have a get
block and omit set block. A property with only a get block is known as read-only
property. For example,
class Emp
{
string SSN, name;
int EmpID;
public Emp(string n, int id)
{
name=n;
EmpID=id;
}
public string EmpSSN
{
get{ return SSN;}
}
}
Understanding static properties
//read-only property
C# supports static properties. Note that, static members of a class are bound to class
but not for objects. That is, to access static members, we need not create an object of
the class. The same rule will apply to static properties too. For example,
class Emp
{
static string CompanyName;
public static string Company
{
get{ return CompanyName;
}
set{ CompanyName=value;
}
}
public static void Main()
{
Emp.Company=“RNSIT”;
//use class name
Console.WriteLine(“We are at {0}”, Emp.Company);
}
}
Understanding static Constructors
As we have seen, static properties can be used to set and get values for static
members. Assume a situation, where we need to just set a value commonly for all the
objects. Writing a static write-only property with a set-block and then assigning value
is quite time-consuming. For this purpose, C# provides another method for doing so,
through static constructors. For example,
class Emp
{
static string CompanyName;
static Emp()
{
CompanyName=“RNSIT”;
}
public static void Main()
{
Console.WriteLine(“We are at {0}”, Emp.Company);
}
}
Now, all the objects of Emp class will be set the value ”RNSIT” for the member
CompanyName automatically as soon as the objects gets created.
3.20 Pseudo-Encapsulation: Creating Read-Only Fields
Just like read-only properties, we have a notion of read-only fields. Read-only fields
offer data preservation via the keyword readonly. The readonly field can be given a
value through assignment only at the time of declaration or as a part of constructor.
class Student
{
public readonly int Sem=5;
public readonly string USN;
//assignment during declaration
string name;
public Student(string n, string usn)
{
name=n;
USN=usn;
//assignment through constructor
}
public static void Main()
{
Student s1=new Student(“Abhishek”, “1RN07MCA01”);
s1.Sem= 3;
//error
s1.USN=“1RN07MCA02”;
//error
………
}
}
NOTE:
•
The keyword readonly is different from const.
•
The const fields can be assigned a value at the time of declaration only and
assignment is not possible there-after.
•
So, const fields will have same value through out the program, as it is compiletime constant.
•
On the other hand, readonly fields can be assigned a value through constructor
also.
•
So, they can have different values based on constructor call.
•
For example –
class Student
{
public readonly int Sem=5;
//make Sem as 5 for all objects
public readonly string USN;
string name;
public Student(string n, string usn)
{
name=n;
USN=usn;
}
public Student(string n, string u, int s) //change sem for one student
{
name=n;
USN=u;
Sem=s;
}
public static void Main()
{
Student s1=new Student(“Ramu”, “MCA01”); //sem is 5 by default
Student s2=new Student(“Shyam”, “CS02”, 3); //sem is set to 3
-------------}
}
In the above example, if the field Sem would have been specified with const modifier,
then trying to change the value through constructor will generate error. But, readonly
keyword will allow the programmer to keep some fields as constant unless and
otherwise specified through constructor.
3.21 Summary
Encapsulation is one of the basic building blocks of object oriented programming
language. In C#, we can achieve encapsulation either by writing accessor and mutator
methods or by writing properties. Again, based on our need in a program, we can write
read-only/write-only properties. One can make use of static constructors and readonly fields too.
3.22 Keywords

Method Overloading

The keyword this and Forwarding constructor calls

Method/Member visibility and Type visibility

Encapsulation, Inheritance, Polymorphism, Data hiding

Is-a and Has-a relationship (Classical inheritance and Containment/Delegation
model)

Classical and Ad-hoc Polymorphism

Late Binding

Accessor and Mutators

Properties (get and set): Read-only, Write-only, static

Static Constructors and Read-only fields
3.23 Exercises
1. What are the basic components of Object oriented programming and how they are
implemented? Give examples.
2. How do you overload a method? Explain with an example.
3. What do you mean by forwarding constructor calls? Explain.
4. What are member visibility and type visibility?
5. Explain “is-a” and “has-a” relationship with respect to inheritance.
6. What is encapsulation? What are two ways of enforcing encapsulation? Give
examples for both the methods.
7. Explain the concept of static properties with example.
8. What do you mean by static constructors? Write a program to illustrate the same.
9. Explain the concept of readonly fields with an example.
Module 2
Unit 4
CONTENTS:
1.29
4.2
Objectives
Introduction
4.3 The Second Pillar: C#’s Inheritance Supports
4.4 Keeping family secrets: The “Protected” Keyword
4.5 Nested Type Definitions
4.6 The Third Pillar: C#’s Polymorphic Support
4.7 Casting Between
4.8 Summary
4.9
Keywords
4.10
Exercises
5.1 Objectives
At the end of this lesson, the students will understand:

The concept of Inheritance

The protected members of a class

Sealed classes

Polymorphism

Virtual Methods

Abstract Classes

Abstract Methods

Versioning Class Members

Implicit and Explicit Casting
4.16 Introduction
The previous unit elaborated about one of the OOP concepts viz. Encapsulation. Here,
we will study the other two viz. Inheritance and Polymorphism. Inheritance allows the
code re-usability. The base class properties can be made use of in the derived class.
But C# does not support multiple inheritance. To get the properties of more than one
class, we need to design interfaces which will be discussed in next module.
Polymorphism means One interface, multiple forms. Under this, we are going to study
the concept of abstract classes, abstract methods, virtual methods and so on.
4.17 The Second Pillar: C#’s Inheritance Supports
Inheritance facilitates code reuse. The inheritance can be either classical inheritance
(is-a relationship) or containment/delegation model (has-a relationship).
When we establish is-a relationship between classes, we are building a dependency
between types. The basic idea of classical inheritance is that new classes may
influence and extend the functionality of other classes. The hierarchy may look
something like –
Employee
Manager
SalesMan
Now we can say that, Manager is-a Employee and SalesMan is-a Employee.
In classical inheritance, the base classes are used to define general characteristics
that are common to all derived classes.
The derived classes extend this general
functionality while adding more specific behaviors to the class.
Consider an example –
public class Emp
{
protected string Name;
protected int EmpID;
…………..
}
public class SalesMan:Emp
//SalesMan is inherited from Emp
{
int number_of_sales;
// also includes members Name, EmpID
………..
}
class Test
{
public static void Main()
{
Emp e=new Emp();
SalesMan s=new SalesMan();
-----------}
}
Controlling Base-Class Creation
Usually, the base class constructors are used to initialize the members of base class.
When derived class has few more members and if we need to initialize, then we will
write constructor for derived class. In such a situation, there is a repetition of code.
For example –
public class Emp
{
protected string Name;
protected int EmpID;
public Emp(string n, int eid, float s)
{
Name=n;
EmpId=eid;
}
}
public class SalesMan:Emp
{
int number_of_sales;
public SalesMan(string n, int eid, int s)
{
Name=n;
//code repeated
EmpId=eid;
//code repeated
number_of_sales=s;
}
}
If there is more number of data fields in the base class, then in each of the derived
class constructors, we need to repeat such assignment statements. To avoid this, C#
provides an option to explicitly call base class constructors as shown below –
public class Emp
{
protected string Name;
protected int EmpID;
public Emp(string n, int eid)
{
Name=n;
EmpId=eid;
}
}
public class SalesMan:Emp
{
int bonus;
public SalesMan(string n, int eid, int b): base(n, eid)
{
bonus=b;
//other members are passed to base class to initialize
}
}
class Test
{
public static void Main()
{
Emp e1=new Emp(“Ram”, 25);
SalesMan s1= new SalesMan(“Sham”, 12, 10);
-----------}
}
Multiple Base Classes
Deriving a class from more than one base class is not possible in C#. That is, multiple
inheritance is not supported by C#. When we need to use the properties of more than
one class in a derived class, we need to write interfaces rather than classes. Then we
can implement any number of interfaces to achieve code-reusability.
4.18 Keeping Family Secrets: The protected Keyword
The private members of a class can not be available to derived classes. So, we will go
for protected keeping the privacy of the data. The protected members of a class can
not be accessed from outside, but still be available to derived classes.
Preventing Inheritance: Sealed Classes
In some of the situations, we may don’t want our class to be inherited by any other
class. For example –
Employee
SalesMan
Part-time SalesMan
Here, a class called Part-time SalesMan is derived from SalesMan class which in-turn
is derived from Employee class as shown in the diagram. Now, we don’t want any more
classes to be derived from Part-time SalesMan class. To prevent such inheritance, C#
provides a keyword sealed. The usage is depicted here-under:
public sealed class Part_TimeSalesMan: SalesMan
{
//body of Part_TimeSalesMan class
}
Now any attempt made to derive a class from Part_TimeSalesMan class will generate
an error:
public class Test: Part_TimeSalesMan
//compile-time error!!!
{
…………..
}
Many of the inbuilt classes of C# are sealed classes. One such example is
System.String.
Programming for Containment/Delegation
Till now we have discussed is-a relationship. Now we will see, how to write program for
has-a relationship. Consider a class Radio –
class Radio
{
public void TurnOn(bool on)
{
if(on)
Console.WriteLine(“Radio is on”);
else
Console.WriteLine(“Radio is off”);
}
}
Now, consider a class Car –
class Car
{
int CurrSpeed, MaxSpeed;
string name;
bool carIsDead=false;
public Car() { MaxSpeed=100}
public Car(string n, int m, int c)
{
name=n;
MaxSpeed=m;
CurrSpeed=c;
}
public void SpeedUp(int a)
{
if(carIsDead)
Console.WriteLine(“Out of order”);
else
{
CurrSpeed+=a;
}
}
}
Now, we have two classes viz. Radio and Car. But, we can not say, “Car is a Radio” or
“Radio is a Car”. Rather, we can say, “Car has a Radio”. Now, the Car is called
containing Class and Radio is called contained class.
To make the contained class to work, we need to re-define Car class as –
class Car
{
………..
private Radio rd=new Radio();
}
To expose the functionality of the inner class to the outside world requires delegation.
Delegation is the act of adding members to the containing class that make use of the
functionality of contained class.
class Car
{
……….
public void Tune(bool s)
{
rd.TurnOn(s);
//delegate request to inner object
}
}
To make this function work, in the Main() function we can write –
Car c=new Car();
c.Tune(false);
Thus by making one class to be a member of other class, we can establish has-a
relationship. But it is always the job of outer class to define a member function which
activates the inner class objects.
4.19 Nested Type Definitions
In C#, it is possible to define a type (enum, class, interface, struct, delegate) directly
within the scope of a class. When a type is nested within a class, it is considered as a
normal member of that class. For example,
class C1
{
………..
//members of outer class
class C2
{
……….
//members of inner class
}
}
Nested type seems like has-a relationship, but it is not. In containment/delegation,
the object of contained class is created within containing class. But, it is possible to
create the objects wherever we wish. But in nested types, the inner class definition is
within the body of out class and hence, inner class objects can not be created outside
the outer class.
4.20 The Third Pillar: C#’s Polymorphic Support
Polymorphism answers the question “how to make related objects respond differently
to the same request?” Consider the following example to illustrate the need of
polymorphism.
Assume we have a base class Employee and two derived classes Manager and
SalesMan.
class Employee
{
…….
public void Bonus(float b)
{
basicSal+=b;
}
}
class Manager:Employee
{
………..
}
class SalesMan: Employee
{
…………
}
Now, the classes Manager and SalesMan both contains the method Bonus(float). Thus
in the Main() function, we can call Bonus() through the objects of Manger and
SalesMan –
Manager m=new Manager();
m.Bonus(500);
SalesMan s=new SalesMan()
s.Bonus(300);
Obviously, the Bonus() method works same for both the objects. But in reality, the
bonus for a SalesMan should be calculated based on number of sales. Manager can
be given bonus based on his other performance like successful completion and
delivery of a project etc. This means, we need a Bonus() method which works
differently in two derived classes.
The polymorphic technique of C# provides solution for this problem. With the help of
virtual and override keywords, we can make same function to behave differently in
base-class and all the derived classes. For example,
class Employee
{
-----------------public virtual void Bonus(float b)
{
basicSal+=b;
}
}
class SalesMan:Employee
{
-----------------public override void Bonus(float b)
{
int salesBonus=0;
if(numOfSales<=100)
salesBonus=10;
elseif (numOfSales<=200)
salesBonus=20;
base.Bonus(b*salesBonus);
//use of base class method
}
}
class Test
{
public static void Main()
{
Employee e=new Employee();
e.Bonus(100);
//base class Bonus() is called
SalesMan s=new SalesMan()
s.Bonus(300);
//derived class Bonus() is called
}
}
We can see that when the Bonus() method is invoked through base class object, the
corresponding method will be called. Whereas, when Bonus() is invoked with the object
s of derived class, the overridden method Bonus() will be called.
NOTE that any overridden method is free to call its corresponding base class method
using the keyword base. Thus, the overridden method can have its own statements
and also can make use of the behaviors of base class virtual method also.
Defining Abstract Classes
Sometimes, the creation of base class objects will not be of any use. For example, an
Employee is always identified with a designation. Thus, an employee must be either a
Manager or a SalesMan or working at some such other designation. So, creation of the
object of class Employee is useless. In such situations, we can prevent base class
instantiation by making it as abstract.
public abstract class Employee
{
………
}
Employee e=new Employee
//error !!
Thus, abstract class is a class which just declares members (data and method) and no
object can be created for this class. The member methods of abstract class may be
used by the objects of derived class either directly or by overriding them in the derived
class.
Enforcing Polymorphic Activity: Abstract Methods
An abstract class may contain the definition for methods it is defining. The derived
classes are free to use such methods directly or they can override and then use. But in
some situations, the definitions of abstract base-class methods may not be useful. The
derived classes only should define the required behavior. In this case, we need to force
the derived classes to override the base class methods. This is achieved using
abstract methods.
An abstract class can define any number of abstract methods, which will not supply
any default implementation. Abstract method is equivalent to pure virtual functions
of C++. The abstract methods can be used whenever we wish to define a method that
does not supply a default implementation. To understand the need for abstract
methods, consider the following situation:
Object
virtual void Draw()
Shape
Draw()
Hexagon
Draw()
Circle
Here, each derived class like Hexagon and Circle of Shape has to override Draw()
method to indicate the methodology of drawing itself. If any derived class forgets to
override Draw() method, the code may look like –
abstract class Shape
{
public virtual void Draw()
{
Console.WriteLine(“Shape.Draw()”);
}
}
public class Circle:Shape()
{
public Circle();
-------------}
public class Hexagon:Shape()
{
…………..
public override void Draw()
{
Console.WriteLine(“Hexagon.Draw()”);
}
}
public class Test
{
public static void Main()
{
Circle c= new Circle();
c.Draw();
//Draw() method of Shape class is called
Hexagon h=new Hexagon();
h.Draw();
}
}
//Draw() method of Hexagon class is called
In this example, the Circle class is not overriding the Draw() method. So, when the
Draw() method is invoked through the object of Circle class, the Draw() method of
Shape class itself will be called. Obviously, the Draw() method of Shape class will not
be having any information about how to draw a circle and hence it will be
meaningless. This shows that virtual methods of base class need not be
overridden by derived classes.
When programmer must be forced to override Draw() method, the method of base class
must be made abstract –
abstract class Shape
{
// completely abstract method. Note the semicolon at the end
public abstract void Draw();
---------}
public class Circle:Shape()
{
----------------public override void Draw()
//has to override
{
Console.WriteLine(“Circle.Draw()”);
}
}
public class Hexagon:Shape()
{
…………..
public override void Draw()
//has to override
{
Console.WriteLine(“Hexagon.Draw()”);
}
}
public class Test
{
public static void Main()
{
Circle c= new Circle();
c.Draw();
//Draw() method of Circle class is called
Hexagon h=new Hexagon();
h.Draw();
//Draw() method of Hexagon class is called
}
}
Thus, by making a base class method as abstract, we are making use of run-time
polymorphism or late binding. That is, the binding between the object and the
method to be invoked is decided only at runtime. This concept is more clear when we
re-design our Main() function in the following format:
public static void Main()
{
Shape[ ] s={new Circle(), new Hexagon()};
for(int i=0;i<s.Length;i++)
s[i].Draw();
}
This may seems to be quite interesting. Till now, we have heard that we can not create
objects for abstract classes. But in the above code snippet, we are creating array of
objects of base-classes. How? This is very obvious as array of objects stores references
to variables but not the objects directly. Thus, in the above example, s[0] stores
reference to the object of type Circle and s[1] stores reference to the object of type
Hexagon.
In the for loop, when we use the statement –
s[i].Draw();
the content (i.e. whether reference to Circle or reference to Hexagon) of s[i] is
considered to decide which Draw() method to be invoked and the type (i.e. here
type is Shape) of s[i] is ignored. This is nothing but run-time polymorphism or
late binding.
NOTE (Very important):
After dealing with entire story about abstract classes and abstract methods, somebody
may feel what exactly is the difference between abstract classes and interfaces?
Because just-like abstract classes, interfaces also provide just a prototype of the
methods and the implementing classes have to define those methods. (more about
interfaces in Module 3)
The major difference comes in case of multiple inheritance. We know that we can not
derive a class from more than one base-class in C#. That is multiple inheritance is not
possible. But, we may have a situation where there is more than one base class and
we need to derive a class from all those base classes and then override the virtual
methods present in all the base classes. In such a situation, instead of writing
abstract base classes, we should write interfaces. Because, a class can be derived from
one base class and it can implement many number of interfaces.
Versioning class members
C# provides facility of method hiding, which is logical opposite of method overriding.
Assume the following situation:
Object
Abstract Draw() method defined
Shape
Hexagon
Draw() method implemented
Circle
Oval
Completely new Draw() method
hiding Circle.Draw()
The class Circle is derived from Shape. The Shape class has an abstract method Draw()
which is overridden within Circle class to indicate how to draw a circle. The class Oval
behaves similar to Circle. But, methodology for drawing an oval is different from that
of circle. So, we need to prevent the Oval class from inheriting Draw() method. This
technique is known as versioning a class. This is achieved by using the key word new
as shown below –
public class Oval: Circle
{
public Oval()
{
-----}
public new void Draw()
{
//Oval specific drawing algorithm
}
}
Now, when we create an object of Oval class and invoke Draw() method, the most
recently used definition of Draw() is called. That is,
Oval ob=new Oval();
ob.Draw();
//calls the Draw() method of Oval class
Thus, the keyword new breaks the relationship between the abstract Draw() method
defined by the base class and the Draw() method in the derived class.
On the other hand, if we wish, we can make use of the base class method itself by
using explicit casting:
Oval obj=new Oval();
((Circle)obj).Draw(); //calls Draw() method of Circle class
NOTE:
The method hiding is useful when we are making use of the types defined in another
.NET assembly and we are not sure whether that type contains any method with the
same name as we are using.
That is, in our application we may be using inbuilt
classes (with the help of keyword using). We are going to create a method Draw() in our
application, but we are not sure whether any of the inbuilt class contains a method
with same name. In such a situation, we can just put a keyword new while declaring
our method. This will prevent the possible call to built-in class method.
4.21 Casting Between
Till now, we have discussed number of class hierarchies. Now we should know the
laws of casting between class types. Consider the following situation, where we have
is-a relationship –
Object
Employee
Manager
SalesMan
Part_time
SalesMan
Keeping the above class hierarchy in mind, the following declarations are purely valid.
object ob=new Manager();
Employee e=new Manager();
SalesMan s= new Part_timeSalesMan();
In all these situations, the implicit casting from derived class to base class is done
by the C# CLR. On the other hand, when we need to convert base class reference to be
stored in derived class object, we should make explicit casting as –
object ob;
Manager m=(Manager)ob;
Here, the explicit cast from base class to derived class is done.
Determining the Type-of Objects
C# provides three ways to determine if a base class reference is actually referring to
derived types:
•
Explicit casting
•
is keyword
•
as keyword
We have seen explicit casting just now.
The usage of other two methodology is
depicted with the help of examples:
Object ob=new Manager();
if(ob is Manager)
//checking whether ob is Manager
//do something
Employee e;
SalesMan s= e as SalesMan;
//converting Employee type to SalesMan type
//do something with s
Numerical Casts
In addition to casting between objects, the numerical conversions also follow similar
rules. When we are trying to cast larger (in size of type) type to smaller type, we need
to make explicit casting:
int x=25000;
byte b=(byte)x;
//loss of information
While converting from larger type to smaller type, there is a chance of data-loss.
When we need to cast smaller type to larger type, the implicit casting is done
automatically:
byte b=23;
int x=b;
//implicit casting
There will not be any loss of data during such casting.
4.22 Summary
Till now, we have discussed some of very important features of C# programming
language. While developing huge project or a product, the concepts inheritance and
polymorphism makes sense. The developer should understand when and where to use
the concept of abstract classes virtual functions etc.
4.23 Keywords

Classical Inheritance (is-a relationship)

Containment/ Delegation Model (has-a relationship)

The keywords protected and sealed

The keywords abstract, virtual, base and override

Abstract methods or Pure Virtual functions

Run-time Polymorphism or Late Binding

Implicit and Explicit type casting

The keywords is and as
4.24 Exercises
5. How do you explicitly call base class constructor from derived class
constructor? Explain with an example.
6. What do you mean by nested classes? What is the difference between nested
classes and Containment/delegation?
7. How do you prevent inheritance using sealed classes? Explain with an example.
8. With an example, explain the concept of abstract classes.
9. Write a program to demonstrate the concept of virtual and override keywords.
10. Explain the concept of late binding with an example.
11. Write a note on Casting.
12. Explain abstract methods with example.
13. What do you mean by versioning members? Explain.
Module 3
Unit 1
CONTENTS:
1.30
1.31
1.32
1.33
1.34
1.35
1.36
1.37
1.38
Objectives
Introduction
Meaning of Errors and Exceptions
The Role of .NET Exception Handling
The System. Exception Base Class
Throwing a Generic Exception
Catching Exception
CLR System – Level Exception (System.SystemException)
Custom Application-Level Exception
(System.ApplicationException)
1.39
1.40
1.41
Handling Multiple Exceptions
The Finally Block
Dynamically Identifying Application and System Level
Exceptions
1.42
1.43
1.44
Summary
Keywords
Exercises
1.25 Objectives
At the end of this lesson, the students will understand:

The meaning of exceptions

Types of exceptions: System and Application

Usage of keywords like try, catch, throw, finally

Building user-defined/custom exceptions
1.26 Introduction
The point of this unit is to understand how to handle runtime anomalies in your code
base through the use of structured exception handling. Not only will you learn about
the C# keywords that allow you to handle such problems, but you will also come to
understand the distinction between application-level and system-level exceptions. This
discussion will also provide a lead-in to the topic of building custom exceptions types
as well as how to leverage the built-in exception handling functionality of VS.NET.
1.27 Meaning of Errors, Bugs and Exceptions
Mistakes are bound to happen during programming. Problems may occur due to bad
code like overflow of array index, invalid input etc. So, the programmer needs to
handle all possible types of problems. There are three terminologies to define mistakes
that may occur.
•
Bugs:
–
Errors done by the programmer.
–
Example: making use of NULL pointer, referring array index out of
bound, not deleting allocated memory etc.
•
•
Errors:
–
Caused by user of the application.
–
For example: entering un-expected value in a textbox, say USN.
Exceptions:
–
Runtime anomalies those are difficult to prevent.
–
Example: trying to open a corrupted file, trying to connect non-existing
database etc.
1.28 The Role of .NET Exception Handling
The .NET platform provides a standard technique to send and trap runtime errors:
structured exception handling (SEH). The beauty of this approach is that developers
now have a unified approach to error handling, which is common to all languages
targeting the .NET universe. Another bonus of .NET exceptions is the fact that rather
than receiving a cryptic numerical value that identifies the problem at hand,
exceptions are objects that contain a human-readable description of the problem, as
well as a detailed snapshot of the call stack that triggered the exception in the first
place. Furthermore, you are able to provide the end user with help link information
that points the user to a URL that provides detailed information regarding the error at
hand as well as custom user-defined data.
The Atoms of .NET Exception Handling
Programming with structured exception handling involves the use of four interrelated
entities:

A class type that represents the details of the exception that occurred

A member that throws an instance of the exception class to the caller

A block of code on the caller’s side that invokes the exception-prone member

A block of code on the caller’s side that will process (or catch) the exception
should it occur
The C# programming language offers four keywords (try, catch, throw, and finally) that
allow you to throw and handle exceptions. The type that represents the problem at
hand is a class derived from System.Exception
1.29 The System.Exception Base Class
All user- and system-defined exceptions ultimately derive from the System.Exception
base class (which in turn derives from System.Object). Core Members of the
System.Exception Type are shown below:
System.Exception
Meaning
Property
HelpLink
This property returns a URL to a help file describing the
error in full detail.
InnerException Used to obtain information about the previous exceptions
that caused the current exception to occur. The previous
exceptions are recorded by passing them into the
constructor of the most current exception. This is a readonly property.
Message
Returns the textual description of a given error. The error
message itself is set as a constructor parameter. This is a
read-only property.
Source
Returns the name of the assembly that threw the
exception.
StackTrace
Contains a string that identifies the sequence of calls
that triggered the exception. This is a read-only property.
Returns a Method-Base type, which describes numerous
details about the method that threw the exception
(ToString() will identify the method by name). This is a
read-only property.
TargetSite
1.30 Throwing a Generic Exception
During the program, if any exception occurs, we can throw it to either a specific
exception
like
FileNotFoundException,
ArrayIndexOutOfBoundException,
DivideByZeroException etc. or we can through a generic exception directly using
Exception class. The object of Exception class can handle any type of exception, as it is
a base class for all type of exceptions. Here is an example to show how to throw an
exception:
using System;
class Test
{
int Max=100;
public void Fun(int d)
{
if(d>Max)
throw new Exception("crossed limit!!!");
else
Console.WriteLine("d={0}", d);
}
public static void Main()
{
Test ob=new Test();
Console.WriteLine("Enter a number:");
int d=int.Parse(Console.ReadLine());
ob.Fun(d);
}
}
Output:
Enter a number: 12
d=12
Enter a number: 567
Unhandled Exception: System.Exception: crossed limit!!!
at Test.Fun(Int32 d)
at Test.Main()
In the above example, if the entered value d is greater than 100, then we are throwing
an exception. We are passing message “crossed limit” to a Message property of
Exception class.
Deciding exactly what constitutes throwing an exception and when to throw an
exception is up to the programmer.
1.31 Catching Exceptions
When a block of code is bound to throw an exception, it needs to be caught using
catch statement. Once we catch an exception, we can invoke the members of
System.Exception class. Using these members in the catch block, we may display a
message about the exception, store the information about the error in a file, send a
mail to administrator etc. For example,
using System;
class Test
{
int Max=100;
public void Fun(int d)
{
try
{
if(d>Max)
throw new Exception("crossed limit!!!");
}
catch(Exception e)
{
Console.WriteLine("{0}", e.Message);
}
Console.WriteLine("d={0}", d);
}
public static void Main()
{
Test ob=new Test();
Console.WriteLine("Enter a number:");
int d=int.Parse(Console.ReadLine());
ob.Fun(d);
}
}
Output:
Enter a number: 12
d=12
Enter a number: 123
crossed limit!!!
d=123
In the above example, we are throwing an exception if d>100 and is caught. Throwing
an exception using the statement –
throw new Exception (“Crossed Limit”);
means, creating an object of Exception class and passing that object to catch block.
While passing an object, we are passing the message also, which will be an input for
the Message property of Exception class.
A try block is a section of code used to check for any exception that may be
encountered during its scope. If an exception is detected, the program control is sent
to the appropriate catch block. If the code within try block does not trigger any
exception, then the catch block is skipped fully and program will continue to execute
further.
Once an exception has been handled, the application will continue its execution from
very next point after catch block. In some situations, a given exception may be critical
and it may warrant the termination of the application. However, in most of the cases,
the logic within the exception handler will ensure the application to be continued in
very normal way.
The TargetSite Property

The System.Exception.TargetSite property allows to determine various details
about the method that threw a given exception.

Printing the value of TargetSite will display the return type, name, and
parameters of the method that threw the exception.

However, TargetSite does not simply return a string, but a strongly typed
System.Reflection.MethodBase object.

This type can be used to gather numerous details regarding the offending
method as well as the class that defines the offending method.
The StackTrace Property
•
The System.Exception.StackTrace property allows you to identify the series of
calls that resulted in the exception.
•
Be aware that you never set the value of StackTrace as it is established
automatically at the time the exception is created.
•
The string returned from StackTrace documents the sequence of calls that
resulted in the throwing of this exception.
using System;
class Test
{
int Max=100;
public void Fun(int d)
{
try
{
if(d>Max)
throw new Exception(string.Format("crossed limit!!!"));
}
catch(Exception e)
{
Console.WriteLine("{0}",e.Message);
// crossed limit!!!
Console.WriteLine("{0}", e.TargetSite); //Void Fun(Int32)
Console.WriteLine("{0}",e.TargetSite.DeclaringType);
//Test
Console.WriteLine("{0}", e.TargetSite.MemberType); //Method
Console.WriteLine("Stack:{0}", e.StackTrace);
//at Test.Fun(Int32 d)
}
Console.WriteLine("d={0}", d);
}
public static void Main()
{
Test ob=new Test();
Console.WriteLine("Enter a number:");
int d=int.Parse(Console.ReadLine());
ob.Fun(d);
//assume d is 123
}
}
NOTE: The comment lines for the statements within the function Fun() are the outputs
for the input d greater than 100.
The Helplink Property
•
The HelpLink property can be set to point the user to a specific URL or
standard Windows help file that contains more detailed information.
•
By default, the value managed by the HelpLink property is an empty string.
•
If you wish to fill this property with an interesting value, you will need to do so
before throwing the System.Exception type.
using System;
class Test
{
int Max=100;
public void Fun(int d)
{
try
{
if(d>Max)
{
Exception ex= new Exception("crossed limit!!!");
ex.HelpLink="g:\\Help.doc";
throw ex;
}
}
catch(Exception e)
{
Console.WriteLine("{0}", e.HelpLink); // G:\Help.doc
}
Console.WriteLine("d={0}", d);
}
public static void Main()
{
Test ob=new Test();
Console.WriteLine("Enter a number:");
int d=int.Parse(Console.ReadLine());
//say d=345
ob.Fun(d);
}
}
1.32 CLR System Level Exceptions
The .NET base class libraries define many classes derived from System.Exception. For
example,
the
System
namespace
ArgumentOutOfRangeException,
defines
core
error
objects
such
as
IndexOutOfRange-Exception,
StackOverflowException etc. Other namespaces define exceptions that reflect the
behavior of that namespace. For ex. System.Drawing.Printing defines printing
exceptions, System.IO defines IO-based exceptions, System.Data defines databasecentric exceptions, and so on.
Exceptions that are thrown by the methods in the BCL are called system exceptions.
These exceptions are regarded as non-recoverable, fatal errors.
System exceptions
derive directly from a base class named System.SystemException, which in turn
derives from System.Exception (which derives from System.Object):
public class SystemException : Exception
{
// Various constructors.
}
The System.SystemException type does not add any additional functionality beyond a
set of constructors.
1.33 Custom Application Level Exceptions
All .NET exceptions are class types and hence we can create our own applicationspecific exceptions.
Since the System.SystemException base class represents
exceptions thrown from the CLR, we may naturally assume that we should derive your
custom exceptions from the System.Exception type. But, the best practice is to derive
from the System.ApplicationException type:
public class ApplicationException : Exception
{
// Various constructors.
}
The purpose of System.ApplicationException is to identify the source of the (nonfatal)
error. When we handle an exception deriving from System.ApplicationException, we
can assume that exception was raised by the code-base of the executing application
rather than by the .NET BCL. The relationship between exception-centric base classes
are shown –
System.Object
System.Exception
System.ApplicationException
Your Application’s
Custom Exceptions
System.SystemExceptio
n
Exceptions from
.NET BCL
Truly
speaking,
it
is
not
necessary
to
derive
a
custom
exception
from
System.ApplicationException class, rather it can directly be derived from more generic
System.Exception class. To understand various possibilities, now we will build custom
exceptions.
Building Custom Exceptions: Take One
We can always throw instances of System.Exception to indicate runtime error. But, it
is better to build a strongly typed exception that represents the unique details of our
current problem.
The first approach is defining a new class derived directly from
System.Exception. Like any class, the exception class also may include fields, methods
and properties that can be used within the catch block. We can also override any
virtual member in the parent class. For example, assume we wish to build a custom
exception (named CarIsDeadException) to represent that the car has crossed the
maximum speed limit.
public class CarIsDeadException : System.Exception
{
private string messageDetails;
public CarIsDeadException(){ }
public CarIsDeadException(string message)
{
messageDetails = message;
}
// Override the Exception.Message property.
public override string Message
{
get
{
return string.Format("Car Error Message: {0}", messageDetails);
}
}
}
public class Car
{
……………………
public void SpeedUp(int delta)
{
try
{
speed=current_speed + delta;
if(speed>max_speed)
throw new CarIsDeadException(“Ford Ikon”);
}
catch(CarIsDeadException e)
{
Console.WriteLine(“Method:{0}”, e.TargetSite);
Console.WriteLine(“Message:{0}”, e. Message);
}
}
……………………..
}
Note that, the custom exceptions are needed only when the error is tightly bound to
the user-defined class issuing the error. For example, a File class may throw number
of file-related errors; Student class may throw errors related to student information
and so on. By writing the custom exception, we provide the facility of handling
numerous exceptions on a name-by-name basis.
Building Custom Exceptions: Take Two
We can write the constructors, methods and overridden properties as we wish in our
exception class. But it is always recommended approach to build a relatively simple
type that supplied three named constructors matching the following signature –
public class CarIsDeadException : System.Exception
{
public CarIsDeadException(){ }
public CarIsDeadException(string message):base(message)
{
}
public CarIsDeadException(string message, Exception
innerEx):base(message, innerEx)
{
}
}
Most of the user defined exceptions follow this pattern. Because, many times, the role
of a custom exception is not to provide additional functionality beyond what is
provided by its base class. Rather, to provide a strongly named type that clearly
identifies the nature of the error.
Building Custom Exceptions: Take Three
We have discussed that; exceptions can be system-level or application-level. If we want
to clearly mark our exception to be thrown by the application itself and not by any
chance by BCL, we can redefine as –
public class CarIsDeadException: ApplicationException
{
//body
}
1.34 Handling Multiple Exceptions
We have seen a situation where a try block will have corresponding catch block. But in
reality, we may face a situation where the code within try block may trigger multiple
possible exceptions. For example,
public void SpeedUp(int delta)
{
try
{
if(delta<0)
throw new ArgumentOutOfRangeException(“Ford Ikon”);
speed=current_speed + delta;
if(speed>max_speed)
throw new CarIsDeadException(“Ford Ikon”);
}
catch(CarIsDeadException e)
{
Console.WriteLine(“Method:{0}”, e.TargetSite);
Console.WriteLine(“Message:{0}”, e. Message);
}
catch(ArgumentOutOfRangeException e)
{
Console.WriteLine(“Method:{0}”, e.TargetSite);
Console.WriteLine(“Message:{0}”, e. Message);
}
}
In the above example, if delta is less than zero, then ArgumentOutOfRangeException is
triggered. If the speed exceeds MaxSpeed, then CarIsDeadException is thrown. While
constructing multiple catch blocks for a single try block, we must be aware that it will
be processed by the nearest available catch block. For example, the following code
snippet generates compile-time error!!
try
{
---------}
catch(Exception e)
{
-------------}
catch(CarIsDeadException e)
{
-------------}
catch(ArgumentOutOfRangeException e)
{
-------------}
Here, the class Exception is a base class for all user-defined and built-in exceptions.
Hence, it can handle any type of exceptions. Thus, if Exception is the first catch-block,
the control will jump to that block itself and the other two exceptions are unreachable.
Thus, during multiple exceptions, we have to design very structured format. The very
first catch block should contain very specific exception or the most derived type in
inheritance hierarchy. Whereas the last catch block should be the most general or the
top most base class in inheritance hierarchy.
Generic Catch Statements
C# supports a generic catch block that does not explicitly define the type of exception.
That is, we can write –
catch
{
Console.WriteLine(“Some error has occurred”);
}
But, using this type of catch blocks indicates that the programmer is un-aware of the
type of exception that is going to occur, which is not acceptable. Hence it is always
advised to use specific type of exceptions.
Re-throwing Exceptions
It is possible to re-throw an error to the previous caller. For example–
try
{
---------------}
catch(CarIsDeadException e)
{
//handle CarIsDeadException partially or do something
throw e;
}
In the above code, the statement throw e will throw the exception again to
CarIsDeadException.
1.35 The finally Block
The try/catch block may be added with an optional Finally block. The finally block
contains a code that always executes irrespective of any number of exceptions that
may interfere with the normal flow of execution. For example, we may want to turn-off
radio within a car before stopping the car due to any reason. Programmatically
speaking, before exiting from Main() function, we may need to turn-off radio of a car.
Then we can write –
public static void Main()
{
try
{
//increase speed
}
catch(CarIsDeadException e)
{
----------}
catch(ArgumentOutOfRangeException e)
{
-------------}
finally
{
CarName.TurnOffRadio();
}
}
NOTE:

The finally block is useful in following situations –
When we want to

–
clean-up any allocated memory
–
Close a file which is in use
–
Close a database connection to a data source
The finally block will be executed even if our try block does not trigger any
exception.

This is especially helpful if a given exception requires the termination of the
current application.
1.36 Dynamically Identifying Application and System
Level Exceptions
In the previous examples, we have handled each exception by its specific class name.
lternatively, if we want to generalize our catch blocks in a such a way that all
application level exceptions are handled apart from possible system-level exceptions:
try
{
//do something
}
catch(ApplicationException e)
{
--------------}
catch(SystemException e)
{
---------------
}
Though C# has the ability to discover at runtime the underlying source of an
exception, we are gaining nothing by doing so. Because some BCL methods that
should ideally throw a type derived from System.SystemException are actually derived
from System.ApplicationException or even more generic System.Exception.
1.37 Summary
This unit examined one of the OOP-centric topic viz. exceptions. You were shown how
the .NET runtime makes use of structured exception handling to contend with runtime
problems. Using a small set of keywords (try, catch, throw and finally), C#
programmers are able to raise and handle system-level and application-level
exceptions.
1.38 Keywords

Bugs, Errors, Exceptions

System.Exception Base Class and its properties

Generic exception

The keywords try, catch, throw and finally

System level and Application Level exceptions

Building Custom/User-defined exceptions

Multiple exceptions

Re-throwing Exceptions
1.39 Exercises
1. What
do
you
understand
by
exception
in
C#?
Illustrate
the
use
of
System.Exception base class in throwing generic exceptions.
2. Differentiate between bugs, errors and exceptions. Explain the concepts of .NET
exception handling with valid example code.
3. Explain the following properties: TargetSite, StackTrace, HelpLink.
4. Explain the concept of re-throwing the exception.
5. What do you mean by custom exception? Write a program to build a custom
exception which raises an exception when the argument passed is a negative
number.
6. Briefly explain the usage of finally block.
Module 3
Unit 2
CONTENTS:
1.45
2.26
2.27
2.28
2.29
2.30
2.31
2.32
2.33
2.34
2.35
2.36
2.37
Objectives
Introduction
Understanding Object Lifetime
The CIL of new
The Basics of Garbage Collection
Finalization a type
The Finalization Process
Building an AD Hoc Destruction Method
Garbage Collection Optimizations
The System. GC Type
Summary
Keywords
Exercises
2.1 Objectives
At the end of this lesson, the students will understand the following tasks in detail:

The concept of Garbage Collection and need for it

Idea about heap memory, meaning of new during object creation

Meaning of finalization and the method Finalize()

Usage of IDisposable Interface

The System.GC class
2.25 Introduction
This unit examines the process of object lifetime management as handled by the CLR.
As you will see, the .NET runtime destroys objects in a rather nondeterministic nature.
Thus, you typically do not know when a given object will be de-allocated from the
managed heap, only that it will come to pass.
On this note, you will come to
understand how the System.Object.Finalize() method and IDisposable interface can be
used to interact with an object’s lifetime management. Finally, we wrap up with an
examination of the System.GC type, and illustrate a number of ways you are able to
get involved with the garbage collection process.
2.26 Understanding Object Lifetime
By the example programs we have seen in previous four chapters, we can note that we
have never directly de-allocated an object from memory. That is there is no delete
keyword in C#. Rather, .NET objects are allocated onto a region of memory termed as
managed heap, where they will be automatically de-allocated by the CLR at some
time later.
Thus, the golden rule of .NET memory management is –
Allocate an object onto the managed heap using the new keyword and forget
about it.
Once an object is created using new, the CLR removes it when it is no longer needed.
That is, the CLR removes an object from the heap when it is unreachable by the
current application. For example,
public static void Main()
{
Car c=new Car(“Ford Ikon”);
………………
}
Here, c is created within the scope of Main(). Thus, once the application shuts down,
this reference is no longer valid and therefore it is a candidate for garbage collection.
But, we can not surely say that the object c is destroyed immediately after Main()
function. All we can say is when CLR performs the next garbage collection, c is ready
to be destroyed.
2.27 The CIL of new
Here we will discuss what actually happens when new keyword is used.

When C# encounters the new keyword, it will produce a CIL newobj instruction to
the code module.

Note that, the managed heap is not just a raw portion of memory accessed by the
CLR.

The .NET garbage collector will compact empty blocks of memory, when needed, for
the purpose of optimization.

To help this process, the managed heap maintains a pointer, referred as new
object pointer that identifies exactly where the next object will be placed on the
heap.

After these tasks, the newobj instruction informs the CLR to perform the following
sequence of events:

Calculate the total amount of memory required for the object to be allocated.
If this object contains other internal objects (i.e. has-a relationship and/or
nested type member), they are also added-up. And the memory required for
each base class is also considered (i.e is-a relationship).

The CLR then examines the managed heap to ensure that there is enough
space for the object to be allocated. If so, the object’s constructor is called
and a reference to the object in the memory is returned.

Finally, before returning the reference, the CLR will move the new object
pointer to point to the next available slot on the managed heap.
The entire process is depicted here-under:
c1
c2
c3
Unused Heap
Space
Car c1=new Car(“Ford”);
New object pointer
Car c2=new Car(“Swift”);
Car c3=new Car(“Zen”);
2.28 The Basics of Garbage Collection

After creating so many objects, the managed heap may become full.

When the newobj instruction is being processed, if the CLR determins that the
managed heap does not have sufficient memory to allocate the requested type, it
will perform a garbage collection in an attempt to free-up the memory.

Thus, the next rule of garbage collection is:
o
If the managed heap does not have sufficient memory to allocate a
new object, a garbage collection will occur.

Now the question arise: how CLR is able to determine an object on the heap that it
is no longer needed and destroy it?

To answer this question, we should know application roots.

A root is a storage location containing a reference to an object on the heap.

In other words, a root is a variable in our application that points to some area of
memory on the managed heap.

The root can fall into any of the following categories:
–
Reference to global objects (Though global objects are not allowed in C#,
raw CIL does permit allocation of global objects)

–
Reference to static objects
–
References to local objects within a given method
–
References to object parameters passed into a method
–
Any CPU register that references a local object
When a garbage collection occurs, the runtime (CLR) will check all objects on the
managed heap to determine if it is still in use (or rooted) in the application.

To do so, the CLR will build an object graph, which represents each object on the
heap that is still reachable.

The object graph documents all co-dependencies for the current object.

The CLR will ensure that all related objects are considered before a possible
garbage collection through the construction of an object graph.

The CLR will never graph the same object twice, and thus avoids the circular
reference count.
To illustrate this concept, assume that the managed heap contains a set of objects
named A, B, C, D, E, F, and G. During a garbage collection, these objects (as well as
any internal object references they may contain) are examined for active roots. Once
the graph has been constructed, unreachable objects, say, the objects C and F are
marked as garbage as shown in the following diagram:
Managed Heap
A
B
C
D
E
F
G
New Object pointer
Now, a possible object graph may look like –
A
B
G
D
E
Here, the arrows indicate depends on or requires. For example, E depends on G, B
depends on E and also G, A is not depending on anything etc. Once an object has
been marked for collection (here, C and F), they are not considered for object graph
creation and are swept from the memory. At this point, the remaining space on the
heap is compacted, which in turn will cause the CLR to modify the set of active
application roots to refer to the correct memory location (this is done automatically
and transparently). Then, the new object pointer is readjusted to point to the next
available slot. Following diagram depicts it –
Managed Heap
A
B
D
E
G
New Object pointer
2.29 Finalizing a Type
From the previous discussion, we can easily make out that the .NET garbage collection
scheme is non-deterministic in nature. In other words, we can not determine exactly
when an object will be de-allocated from the memory. This approach seems to be quite
good because, we, the programmers need not worry once the object has been created.
But, there is a possibility that the objects are holding unmanaged resources (Win32
files etc) longer than necessary.
When we build .Net types that interact with unmanaged resources, we like to ensure
that this resource is released in-time rather than waiting for .NET garbage collector. To
facilitate
this,
the
C#
provides
System.Object.Finalize() method.
does nothing!!
an
option
for
overriding
the
The default implementation of Finalize() method
Note that we need to override it only if we are making use of
unmanaged resources. Otherwise, the C# garbage collector will do the job.
C# will not allow the programmer to directly override the Finalize() method.
public class Test
{
virtual
protected override void Finalize() //error!!!
{
……….
}
}
Rather, we need to use a C++ -type destructor syntax:
public class Test
{
~Test()
{
………
}
}
Indirectly Invoking System.Object.Finalize()
We have discussed till now that the .NET runtime will trigger garbage collector when it
requires more memory that what is available at heap. But also note that, the
finalization
will
automatically
take
AppDomain is unloaded by CLR.
place
when
an
application
domain
or
Application domain can be assumed to be
Application itself. Thus, once our application is about to shut down, the finalize logic
is triggered.
Thus, the next rule of garbage collection is:
When an AppDomain is unloaded, the Finalize() method is invoked for
all finalizable objects.
For illustration, consider the following example –
using System;
class Test
{
public Test()
{}
~Test()
{
Console.WriteLine("Finalizing!!!");
}
public static void Main()
{
Console.WriteLine(“Within Main()");
Test t=new Test();
Console.WriteLine(“Exiting Main()");
}
}
Output:
Within Main()
Exiting Main()
Finalizing!!!
We can see that, once the program control goes out the scope of Main() function, the
destructor is called for the object t. In the destructor or the finalizer, we need to write
the code to release the resources that may be held by the object t.
2.30 The Finalization Process
Though finalizer seems to be good to use, it is not advised unless the objects in our
application are using unmanaged resources. The good programming practice is to
avoid Finalize() method, as finalization takes time.

When an object is placed on a heap using new, the CLR automatically determines
whether this object supports a user-defined Finalize() method.

If yes, the object is marked as finalizable and a pointer to this object is stored on
an internal queue names as the finalization queue.

The finalization queue is a table maintained by the CLR that points to every object
that must be finalized before it is removed from the heap.

When the garbage collector starts its action, it checks every entry on the
finalization queue and copies the object from the heap to another CLR-managed
structure termed as finalization reachable table (f-reachable).

At this moment, a separate thread is produced to invoke the Finalize() method for
each object on the f-reachable table at the next garbage collection.

Thus, when we build a custom-type (user-defined type) that overrides the
System.Object.Finalize() method, the .NET runtime will ensure that this member is
called when our object is removed from the managed heap.

But this will consume time and hence affects the performance of our application.
2.31 Building an Ad Hoc Destruction Method
We have seen that the objects holding unmanaged resources can be destroyed using
Finalize() method. But the process of finalization is time consuming. C# provides an
alternative way to avoid this problem of time consumption.
The programmer can write a custom ad hoc method that can be invoked manually
before the object goes out of the scope. This will avoid the object being placed at
finalization queue and avoid waiting for garbage collector to clean-up. The userdefined method will take care of cleaning up the unmanaged resources.
public class Car
{
…………..
public void Kill()
//name of method can be anything
{
//clean up unmanaged resources
}
}
The IDisposable Interface
In order to provide symmetry among all objects that support an explicit destruction,
the .NET class libraries define an interface named IDisposable. This interface
contains a single member Dispose():
public interface IDisposable
{
public void Dispose();
}
Now, our application can implement this interface and define Dispose() method. Then,
this method can be called manually to release unmanaged resources. Thus, we can
avoid the problems with finalization.
public class Car: IDisposable
{
………
public void Dispose()
{
//code to clean up unmanaged resources
}
}
class Test
{
………….
public static void Main()
{
Car c=new Car(“Ford”);
……….
c.Dispose();
………
}
//c still remains on heap and may be collected by GC now
}
Thus, another rule for working with garbage collection is:
Always call Dispose() for any object in the heap. The assumption is: if
there is a Dispose() method, the object has some clean up to perform.
Note that, for a single class, it is possible to have C# - style destructor and also to
implement IDisposable interface for defining Dispose() method.
Reusing the C# using Keyword
When we are using an object that implements IDisposable, it is quite common to use
structured exceptions just to ensure the Dispose() method is called when exception
occurs:
public void Test()
{
Car c=new Car();
try
{
……..
}
catch{ …….. }
finally
{
……..
c.Dispose();
}
}
C# provides another way of doing this with the help of using keyword:
public void Test()
{
using(Car c=new Car())
{
//Do something
//Dispose() method is called automatically when this block exits
}
}
One good thing here is, the Dispose() method is called automatically when the program
control comes out of using block. But there is a disadvantage: If at all the object
specified at using does not implement IDisposable, then we will get compile-time error.
2.32 Garbage Collection Optimizations
Till now we have seen two methodologies for cleaning user-defined types. Now, we will
discuss little deeply about the functionality of .NET garbage collector.

When CLR is attempting to locate unreachable objects, it does not literally search
through every object placed on the managed heap looking for orphaned roots.

Because, doing so will consume more time for larger applications.

To optimize the collection process, each object on the heap is assigned to a given
generation.

The idea behind generation is as follows:
o
If an object is on the heap since long time, it means, the object will continue
to exist for more time. For example, application-level objects.
o
Conversely, if an object has been recently placed on the heap, it may be
dereferenced by the application quickly. For example, objects within a scope
of a method.

Based on these assumptions, each object belongs to one of the following
generations:
o
Generation 0: Identifies a newly allocated object that has never been
marked for collection.
o
Generation 1: Identifies an object that has survived a garbage collection
sweep (i.e. it was marked for collection, but was not removed due to the fact
that the heap had enough free space)
o
Generation 2: Identifies an object that has survived more than on sweep of
the garbage collector.

Now, when a collection occurs, the GC marks and sweeps all generation 0 objects
first.

If required amount of memory is gained, the remaining objects are promoted to the
next available generation.
To illustrate how an object’s generation affects the collection process, consider the
following diagram –
Gen 0
A
B
Gen 1
C
D
E
F
G
A

B
E
If all generation 0 objects have been removed from heap and still more memory is
necessary, generation 1 objects are checked for their reachability and collected
accordingly.

Surviving generation 1 objects are then promoted to generation 2.

If the garbage collector still requires additional memory, generation 2 objects are
checked for their reachability.

At this point, if generation 2 objects survive a garbage collection, they remain at
that generation only.

Thus, the newer objects (local variables) are removed quickly and older objects
(application level variables) are assumed to be still in use.

This is how, the GC is able to quickly free heap space using the generation as a
baseline.
2.33 The System.GC Type
The programmer can interact with the garbage collector using a base class
System.GC. This class provides following members:
System.GC Member
Meaning
Collect()
Forces the GC to perform a garbage collection.
GetGeneration()
Returns the generation to which an object
currently belongs.
GetTotalMemory()
Returns the estimated amount of memory (in
bytes) currently allocated on the managed
heap. The Boolean parameter specifies
whether the call should wait for garbage
collection to occur before returning.
MaxGeneration
Returns
the
maximum of generations
supported on the target system. Under
Microsoft’s .NET 2.0, there are three possible
generations (0, 1, and 2).
SuppressFinalize()
Sets a flag indicating that the specified object
should not have its Finalize() method called.
WaitForPendingFinalizers()
Suspends the current thread until all
finalizable objects have been finalized. This
method is typically called directly after
invoking GC.Collect().
Building Finalization and Disposable Types
Consider an example to illustrate how to interact with .NET garbage collector.
using System;
class Car:IDisposable
{
string name;
public Car(string n)
{
name=n;
}
~Car()
{
Console.WriteLine("Within destructor of {0}", name);
}
public void Dispose()
{
Console.WriteLine("Within Dispose() of {0}", name);
GC.SuppressFinalize(this);
}
}
class Test
{
public static void Main()
{
Car c1=new Car("One");
Car c2=new Car("Two");
Car c3=new Car("Three");
Car c4=new Car("Four");
c1.Dispose();
c3.Dispose();
}
}
Output:
Within Dispose() of One
Within Dispose() of Three
Within destructor of Four
Within destructor of Two
We have discussed earlier that both Dispose() and Finalize() (or destructor) methods
are used to release the unmanaged resources. As we can see in the above example,
when Dispose() method is invoked through an object, we can prevent the CLR from
calling the corresponding destructor with the help of SuppressFinalize() method of GC
class. By manually calling Dispose() method, we are releasing the resources and hence
there is no need to call finalizer.
Calling the Dispose() function manually is termed as explicit object de-allocation
and making use of finalizer is known as implicit object de-allocation.
Forcing Garbage Collection
We know that, CLR will automatically trigger a garbage collection when a managed
heap is full. We, the programmers, will not be knowing, when this process will happen.
However, if we wish, we can force the garbage collection to occur using the following
statements:
GC.Collect();
GC.WaitForPendingFinalizers();
The method WaitForPendingFinalizers() will allow all finalizable objects to perform any
necessary cleanup before getting destroyed. Though, we can force garbage collection to
occur, it is not a good programming practice.
Interacting with Generations
It is possible to find generation of every object at any moment of time in the program.
class Car:IDisposable
{
string name;
public Car(string n)
{
name=n;
}
~Car()
{
Console.WriteLine("Within destructor of {0}", name);
}
public void Dispose()
{
Console.WriteLine("Within Dispose() of {0}", name);
GC.SuppressFinalize(this);
}
}
class Test
{
public static void Main()
{
Car c1=new Car("One");
Car c2=new Car("Two");
Car c3=new Car("Three");
Car c4=new Car("Four");
Console.WriteLine("c1 is Gen {0}", GC.GetGeneration(c1));
Console.WriteLine("c2 is Gen {0}", GC.GetGeneration(c2));
Console.WriteLine("c3 is Gen {0}", GC.GetGeneration(c3));
Console.WriteLine("c4 is Gen {0}", GC.GetGeneration(c4));
c1.Dispose();
c3.Dispose();
GC.Collect(0);
Console.WriteLine("c1 is Gen {0}", GC.GetGeneration(c1));
Console.WriteLine("c2 is Gen {0}", GC.GetGeneration(c2));
Console.WriteLine("c3 is Gen {0}", GC.GetGeneration(c3));
Console.WriteLine("c4 is Gen {0}", GC.GetGeneration(c4));
}
}
Output would be –
C1 is Gen 0
C2 is Gen 0
C3 is Gen 0
C4 is Gen 0
Within Dispose() of One
Within Dispose() of Three
C1 is Gen 1
C2 is Gen 1
C3 is Gen 1
C4 is Gen 1
Within Destructor of Four
Within Destructor of Two
Notice that when you request a collection of generation 0, each object is promoted to
generation 1, given that these objects did not need to be removed from memory (as the
heap was not exhausted). Also note that if you request a collection of generation 2
objects, the object that have survived the current garbage collection remain at
generation 2.
2.34 Summary
In this unit, we have exposed to various aspects of object lifetime. Given hat the CLR
makes use of runtime garbage collections, the basic rule of thumb is to assume that
the runtime will destroy as object when it is no longer needed. However, if your C#
types make use of unmanaged resources that need to be freed in a timely and
predictable manner, you are free to override the virtual System.Object.Finalize()
method and/or implement the IDisposable interface. Finally, you were exposed to be
role of the System.GC type and checked out a subset of its functionality.
2.35 Keywords

Managed Heap

Candidate for garbage collection

newobj Instruction

New Object Pointer

Application Roots

Object Graph

Unmanaged resources

Application Domain (AppDomain)

Finalizable and Finalization Queue

F-reachable table

Finalize() method

IDisposable Interface

The new format of using keyword

Generation

The System.GC class and its members

Explicit and implicit object de-allocation
2.36 Exercises
14. How does .NET framework manage garbage collection? Explain using IDisposable
interface.
15. Explain how garbage collection is optimized in .NET?
16. What are the rules of garbage collection?
17. Define root and explain the role of application roots in garbage collection.
18. Explain the concept of object graph with an example.
19. What is f-reachable table? Explain.
20. Briefly explain the use of using keyword with respect to garbage collection.
21. Explain the concept of generations.
22. When do you override the virtual System.Object.Finalize() method? How to
implement it using destructors?
Module 3
Unit 3
CONTENTS:
1.46
3.2
Objectives
Introduction
3.3 Defining Interfaces Using C#
3.4 Invoking Interface Members at the object level
3.5 Exercising the shapes hierarchy
3.6 Understanding Explicit Interface Implementation
3.7 Interfaces as Polymorphic Agents
3.8
Building Interface Hierarchies
3.9 Summary
3.10
Keywords
3.11
Exercises
3.1 Objectives
At the end of this lesson, the students will understand the following tasks in detail:

Meaning of interface

Writing user-defined/custom interface

Defining methods of interface within the implemented classes

Obtaining interface references using three different ways

Passing interfaces as parameters to methods

Explicit interface implementation

Interfaces as polymorphic agents

Building interfaces hierarchies

Interfaces with multiple base interfaces
3.24 Introduction
This unit introduces the topic of interface-based programming. Here, you learn how to
use C# to define and implement custom interfaces, and come to understand the
benefits of building types that support multiple behaviors. Along the way, a number of
related topics are also discussed such as obtaining interface references, explicit
interface implementation and the construction of interface hierarchies.
3.25 Defining Interfaces using C#
An interface is nothing more than a named collection of semantically related abstract
members. The specific members defined by an interface depend on the exact behavior
it is modeling. An interface expresses a behavior that a given class or structure may
choose to support. At a syntactic level, an interface is defined using the C# interface
keyword. Unlike other .NET types, interfaces never specify a base class (not even
System.Object) and contain members that do not take an access modifier (as all
interface members are implicitly public). Following is an example showing a custom
interface defined in C#:
// This interface defines the behavior of "having points."
public interface IPointy
{
// Implicitly public and abstract.
byte GetNumberOfPoints();
}
As you can see, the IPointy interface defines a single method. However, .NET interface
types are also able to define any number of properties. For example, you could create
the IPointy interface to use a read-only property rather than a traditional accessor
method:
// The pointy behavior as a read-only property.
public interface IPointy
{
byte Points { get; }
}
Do understand that interface types are quite useless on their own, as they are nothing
more than a named collection of abstract members. Given this, you cannot allocate
interface types as you would a class or structure:
// Ack! Illegal to "new" interface types.
static void Main(string[] args)
{
IPointy p = new IPointy(); // Compiler error!
}
Interfaces do not bring much to the table until they are implemented by a class or
structure. Here, IPointy is an interface that expresses the behavior of “having points.”
As you can tell, this behavior might be useful in the shapes hierarchy developed in
Unit 4 of Module 2. The idea is simple: Some classes in the Shapes hierarchy have
points (such as the Hexagon), while others (such as the Circle) do not. If you configure
Hexagon and Triangle to implement the IPointy interface, you can safely assume that
each class now supports a common behavior, and therefore a common set of
members.
Implementing an Interface using C#
When a class (or structure) chooses to extend its functionality by supporting interface
types, it does so using a comma-delimited list in the type definition. Be aware that the
direct base class must be the first item listed after the colon operator. When your class
type derives directly from System.Object, you are free to simply list the interface(s)
supported by the class, as the C# compiler will extend your types from System.Object
if you do not say otherwise. On a related note, given that structures always derive
from System.ValueType, simply list each interface directly after the structure
definition. Consider the following examples:
// This class derives from System.Object and implements single interface.
public class SomeClass : ISomeInterface
{...}
//Class derives from custom base class and implements a single interface.
public class AnotherClass : MyBaseClass, ISomeInterface
{...}
// This struct derives from System.ValueType and implements two //interfaces.
public struct SomeStruct : ISomeInterface, IPointy
{...}
Understand that implementing an interface is an all-or-nothing proposition. That is,
one has to define all the members of interface in an implemented class. We can not
selectively choose which members it will implement. Given that the IPointy interface
defines a single property, this is not too much of a burden. However, if you are
implementing an interface that defines ten members, the type is now responsible for
fleshing out the details of all the ten abstract entities. Here is the implementation of
the updated shapes hierarchy (note the new Triangle class type):
// Hexagon now implements IPointy.
public class Hexagon : Shape, IPointy
{
public Hexagon(){ }
public Hexagon(string name) : base(name){ }
public override void Draw()
{
Console.WriteLine("Drawing {0} the Hexagon", PetName);
}
// IPointy Implementation.
public byte Points
{
get { return 6; }
}
}
// New Shape derived class named Triangle.
public class Triangle : Shape, IPointy
{
public Triangle() { }
public Triangle(string name) : base(name) { }
public override void Draw()
{
Console.WriteLine("Drawing {0} the Triangle", PetName);
}
// IPointy Implementation.
public byte Points
{
get { return 3; }
}
}
Each class now returns its number of points to the caller when asked to do so.
Following figure illustrates IPointy-compatible classes.
Object
Shape
IPointy
Hexagon
Circle
IPointy
Triangle
Contrasting Interfaces to Abstract Base Classes
By understanding abstract classes earlier, and interfaces now, a question may arise:
what is the need of interface types? After all, C# already allows you to build abstract
class types containing abstract methods. Like an interface, when a class derives from
an abstract base class, it is also under obligation to define the abstract methods
(provided the derived class is not declared abstract as well). However, abstract base
classes do far more than define a group of abstract methods. They are free to define
public, private, and protected state data, as well as any number of concrete methods
that can be accessed by the subclasses.
Interfaces, on the other hand, are pure protocol. Interfaces never define state data and
never provide an implementation of the methods (if you try, you receive a compile-time
error):
public interface IAmABadInterface
{
// Error, interfaces can't define data!
int myInt = 0;
// Error, only abstract members allowed!
void MyMethod()
{
Console.WriteLine("Hi!");
}
}
Interface types are also quite helpful given that C# (and .NET-aware languages in
general) only support single inheritance; the interface-based protocol allows a given
type to support numerous behaviors, while avoiding the issues that arise when
deriving from extending multiple base classes. Most importantly, interface-based
programming provides yet another way to inject polymorphic behavior into a system. If
multiple classes (or structures) implement the same interface in their unique ways,
you have the power to treat each type in the same manner. As you will see a bit later
in this chapter, interfaces are extremely polymorphic, given that types that are not
related via classical inheritance can support identical behaviors.
3.26 Invoking Interface Members at the Object Level
Once the methods declared in interfaces are defined within the implemented class, one
can invoke that method using the object of the class. For example:
public static void Main(string[] args)
{
// Call new Points member defined by IPointy
Hexagon hex = new Hexagon();
Console.WriteLine("Points: {0}", hex.Points);
}
This approach works fine in this particular case, given that you are well aware that the
Hexagon type has implemented the interface in question. Other times, however, you
will not be able to determine at compile time which interfaces are supported by a given
type. For example, assume you have an array containing 50 Shape-compatible types,
only some of which support IPointy. Obviously, if you attempt to invoke the Points
property on a type that has not implemented IPointy, you receive a compile-time error.
Next question: How can we dynamically determine the set of interfaces supported by a
type? This question can be answered in three different ways as discussed in
subsequent sections.
Obtaining Interface References: Explicit Casting
The first way you can determine at runtime if a type supports a specific interface is to
make use of an explicit cast. If the type does not support the requested interface, you
receive an InvalidCastException. To handle this, make use of structured exception
handling, for example:
public static void Main(string[] args)
{
......
// Catch a possible InvalidCastException
Circle c = new Circle("Lisa");
IPointy ipt;
try
{
ipt = (IPointy)c;
//boxing or explicit casting
Console.WriteLine(ipt.Points);
}
catch (InvalidCastException e)
{
Console.WriteLine(e.Message);
}
}
While you could make use of try/catch logic and hope for the best, it would be ideal to
determine which interfaces are supported before invoking the interface members in
the first place. So, let’s see the remaining two ways of doing so.
Obtaining Interface References: The as Keyword
The second way you can determine whether a given type supports an interface is to
make use of the as keyword, which was first introduced in Chapter 4. If the object can
be treated as the specified interface, you are returned a reference to the interface in
question. If not, you receive a null reference:
public static void Main(string[] args)
{
...
// Can we treat hex2 as IPointy?
Hexagon h = new Hexagon("Ram");
IPointy ipt = h as IPointy;
if(ipt != null)
Console.WriteLine("Points: {0}", ipt.Points);
else
Console.WriteLine("OOPS! Not pointy...");
}
Notice that when you make use of the as keyword, you have no need to make use of
try/catch logic, given that if the reference is not null, you know you are calling on a
valid interface reference.
Obtaining Interface References: The is Keyword
Finally, you may also obtain an interface from an object using the is keyword. If the
object in question is not IPointy compatible, the condition fails. For example,
//third way to test for an interface
Triangle t = new Triangle();
if(t is IPointy)
Console.WriteLine(t.Points);
else
Console.WriteLine(“OOPS! Not pointy...");
3.27 Exercising the Shapes Hierarchy
In these previous examples, you could have avoided checking the outcome of asking
for the IPointy reference, given that you knew ahead of time which shapes were IPointy
compatible. However, what if you were to create an array of generic Shape references,
each of which has been assigned to a given subclass? You may make use of any of the
previous techniques to discover at runtime which items in the array support this
behavior. For example,
public static void Main(string[] args)
{
...
Shape[ ] s = { new Hexagon(), new Circle(), new Triangle("Ram"), new
Circle("Sham")} ;
for(int i = 0; i < s.Length; i++)
{
// Recall the Shape base class defines an abstract Draw()
// member, so all shapes know how to draw themselves.
s[i].Draw();
if(s[i] is IPointy)
Console.WriteLine("Points: {0} ", ((IPointy)s[i]).Points);
else
Console.WriteLine("{0} is not a pointy!", s[i].PetName);
}
}
Interfaces As Parameters
Given that interfaces are valid .NET types, you may construct methods that take
interfaces as parameters. To illustrate, assume you have defined another interface
named IDraw3D:
// Models the ability to render a type in stunning 3D.
public interface IDraw3D
{
void Draw3D();
}
Next, assume that two of your three shapes (Circle and Hexagon) have been configured
to support this new behavior:
// Circle supports IDraw3D
public class Circle : Shape, IDraw3D
{
...
public void Draw3D()
{
Console.WriteLine("Drawing Circle in 3D!");
}
}
// Hexagon supports IPointy and IDraw3D
public class Hexagon : Shape, IPointy, IDraw3D
{
...
public void Draw3D()
{
Console.WriteLine("Drawing Hexagon in 3D!");
}
}
If you now define a method taking an IDraw3D interface as a parameter, you are able
to effectively send in any object implementing IDraw3D (if you attempt to pass in a
type not supporting the necessary interface, you receive a compile-time error).
Consider the following:
// Make some shapes. If they can be rendered in 3D, do it!
public class Program
{
// I'll draw anyone supporting IDraw3D.
public static void DrawIn3D(IDraw3D itf3d)
{
Console.WriteLine("Drawing IDraw3D compatible type");
itf3d.Draw3D();
}
static void Main()
{
Shape[ ] s = { new Hexagon(), new Circle(), new Triangle()};
for(int i = 0; i < s.Length; i++)
{
…..
if(s[i] is IDraw3D)
DrawIn3D((IDraw3D)s[i]);
}
}
}
Notice that the triangle is never drawn, as it is not IDraw3D-compatible
3.28 Understanding Explicit Interface Implementation
In our current definition of IDraw3D, we were forced to name its sole method
Draw3D() in order to avoid clashing with the abstract Draw() method defined in the
Shape base class. While there is nothing horribly wrong with this interface definition,
a more natural method name would simply be Draw():
// Re-factor method name from "Draw3D" to "Draw".
public interface IDraw3D
{
void Draw();
}
If we were to make such a change, this would require us to also update our
implementation of DrawIn3D().
public static void DrawIn3D(IDraw3D itf3d)
{
Console.WriteLine("Drawing IDraw3D compatible type");
itf3d.Draw();
}
Now, assume you have defined a new class named Line that derives from the abstract
Shape class and implements IDraw3D (both of which now define an identically named
abstract Draw() method):
public class Line : Shape, IDraw3D
{
public override void Draw()
{
Console.WriteLine("Drawing a line...");
}
}
The Line class compiles without a hitch. But consider the following Main() logic:
static void Main(string[] args)
{
...
// Calls Draw().
Line myLine = new Line();
myLine.Draw();
// Calls same implementation of Draw()!
IDraw3D itfDraw3d= (IDraw3D) myLine;
itfDraw3d.Draw();
}
Given what you already know about the Shape base class and IDraw3D interface, it
looks as if you have called two variations of the Draw() method (one from the object
level, the other from an interface reference). Nevertheless, the compiler is happy to call
the same implementation from an interface or object reference, given that the Shape
abstract base class and IDraw3D interface have an identically named member. This
would be problematic if you would like to have the IDraw3D.Draw() method to draw a
3D shape, while the overridden Shape.Draw() method draws a 2D. Now consider a
related problem. What if you wish to ensure that the methods defined by a given
interface are only accessible from an interface reference rather than an object
reference? Currently, the members defined by the IPointy interface can be accessed
using either an object reference or an IPointy reference. The answer to both questions
comes by way of explicit interface implementation. Using this technique, you are able to
ensure that the object user can only access methods defined by a given interface using
the correct interface reference, as well as circumvent possible name clashes. To
illustrate, here is the updated Line class (assume you have updated Hexagon and
Circle in a similar manner):
// Using explicit method implementation we are able to provide distinct
//Draw() implementations.
public class Line : Shape, IDraw3D
{
// this method can be called from an IDraw3D interface reference.
void IDraw3D.Draw()
{
Console.WriteLine("Drawing a 3D line...");
}
// You can only call this at the object level.
public override void Draw()
{
Console.WriteLine("Drawing a line...");
}
}
As you can see, when explicitly implementing an interface member, the general
pattern breaks down to returnValue InterfaceName.MethodName(args). There are a few
odds and ends to be aware of when using explicit interface implementation. First and
foremost, you cannot define the explicitly implemented members with an access
modifier. For example, the following is illegal syntax:
public class Line : Shape, IDraw3D
{
public void IDraw3D.Draw() // Error!
{
Console.WriteLine("Drawing a 3D line...");
}
...
}
This should make sense. The whole reason to use explicit interface method
implementation is to ensure that a given interface method is bound at the interface
level. If you were to add the public keyword, this would suggest that the method is a
member of the public sector of the class, whichdefeats the point! Given this design, the
caller is only able to invoke the Draw() method defined by the Shape base class from
the object level:
// This invokes the overridden Shape.Draw() method.
Line myLine = new Line();
myLine.Draw();
To invoke the Draw() method defined by IDraw3D, we must now explicitly obtain the
interface reference using any of the techniques shown previously. For example:
// This triggers the IDraw3D.Draw() method.
Line myLine = new Line();
IDraw3D i3d = (IDraw3D)myLine;
i3d.Draw();
3.29 Interfaces as Polymorphic Agents
As you know, an abstract base class containing abstract members allows us to define
a specific behavior that is common across all members in the same class hierarchy.
To understand the usefulness of interfaces, assume that the GetNumberOFPoints()
method was not defined by IPointy, but rather as an abstract member of the Shape
class. if this were the case, Hexagon and Triangle would still be able to return the
correct result. At the same time, Circle would also be obligated to contend with the
GetNumberOfPoints() method as well. In this case, however, it might seem a bit
inelegant to simply return 0.
The problem with defining GetNumberOFPoints() in the Shape base class is that all
derived types must contend with this member, regardless of the semantics. Thus, the
first key point about interfaces is the fact that you can select which members in the
hierarchy support custom behaviors. Also understand that the same interface can be
implemented by numerous types, even if they are not within the same class hierarchy
in the first place.
3.30 Building Interface Hierarchies
To continue our investigation of creating custom interfaces, let’s examine the topic of
interface hierarchies. Just as a class can serve as a base class to other classes (which
can in turn function as base classes to yet another class), it is possible to build
inheritance relationships among interfaces. As you might expect, the topmost interface
defines a general behavior, while the most derived interface defines more specific
behaviors. To illustrate, ponder the following interface hierarchy:
// The base interface.
public interface IDrawable
{
void Draw();
}
public interface IPrintable : IDrawable
{
void Print();
}
public interface IMetaFileRender : IPrintable
{
void Render();
}
Now, if a class wished to support each behavior expressed in this interface hierarchy,
it would derive from the nth-most interface (IMetaFileRender in this case). Any
methods defined by the base interface(s) are automatically carried into the definition.
For example:
// This class supports IDrawable, IPrintable, and IMetaFileRender.
public class SuperImage : IMetaFileRender
{
public void Draw()
{
Console.WriteLine("Basic drawing logic.");
}
public void Print()
{
Console.WriteLine("Draw to printer.");
}
public void Render()
{
Console.WriteLine("Render to metafile.");
}
}
Here is some sample usage of exercising each interface from a SuperImage instance:
// Exercise the interfaces.
static void Main(string[] args)
{
SuperImage si = new SuperImage();
// Get IDrawable.
IDrawable itfDraw = (IDrawable)si;
itfDraw.Draw();
// Now get ImetaFileRender, which exposes all methods up
// the chain of inheritance.
if (itfDraw is IMetaFileRender)
{
IMetaFileRender itfMF = (IMetaFileRender)itfDraw;
itfMF.Render();
itfMF.Print();
}
}
Interfaces with Multiple Base Interfaces
As you build interface hierarchies, be aware that it is completely permissible to create
an interface that derives from multiple base interfaces. Recall, however, that it is not
permissible to build a class that derives from multiple base classes. To illustrate,
assume you are building a new set of interfaces that model the automobile behaviors
for a particular English agent:
public interface ICar
{
void Drive();
}
public interface IUnderwaterCar
{
void Dive();
}
// Here we have an interface with TWO base interfaces.
public interface IJamesBondCar : ICar, IUnderwaterCar
{
void TurboBoost();
}
If you were to build a class that implements IJamesBondCar, you would now be
responsible for implementing TurboBoost(), Dive(), and Drive():
public class JamesBondCar : IJamesBondCar
{
public void Drive()
{
Console.WriteLine("Speeding up...");
}
public void Dive()
{
Console.WriteLine("Submerging...");
}
public void TurboBoost()
{
Console.WriteLine("Blast off!");
}
}
This specialized automobile can now be manipulated as you would expect:
static void Main(string[] args)
{
...
JamesBondCar j = new JamesBondCar();
j.Drive();
j.TurboBoost();
j.Dive();
}
3.31 Summary
Interface is a collection of abstract members that may be implemented by a given
class. Because an interface does not supply any implantation details, it is common to
regard an interface as a behavior that may be supported by a given type. When to or
more classes implement the same interface, you are able to treat each type in the
same way. This is known an interface-based polymorphism. C# provides the keyword
interface to allow you to define a new interface. A type can support as many interfaces
as necessary using comma-delimited list.
Furthermore, it is permissible to build
interfaces that derive from multiple base interfaces.
3.32 Keywords

Interfaces

Abstract Class

Abstract Members

The keyword is and as

Polymorphism
3.33 Exercises
10.
Define an interface. Bring out the differences between interface and abstract
base classes.
11.
How do you implement an interface? Explain with an example.
12.
Explain different techniques for invoking interface members at the object level.
13.
How do you pass interface as parameter to a method? Explain with an example.
14.
Explain the concept of interface-based polymorphism.
Module 3
Unit 4
CONTENTS:
1.47
4.2
Objectives
Introduction
4.3 Understanding the IConvertible Interface
4.4
Building a Custom Enumerator (IEnummerable and Enumerator)
4.5 Building Cloneable objects (ICloneable)
4.6 Building Comparable Objects (IComparable)
4.7 Exploring the System.Collections Namespace
4.8 Summary
4.9
Keywords
4.10
Exercises
6.1 Objectives
At the end of this lesson, the students will understand:

The members and implementation of inbuilt interfaces like IConvertible,
IEnumerable, Enumerator, ICloneable and IComparable

The members of System.Collections Namespace and their implementation by
the classes.
4.25 Introduction
The previous unit elaborated about the custom interfaces. Here, we will examine some
of the standard interfaces defined within the .NET base class libraries.
As we are
going to see, the custom types are free to implement these predefined interfaces to
support a number of advanced behaviors such as object cloning, object enumeration
and object sorting. To wrap things up, we will check out the numerous predefined
interfaces that are implemented by various collection classes (ArrayList, Stack etc.)
defined by System.Collections namespace.
4.26 Understanding the IConvertible Interface
The IConvertible type allows the programmer to dynamically convert between data
types using interface-based programming techniques. Using this interface, one can
cast between types using language-agnostic terms. We know that a majority of the
intrinsic data types of C# (bool, int, double etc) are simply aliases to the structures in
the System namespaces. Like any .NET type, each of these structures may define any
number of methods and may optionally implement any number of predefined
interfaces.
The IConvertible.ToXXXX() Members
The IConvertible interface defines a number of methods of the form ToXXXX(), which
provides a way to convert from one type into another. But sometimes, conversion may
not be possible. For example, converting from Int32 to Double make sense, whereas,
there is no sense in converting Boolean into DateTime. If the conversion is not
supported, then the system will throw InvalidCastException.
Note that, all of the ToXXXX() methods take a parameter of type IFormatProvider.
Objects that implement this interface are able to format their contents based on
culture-specific information. The formal definition would be–
public interface IFormatProvider
{
object GetFormat(Type formatType);
}
If we are building a custom type that should be formatted using various locals, then
we need to implement IFormatProvider.
IConvertible.GetTypeCode()
The GetTypeCode() method of IConvertible interface is available to any class or
structure which implements IConvertible. This method allows to programmatically
discover a value that represents the type code of the type, which is represented by the
following enumeration:
public enum TypeCode
{
Boolean, Byte, Char, DateTime, DBNull, Decimal, Double, Empty, Int16,
Int32, Int64, Object, SByte, Single, String, UInt16, UInt32, UInt64
}
The System.Convert Type
To wrap up this overview of the IConvertible type, it is worth pointing out that the
System namespace defines a type named Convert, which echoes the functionality of
the
IConvertible
interface.
The
System.Convert
does
not
directly
implement
IConvertible, however, the same set of members are defined on its default public
interface.
4.27 Building a Custom Enumerator (IEnumerable and
IEnumerator)
Here, we will examine the role of IEnumerable and IEnumerator. Assume you have
developed a class named Garage that contains a set of individual Car types stored
within a System.Array:
// Garage contains a set of Car objects.
public class Garage
{
private Car[] carArray;
// Fill with some Car objects upon startup.
public Garage()
{
carArray = new Car[4];
carArray[0] = new Car("Rusty", 30);
carArray[1] = new Car("Clunker", 55);
carArray[2] = new Car("Zippy", 30);
carArray[3] = new Car("Fred", 30);
}
}
Ideally, it would be convenient to iterate over the Garage object’s sub-items using the
C# foreach construct:
// This seems reasonable...
public class Program
{
static void Main(string[] args)
{
Garage carLot = new Garage();
foreach (Car c in carLot)
{
Console.WriteLine("{0} is going {1} MPH", c.PetName,
c.CurrSpeed);
}
}
}
Sadly, the compiler informs you that the Garage class does not implement a method
named GetEnumerator(). This method is formalized by the IEnumerable interface,
which is found lurking within the System.Collections namespace. Objects that support
this behavior advertise that they are able to expose contained subitems to the caller:
// This interface informs the caller that the object's subitems can // be
enumerated.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
As you can see, the GetEnumerator() method returns a reference to yet another
interface
named
System.Collections.IEnumerator.
This
interface
provides
the
infrastructure to allow the caller to traverse the internal objects contained by the
IEnumerable-compatible container:
// This interface allows the caller to obtain a container's subitems.
public interface IEnumerator
{
bool MoveNext ();
// Advance the internal position of the cursor.
object Current { get;}
// Get the current item (read-only property).
void Reset ();
// Reset the cursor before the first member.
}
If you wish to update the Garage type to support these interfaces, you could take the
long road and implement each method manually. While you are certainly free to
provide customized versions of GetEnumerator(), MoveNext(), Current, and Reset(),
there is a simpler way. As the System.Array type (as well as many other types) already
implements IEnumerable and IEnumerator, you can simply delegate the request to the
System.Array as follows:
using System.Collections;
...
public class Garage : IEnumerable
{
// System.Array already implements IEnumerator!
private Car[] carArray;
public Garage()
{
carArray = new Car[4];
carArray[0] = new Car("FeeFee", 200, 0);
carArray[1] = new Car("Clunker", 90, 0);
carArray[2] = new Car("Zippy", 30, 0);
carArray[3] = new Car("Fred", 30, 0);
}
public IEnumerator GetEnumerator()
{
// Return the array object's IEnumerator.
return carArray.GetEnumerator();
}
}
Once you have updated your Garage type, you can now safely use the type within the
C# foreach construct. Furthermore, given that the GetEnumerator() method has been
defined publicly, the object user could also interact with the IEnumerator type:
// Manually work with IEnumerator.
IEnumerator i = carLot.GetEnumerator();
i.MoveNext();
Car myCar = (Car)i.Current;
Console.WriteLine("{0} is going {1} MPH", myCar.PetName,
myCar.CurrSpeed);
If you would prefer to hide the functionality of IEnumerable from the object level,
simply make use of explicit interface implementation:
public IEnumerator IEnumerable.GetEnumerator()
{
// Return the array object's IEnumerator.
return carArray.GetEnumerator();
}
4.28 Building Cloneable Objects (ICloneable)
As you recall from earlier discussion, System.Object defines a member named
MemberwiseClone(). This method is used to obtain a shallow copy of the current
object. Object users do not call this method directly (as it is protected); however, a
given object may call this method itself during the cloning process. To illustrate,
assume you have a class named Point:
// A class named Point.
public class Point
{
// Public for easy access.
public int x, y;
public Point(int x, int y) { this.x = x; this.y = y;}
public Point(){}
// Override Object.ToString().
public override string ToString()
{
return string.Format("X = {0}; Y = {1}", x, y );
}
}
Given what you already know about reference types and value types, you are aware
that if you assign one reference variable to another, you have two references pointing
to the same object in memory. Thus, the following assignment operation results in two
references to the same Point object on the heap; modifications using either reference
affect the same object on the heap:
static void Main(string[] args)
{
// Two references to same object!
Point p1 = new Point(50, 50);
Point p2 = p1;
p2.x = 0;
Console.WriteLine(p1);
Console.WriteLine(p2);
}
When you wish to equip your custom types to support the ability to return an identical
copy of itself to the caller, you may implement the standard ICloneable interface. This
type defines a single method named Clone():
public interface ICloneable
{
object Clone();
}
Obviously, the implementation of the Clone() method varies between objects. However,
the basic functionality tends to be the same: Copy the values of your member
variables into a new object instance, and return it to the user. To illustrate, ponder the
following update to the Point class:
// The Point now supports "clone-ability."
public class Point : ICloneable
{
public int x, y;
public Point(){ }
public Point(int x, int y)
{
this.x = x; this.y = y;
}
// Return a copy of the current object.
public object Clone()
{
return new Point(this.x, this.y);
}
public override string ToString()
{
return string.Format("X = {0}; Y = {1}", x, y );
}
}
In this way, you can create exact stand-alone copies of the Point type, as illustrated by
the following code:
static void Main(string[] args)
{
// Notice Clone() returns a generic object type.
// You must perform explicit cast to obtain the derived type.
Point p3 = new Point(100, 100);
Point p4 = (Point)p3.Clone();
// Change p4.x (which will not change p3.x).
p4.x = 0;
// Print each object.
Console.WriteLine(p3);
Console.WriteLine(p4);
}
While the current implementation of Point fits the bill, you can streamline things just
a bit. Because the Point type does not contain reference type variables, you could
simplify the implementation of the Clone() method as follows:
public object Clone()
{
// Copy each field of the Point member by member.
return this.MemberwiseClone();
}
Be aware, however, that if the Point did contain any reference type member variables,
MemberwiseClone() will copy the references to those objects (aka a shallow copy). If
you wish to support a true deep copy, you will need to create a new instance of any
reference type variables during the cloning process. Let’s see an example.
A More Elaborate Cloning Example
Now assume the Point class contains a reference type member variable of type
PointDescription. This class maintains a point’s friendly name as well as an
identification number expressed as a System.Guid (Globally Unique Identifier [GUID]
is a statistically unique 128-bit number). Here is the implementation:
// This class describes a point.
public class PointDescription
{
// Exposed publicly for simplicity.
public string petName;
public Guid pointID;
public PointDescription()
{
this.petName = "No-name";
pointID = Guid.NewGuid();
}
}
The initial updates to the Point class itself included modifying ToString() to account for
these new bits of state data, as well as defining and creating the PointDescription
reference type. To allow the outside world to establish a pet name for the Point, you
also update the arguments passed into the overloaded constructor:
public class Point : ICloneable
{
public int x, y;
public PointDescription desc = new PointDescription();
public Point(){ }
public Point(int x, int y)
{
this.x = x; this.y = y;
}
public Point(int x, int y, string petname)
{
this.x = x;
this.y = y;
desc.petName = petname;
}
public object Clone()
{
return this.MemberwiseClone();
}
public override string ToString()
{
return string.Format("X = {0}; Y = {1}; Name = {2};\n ID =
{3}\n", x, y, desc.petName, desc.pointID);
}
}
Notice that you did not yet update your Clone() method. Therefore, when the object
user asks for a clone using the current implementation, a shallow (member-bymember) copy is achieved. To illustrate, assume you have updated Main() as follows:
static void Main(string[] args)
{
Console.WriteLine("Cloned p3 and stored new Point in p4");
Point p3 = new Point(100, 100, "Jane");
Point p4 = (Point)p3.Clone();
Console.WriteLine("Before modification:");
Console.WriteLine("p3: {0}", p3);
Console.WriteLine("p4: {0}", p4);
p4.desc.petName = "Mr. X";
p4.x = 9;
Console.WriteLine("\nChanged p4.desc.petName and p4.x");
Console.WriteLine("After modification:");
Console.WriteLine("p3: {0}", p3);
Console.WriteLine("p4: {0}", p4);
}
In order for your Clone() method to make a complete deep copy of the internal
reference types, you need to configure the object returned by MemberwiseClone() t o
account for the current point’s name (the System.Guid type is in fact a structure, so
the numerical data is indeed copied). Here is one possible implementation:
// Now we need to adjust for the PointDescription member.
public object Clone()
{
Point newPoint = (Point)this.MemberwiseClone();
PointDescription currentDesc = new PointDescription();
currentDesc.petName = this.desc.petName;
newPoint.desc = currentDesc;
return newPoint;
}
If you rerun the application once again as shown in Figure 7-9, you see that the Point
returned from Clone() does copy its internal reference type member variables (note the
pet name is now unique for both p3 and p4).
To summarize the cloning process, if you have a class or structure that contains
nothing but value types, implement your Clone() method using MemberwiseClone().
However, if you have a custom type that maintains other reference types, you need to
establish a new type that takes into account each reference type member variable.
4.29 Building Comparable Objects (IComparable)
The System.IComparable interface specifies a behavior that allows an object to be
sorted based on some specified key. Here is the formal definition:
// This interface allows an object to specify its relationship //between
other like objects.
public interface IComparable
{
int CompareTo(object o);
}
Let’s assume you have updated the Car class to maintain an internal ID number
(represented by a simple integer named carID) that can be set via a constructor
parameter and manipulated using a new property named ID. Here are the relevant
updates to the Car type:
public class Car
{
...
private int carID;
public int ID
{
get { return carID; }
set { carID = value; }
}
public Car(string name, int currSp, int id)
{
currSpeed = currSp;
petName = name;
carID = id;
}
...
}
Object users might create an array of Car types as follows:
static void Main(string[] args)
{
// Make an array of Car types.
Car[] myAutos = new Car[5];
myAutos[0] = new Car("Rusty", 80, 1);
myAutos[1] = new Car("Mary", 40, 234);
myAutos[2] = new Car("Viper", 40, 34);
myAutos[3] = new Car("Mel", 40, 4);
myAutos[4] = new Car("Chucky", 40, 5);
}
As you recall, the System.Array class defines a static method named Sort(). When you
invoke this method on an array of intrinsic types (int, short, string, etc.), you are able
to sort the items in the array in numerical/alphabetic order as these intrinsic data
types implement IComparable. However, what if you were to send an array of Car
types into the Sort() method as follows?
// Sort my cars?
Array.Sort(myAutos);
If you run this test, you would find that an ArgumentException exception is thrown by
the runtime, with the following message: “At least one object must implement
IComparable.” When you build custom types, you can implement IComparable to allow
arrays of your types to be sorted. When you flesh out the details of CompareTo(), it will
be up to you to decide what the baseline of the ordering operation will be. For the Car
type, the internal carID seems to be the most logical candidate:
// The iteration of the Car can be ordered based on the CarID.
public class Car : IComparable
{
...
// IComparable implementation.
int IComparable.CompareTo(object obj)
{
Car temp = (Car)obj;
if(this.carID > temp.carID)
return 1;
if(this.carID < temp.carID)
return -1;
else
return 0;
}
}
As you can see, the logic behind CompareTo() is to test the incoming type against the
current instance based on a specific point of data. The return value of CompareTo() is
used to discover if this type is less than, greater than, or equal to the object it is being
compared with (see the following table).
CompareTo() Return Value
Meaning
Any number less than zero
This instance comes before the
specified object in the sort order.
Zero
This instance is equal to the specified
object.
Any number greater than zero
This instance comes after the specified
object in the sort order.
Now that your Car type understands how to compare itself to like objects, you can
write the following user code:
// Exercise the IComparable interface.
static void Main(string[] args)
{
// Make an array of Car types.
...
// Dump current array.
Console.WriteLine("Here is the unordered set of cars:");
foreach(Car c in myAutos)
Console.WriteLine("{0} {1}", c.ID, c.PetName);
// Now, sort them using IComparable!
Array.Sort(myAutos);
// Dump sorted array.
Console.WriteLine("Here is the ordered set of cars:");
foreach(Car c in myAutos)
Console.WriteLine("{0} {1}", c.ID, c.PetName);
Console.ReadLine();
}
4.30 Exploring the System.Collections Namespace
We know that the System.Array class provides a number of services (e.g., reversing,
sorting, clearing, and enumerating). However, the simple Array class has a number of
limitations, most notably it does not dynamically resize itself as you add or clear
items. When you need to contain types in a more flexible container, you may wish to
leverage the types defined within the System.Collections namespace.
The System.Collections namespace defines a number of interfaces. As you can guess,
a majority of the collection classes implement these interfaces to provide access to
their contents. Following table gives a breakdown of the core collection-centric
interfaces.
System.Collections
Meaning
Interface
ICollection
Defines generic characteristics (e.g., count and
thread safety) for a collection type.
IComparer
Defines methods to support the comparison of
objects for equality.
IDictionary
Allows an object to represent its contents using
name/value pairs.
IDictionaryEnumerator
Enumerates the contents of a type supporting
IDictionary.
IEnumerable
Returns the IEnumerator interface for a given
object.
IEnumerator
Generally supports
subtypes.
IHashCodeProvider
Returns the hash code for the implementing type
using a customized hash algorithm.
IList
Provides behavior to add, remove, and index
items in a list of objects.
foreach-style
iteration
of
Following figure illustrates the relationship between each type of the interfaces:
IHashCodeProvider
IEnumerator
IComparer
IDictionaryEnumerator
IEnumerable
IList
IDictionary
ICollection
The Class Types of System.Collections
We, know that interfaces by themselves are not very useful until they are implemented
by a given class or structure. The following table provides a rundown of the core
classes in the System.Collections namespace and the key interfaces they support.
System.Collections
Class
ArrayList
Hashtable
Key Implemented
Interfaces
Meaning
Represents a dynamically
array of objects.
sized IList, ICollection,
Represents a collection of objects
identified by a numerical key.
Custom types stored in a Hashtable
should always override
IEnumerable, and
ICloneable
IDictionary,
ICollection,
IEnumerable, and
ICloneable
System.Object.GetHashCode().
Queue
Represents a standard first-in, first- ICollection,
ICloneable,
out (FIFO) queue.
IEnumerable
and
SortedList
Like a dictionary; however, the IDictionary,
elements can also be accessed by ICollection,
IEnumerable, and
ordinal position (e.g., index).
ICloneable
Stack
A last-in, first-out (LIFO) queue ICollection,
providing push and pop (and peek) ICloneable, and
functionality.
IEnumerable
In addition to these key types, System.Collections defines some minor players (at least
in terms of their day-to-day usefulness) such as BitArray, CaseInsensitiveComparer,
and CaseInsensitiveHashCodeProvider. Furthermore, this namespace also defines a
small set of abstract base classes (CollectionBase, ReadOnlyCollectionBase, and
DictionaryBase) that can be used to build strongly typed containers.
Working with the ArrayList Type
The ArrayList type is bound to be your most frequently used type in the
System.Collections namespace in that it allows you to dynamically resize the contents
at your whim. To illustrate the basics of this type, ponder the following code, which
leverages the ArrayList to manipulate a set of Car objects:
static void Main(string[] args)
{
// Create ArrayList and fill with some initial values.
ArrayList carArList = new ArrayList();
carArList.AddRange(new Car[ ] { new Car("Fred", 90, 10),
new Car("Mary", 100, 50), new Car("MB", 190, 11)});
Console.WriteLine("Items in carArList: {0}", carArList.Count);
// Print out current values.
foreach(Car c in carArList)
Console.WriteLine("Car pet name: {0}", c.PetName);
// Insert a new item.
Console.WriteLine("\n->Inserting new Car.");
carArList.Insert(2, new Car("TheNewCar", 0, 12));
Console.WriteLine("Items in carArList: {0}", carArList.Count);
// Get object array from ArrayList and print again.
object[] arrayOfCars = carArList.ToArray();
for(int i = 0; i < arrayOfCars.Length; i++)
{
Console.WriteLine("Car pet name: {0}",
((Car)arrayOfCars[i]).PetName);
}
}
Here you are making use of the AddRange() method to populate your ArrayList with a
set of Car types (this is basically a shorthand notation for calling Add() n number of
times). Once you print out the number of items in the collection (as well as enumerate
over each item to obtain the pet name), you invoke Insert(). The Insert() allows you to
plug a new item into the ArrayList at a specified index. Finally, notice the call to the
ToArray() method, which returns a generic array of System.Object types based on the
contents of the original ArrayList.
Working with the Queue Type
Queues are containers that ensure items are accessed using a first-in, first-out
manner. Sadly, we humans are subject to queues all day long: lines at the bank, lines
at the movie theater, and lines at the morning coffeehouse. When you are modeling a
scenario
in
which
items
are
handled
on
a
first-come,
first-served
basis,
System.Collections.Queue is your type of choice. In addition to the functionality
provided by the supported interfaces, Queue defines the key members shown in the
following table.
Member of
Meaning
System.Collection.Queue
Dequeue()
Removes and returns the object at the
beginning of the Queue
Enqueue()
Adds an object to the end of the Queue
Peek()
Returns the object at the beginning of the
Queue without removing it
Working with the Stack Type
The System.Collections.Stack type represents a collection that maintains items using
a last-in, first-out manner. As you would expect, Stack defines a member named
Push() and Pop() (to place items onto or remove items from the stack). The following
stack example makes use of the standard System.String:
static void Main(string[] args)
{
...
Stack stringStack = new Stack();
stringStack.Push("One");
stringStack.Push("Two");
stringStack.Push("Three");
// Now look at the top item, pop it, and look again.
Console.WriteLine("Top item is: {0}", stringStack.Peek());
Console.WriteLine("Popped off {0}", stringStack.Pop());
Console.WriteLine("Top item is: {0}", stringStack.Peek());
Console.WriteLine("Popped off {0}", stringStack.Pop());
Console.WriteLine("Top item is: {0}", stringStack.Peek());
Console.WriteLine("Popped off {0}", stringStack.Pop());
try
{
Console.WriteLine("Top item is: {0}", stringStack.Peek());
Console.WriteLine("Popped off {0}", stringStack.Pop());
}
catch(Exception e)
{ Console.WriteLine("Error!! {0}", e.Message); }
}
Here, you build a stack that contains three string types (named according to their
order of insertion). As you peek onto the stack, you will always see the item at the very
top, and therefore the first call to Peek() reveals the third string. After a series of Pop()
and Peek() calls, the stack is eventually empty, at which time additional Peek()/Pop()
calls raise a system exception.
4.31 Summary
In addition to building you custom interfaces, the .NET libraries define a number of
standard (i.e. framework-supplied) interfaces.
This unit focused on the interfaces
defined within the System.Collections namespace. As you have seen, you are free to
build custom types that implement these predefined interfaces to gain a number of
desirable traits such as cloning, sorting and enumerating.
4.32 Keywords










IConvertible.ToXXXX() method
IFormatProvider Interface
GetFormat() method
IConvertible.GetTypeCode() method
System.Convert
GetEnumerator() method
Clone() method
GUID
CompareTo() method
The classes: ArrayList, Hashtable, Queue, SortedList, Stack
4.33 Exercises
23. Explain any two methods of IConvertible interface.
24. Explain the usage of IEnumerable and IEnumerator interfaces with suitable
examples.
25. What do you mean by cloneable object? Write an example to depict the
implementation of ICloneable Interface.
26. Illustrate with an example, the implementation of IComparable interface.
27. Give the syntax for the method CompareTo() and explain briefly.
28. List out some of the interfaces provided by System.Collection namespace. Draw
the diagram to show the hierarchical relationship that exists between these
interfaces.
29. What are the major classes in System.Collections namespace? Explain.
30. Write a code segment to illustrate the working of ArrayList class.
31. What the members of System.Collection.Queue? Explain.
32. Write a code snippet to show the usage of Stack class.
Module 4
Unit 1
CONTENTS:
1.48
1.49
1.50
1.51
1.52
1.53
1.54
1.55
1.56
1.57
Objectives
Introduction
Understanding Callback Interfaces
Understanding the .NET Delegate Type
Members of System.Multicast Delegate
The Simplest Possible Delegate Example
Building More Elaborate Delegate Example
Understanding Asynchronous Delegates
Understanding (and Using) Events
Summary
1.58
1.59
Keywords
Exercises
1.40 Objectives
At the end of this lesson, the students will understand:

The meaning of callback interfaces and its usage

Delegate types, their usage, members of Multicast delegate, synchronous and
asynchronous delegates

Events
1.41 Introduction
Up to this point in the text, every application you have developed added various bits of
code to Main(), which, in some way or another, sent requests to a given object.
However, you have not yet examined how an object can talk back to the entity that
created it. In most programs, it is quite common for objects in a system to engage in a
two-way conversation through the use of callback interfaces, events, and other
programming constructs. To set the stage, this chapter begins by examining how
interface types may be used to enable callback functionality.
Next, you learn about the .NET delegate type, which is a type-safe object that “points
to” other method(s) that can be invoked at a later time. Unlike a traditional C++
function pointer, however, .NET delegates are objects that have built-in support for
multicasting and asynchronous method invocation. Once you learn how to create and
manipulate delegate types, you then investigate the C# event keyword, which
simplifies and streamlines the process of working with delegate types.
1.42 Understanding Callback Interfaces
As you have seen in the previous module, interfaces define a behavior that may be
supported by various types in your system. Beyond using interfaces to establish
polymorphism, interfaces may also be used as a callback mechanism. This technique
enables objects to engage in a two-way conversation using a common set of members.
To illustrate the use of callback interfaces, let’s update the now familiar Car type in
such a way that it is able to inform the caller when it is about to explode (the current
speed is 10 miles below the maximum speed) and has exploded (the current speed is
at or above the maximum speed). The ability to send and receive these events will be
facilitated with a custom interface named IEngineEvents:
// The callback interface.
public interface IEngineEvents
{
void AboutToBlow(string msg);
void Exploded(string msg);
}
Event interfaces are not typically implemented directly by the object directly interested
in receiving the events, but rather by a helper object called a sink object. The sender of
the events (the Car type in this case) will make calls on the sink under the appropriate
circumstances. Assume the sink class is called CarEventSink, which simply prints out
the incoming messages to the console. Beyond this point, our sink will also maintain a
string that identifies its friendly name:
// Car event sink.
public class CarEventSink : IEngineEvents
{
private string name;
public CarEventSink(){ }
public CarEventSink(string sinkName)
{ name = sinkName; }
public void AboutToBlow(string msg)
{ Console.WriteLine("{0} reporting: {1}", name, msg); }
public void Exploded(string msg)
{ Console.WriteLine("{0} reporting: {1}", name, msg); }
}
Now that you have a sink object that implements the event interface, your next task is
to pass a reference to this sink into the Car type. The Car holds onto the reference and
makes calls back on the sink when appropriate. In order to allow the Car to obtain a
reference to the sink, we will need to add a public helper member to the Car type that
we will call Advise(). Likewise, if the caller wishes to detach from the event source, it
may call another helper method on the Car type named Unadvise(). Finally, in order to
allow the caller to register multiple event sinks (for the purposes of multicasting), the
Car now maintains an ArrayList to represent each outstanding connection:
// This Car and caller can now communicate
// using the IEngineEvents interface.
public class Car
{
// The set of connected sinks.
ArrayList clientSinks = new ArrayList();
// Attach or disconnect from the source of events.
public void Advise(IEngineEvents sink)
{ clientSinks.Add(sink); }
public void Unadvise(IEngineEvents sink)
{ clientSinks.Remove(sink); }
...
}
To actually send the events, let’s update the Car.Accelerate() method to iterate over the
list of connections maintained by the ArrayList and fire the correct notification when
appropriate (note the Car class now maintains a Boolean member variable named
carIsDead to represent the engine’s state):
// Interface-based event protocol!
class Car
{
...
// Is the car alive or dead?
bool carIsDead;
public void Accelerate(int delta)
{
// If the car is 'dead', send Exploded event to each sink.
if(carIsDead)
{
foreach(IEngineEvents e in clientSinks)
e.Exploded("Sorry, this car is dead...");
}
else
{
currSpeed += delta;
// Send AboutToBlow event.
if(10 == maxSpeed - currSpeed)
{
foreach(IEngineEvents e in clientSinks)
e.AboutToBlow("Careful !!!");
}
if(currSpeed >= maxSpeed)
carIsDead = true;
else
Console.WriteLine("CurrSpeed= {0} ", currSpeed);
}
}
}
Here is some client-side code, now making use of a callback interface to listen to the
Car events:
// Make a car and listen to the events.
public class CarApp
{
static void Main(string[] args)
{
Console.WriteLine("***** Interfaces as event enablers *****");
Car c1 = new Car("SlugBug", 100, 10);
// Make sink object.
CarEventSink sink = new CarEventSink();
// Pass the Car a reference to the sink.
c1.Advise(sink);
// Speed up (this will trigger the events).
for(int i = 0; i < 10; i++)
c1.Accelerate(20);
// Detach from event source.
c1.Unadvise(sink);
Console.ReadLine();
}
}
Do note that the Unadvise() method can be very helpful in that it allows the caller to
selectively detach from an event source at will. Here, you call Unadvise() before exiting
Main(), although this is not technically necessary. However, assume that the
application now wishes to register two sinks, dynamically remove a particular sink
during the flow of execution, and continue processing the program at large.
1.43 Understanding the .NET Delegate Type
Before formally defining .NET delegates, let’s gain a bit of perspective. Historically
speaking, the Windows API makes frequent use of C-style function pointers to create
entities termed callback functions or simply callbacks. Using callbacks, programmers
were able to configure one function to report back to (call back) another function in
the application. The problem with standard C-style callback functions is that they
represent little more than a raw address in memory. Ideally, callbacks could be
configured to include additional type-safe information such as the number of (and
types of) parameters and the return value (if any) of the method pointed to. Sadly, this
is not the case in traditional callback functions, and, as you may suspect, can
therefore be a frequent source of bugs, hard crashes, and other runtime disasters.
Nevertheless, callbacks are useful entities. In the .NET Framework, callbacks are still
possible, and their functionality is accomplished in a much safer and more objectoriented manner using delegates. In essence, a delegate is a type-safe object that
points to another method (or possibly multiple methods) in the application, which can
be invoked at a later time. Specifically speaking, a delegate type maintains three
important pieces of information:



The name of the method on which it makes calls
The arguments (if any) of this method
The return value (if any) of this method
Once a delegate has been created and provided the aforementioned information, it may
dynamically invoke the method(s) it points to at runtime. As you will see, every
delegate in the .NET Framework (including your custom delegates) is automatically
endowed with the ability to call their methods synchronously or asynchronously. This
fact greatly simplifies programming tasks, given that we can call a method on a
secondary thread of execution without manually creating and managing a Thread
object.
Defining a Delegate in C#
When you want to create a delegate in C#, you make use of the delegate keyword. The
name of your delegate can be whatever you desire. However, you must define the
delegate to match the signature of the method it will point to. For example, assume
you wish to build a delegate named BinaryOp that can point to any method that
returns an integer and takes two integers as input parameters:
// This delegate can point to any method, taking two integers
// and returning an integer.
public delegate int BinaryOp(int x, int y);
When the C# compiler processes delegate types, it automatically generates a sealed
class deriving from System.MulticastDelegate. This class (in conjunction with its base
class, System.Delegate) provides the necessary infrastructure for the delegate to hold
onto the list of methods to be invoked at a later time.
1.44 Members of System.MulticastDelegate
When you build a type using the C# delegate keyword, you indirectly declare a class
type that derives from System.MulticastDelegate. This class provides descendents with
access to a list that contains the addresses of the methods maintained by the delegate
type, as well as several additional methods (and a few overloaded operators) to interact
with the invocation list. Here are some select members of System.MulticastDelegate:
Inherited Member
Meaning
Method
This property returns a System.Reflection.MethodInfo type
that represents details of a static method that is
maintained by the delegate.
Target
If the method to be called is defined at the object level
(rather than a static method), Target returns an object that
represents the method maintained by the delegate. If the
value returned from Target equals null, the method to be
called is a static member.
This static method adds a method to the list maintained
by the delegate. In C#, you trigger this method using the
overloaded += operator as
Combine()
a shorthand notation.
GetInvocationList()
This method returns an array of System.Delegate types,
each representing a particular method that may be
invoked.
Remove()
These static methods remove a method (or all methods)
from the invocation list. In C#, the Remove() method can
be called indirectly using
RemoveAll()
the overloaded -= operator.
1.45 The Simplest Possible Delegate Example
Delegates can tend to cause a great deal of confusion when encountered for the first
time. Thus, to get the ball rolling, let’s take a look at a very simple example that
leverages our BinaryOp delegate type. Here is the complete code, with analysis to
follow:
namespace SimpleDelegate
{
// This delegate can point to any method, taking two
// integers and returning an integer.
public delegate int BinaryOp(int x, int y);
// This class contains methods BinaryOp will point to.
public class SimpleMath
{
public static int Add(int x, int y)
{ return x + y; }
public static int Subtract(int x, int y)
{ return x – y; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Simple Delegate Example");
// Create a BinaryOp object that
// "points to" SimpleMath.Add().
BinaryOp b = new BinaryOp(SimpleMath.Add);
// Invoke Add() method using delegate.
Console.WriteLine("10 + 10 is {0}", b(10, 10));
Console.ReadLine();
}
}
}
Again notice the format of the BinaryOp delegate, which can point to any method
taking two integers and returning an integer. Given this, we have created a class
named SimpleMath, which defines two static methods that match the pattern defined
by the BinaryOp delegate. When you want to insert the target method to a given
delegate, simply pass in the name of the method to the delegate’s constructor. At this
point, you are able to invoke the member pointed to using a syntax that looks like a
direct function invocation:
// Invoke() is really called here!
Console.WriteLine("10 + 10 is {0}", b(10, 10));
Under the hood, the runtime actually calls the compiler-generated Invoke() method.
Recall that .NET delegates (unlike C-style function pointers) are type safe. Therefore, if
you attempt to pass a delegate a method that does not “match the pattern,” you
receive a compile-time error. To illustrate, assume the SimpleMath class defines an
additional method named SquareNumber():
public class SimpleMath
{
...
public static int SquareNumber(int a)
{ return a * a; }
}
Given that the BinaryOp delegate can only point to methods that take two integers and
return an integer, the following code is illegal and will not compile:
// Error! Method does not match delegate pattern!
BinaryOp b = new BinaryOp(SimpleMath.SquareNumber);
1.46 Building a More Elaborate Delegate Example
To illustrate a more advanced use of delegates, let’s begin by updating the Car class to
include two new Boolean member variables. The first is used to determine whether
your automobile is due for a wash (isDirty); the other represents whether the car in
question is in need of a tire rotation (shouldRotate). To enable the object user to
interact with this new state data, Car also defines some additional properties and an
updated constructor. Here is the story so far:
// Updated Car class.
public class Car
{
...
// Are we in need of a wash? Need to rotate tires?
private bool isDirty;
private bool shouldRotate;
// Extra params to set bools.
public Car(string name, int max, int curr, bool washCar, bool rotateTires)
{
...
isDirty = washCar;
shouldRotate = rotateTires;
}
public bool Dirty
{
get{ return isDirty; }
set{ isDirty = value; }
}
public bool Rotate
{
get{ return shouldRotate; }
set{ shouldRotate = value; }
}
}
Now, also assume the Car type nests a new delegate, CarDelegate:
// Car defines yet another delegate.
public class Car
{
...
// Can call any method taking a Car as
// a parameter and returning nothing.
public delegate void CarDelegate(Car c);
...
}
Here, you have created a delegate named CarDelegate. The CarDelegate type
represents “some function” taking a Car as a parameter and returning void.
1.47 Understanding Asynchronous Delegates
Till now, we have seen the synchronous behavior of .NET delegate types. Now let us
examine how to invoke methods asynchronously. First off, what exactly warrants an
asynchronous method invocation? As you are aware, some programming operations
take time. For example, if you build a word processing application that has the ability
to print the current document, and that document happens to be 1000 pages in
length, the computer’s CPU has the potential to spin away for quite some time.
Now assume that this application has all of its programming logic taking place within
the Main() method using a single thread. Simply put, a thread is a path of execution
within a .NET application. Single-threaded applications are quite simple to program;
however, in the case of word processor application, the end user is far less than
pleased. The reason has to do with the fact that while the application’s single thread
of execution is crunching out the 1000-page document, all other aspects of this
program (such as menu activation, toolbar clicking, and keyboard input) are
unresponsive.
When programmers wish to build applications that are able to simulate numerous
tasks performing “at the same time”, they will typically spawn additional threads to
perform background tasks (e.g. printing documents) while the main thread is still able
to respond to basic user-input needs.
So, what does threading have to do with .NET delegates? Well, to illustrate the
potential problem with synchronous delegate invocations, consider the following
application:
using System;
using System.Threading;
namespace AsyncDelegate
{
// a new delegate type
public delegate string NewCarDelegate(Car carToDetail);
public class Car { ….}
class App
{
public static stirng DetailCar(Car c)
{
//Detailing a car takes 10 seconds
Console.WriteLine(“Detailing Car on Thread {0}”,
Thread.CurrentThread.GetHashCode());
Thread.Sleep(10000);
return “Your car is ready!!”;
}
static void Main(string[] args)
{
Console.WriteLine(“Main() is on thread {0}”,
Thread.CurrentThread.GetHashCode());
NewCarDelegate d= new NewCarDelegate(DetailCar);
Car mycar=new Car();
Console.WriteLine(d(mycar));
Console.WriteLine(“Done invoking delegate”);
}
}
}
Notice that, this example makes use of a new namespace named System.Threading.
This namespace defines a type named Thread, which provides a static method named
CurrentThread(). If you obtain the hash code for the current thread, you obtain a
unique identifier for the currently executing thread.
Here, you print out the hash code of the current thread within Main(), and then
synchronously invoke the NewCarDelegate type. Once the flow of execution passes
into the DetailCar() helper function, you print out the hash code for the active thread
once again and put it to sleep for 10 seconds. Given this, you will not see the final
message of Main() print to the console until approximately 10 seconds after the
delegate’s invocation.
Invoking Methods Asynchronously
When the C# compiler processes the “delegate” keyword, you dynamically receive two
methods named BeginInvoke() and EndInvoke(). Thus, for the NewCarDelegate type,
you are provided with the following member:
public IAsyncResult BeginInvoke(Car carToDetail,
System.AsyncCallback callback, object state);
public string EndInvoke(IAsyncResult result);
Note that BeginInvoke() returns an IAsyncResult interface, while EndInvoke() requires
an IAsyncInvoke type as parameter.
In the simplest case, one is able to effectively ignore directly interacting with these
members. All you have to do is cache the returned IAsyncResult type in a local
variable in order to pass it to EndInvoke() when you are ready to obtain the result of
the method invocation.
1.48 Understanding (and using) Events
Delegates are fairly interesting constructs in that they enable two objects in memory to
engage in a two-way conversation. As you may agree, however, working with delegates
in the raw does require a good amount of boilerplate code (defining the delegate,
declaring
necessary
member
variables,
and
creating
custom
registration/un-
registration methods). Because the ability for one object to call back to another object
is such a helpful construct, C# provides the event keyword to lessen the burden of
using delegates in the raw. When the compiler processes the event keyword, you are
automatically provided with registration and un-registration methods as well as any
necessary member variable for your delegate types. In this light, the event keyword is
little more than syntactic sugar, which can be used to save you some typing time.
Defining an event is a two-step process. First, you need to define a delegate that
contains the methods to be called when the event is fired. Next, you declare the events
(using the C# event keyword) in terms of the related delegate. In a nutshell, defining a
type that can send events entails the following pattern (shown in pseudo-code):
public class SenderOfEvents
{
public delegate retval AssociatedDelegate(args);
public event AssociatedDelegate NameOfEvent;
...
}
The events of the Car type will take the same name as the previous delegates
(AboutToBlow and Exploded). The new delegate to which the events are associated will
be called CarEventHandler. Here are the initial updates to the Car type:
public class Car
{
// This delegate works in conjunction with the
// Car's events.
public delegate void CarEventHandler(string msg);
// This car can send these events.
public event CarEventHandler Exploded;
public event CarEventHandler AboutToBlow;
...
}
Sending an event to the caller is as simple as specifying the event by name as well as
any required parameters as defined by the associated delegate. To ensure that the
caller has indeed registered with event, you will want to check the event against a null
value before invoking the delegate’s method set. These things being said, here is the
new iteration of the Car’s Accelerate() method:
public void Accelerate(int delta)
{
// If the car is dead, fire Exploded event.
if (carIsDead)
{
if (Exploded != null)
Exploded("Sorry, this car is dead...");
}
else
{
currSpeed += delta;
// Almost dead?
if (10 == maxSpeed – currSpeed && AboutToBlow != null)
{
AboutToBlow("Careful buddy! Gonna blow!");
}
// Still OK!
if (currSpeed >= maxSpeed)
carIsDead = true;
else
Console.WriteLine("->CurrSpeed = {0}", currSpeed);
}
}
With this, you have configured the car to send two custom events without the need to
define custom registration functions. You will see the usage of this new automobile in
just a moment, but first, let’s check the event architecture in a bit more detail.
1.49 Summary
In this unit, you have examined a number of ways in which multiple objects can
partake in a bidirectional conversation under .NET. First, you examined the use of
callback interfaces, which provide a way to have object B make calls on object A via an
interface reference.
Next, you examined the C# delegate types which is used to
indirectly construct a class derived from System.MulticastDelegate. It is simply an
object that maintains a list of methods to call when told to do so. These invocations
may be made synchronously (using Invoke() method) or asynchronously (via
BeginInvoke() and EndInvoke() methods). Finally, you have seen event, which when
used in conjunction with a delegate type, can simplify the process of sending your
even notifications to awaiting callers.
1.50 Keywords










Function Pointer
Callback Mechanism
Sink Object
Advice() and Unadvice() methods
Callback functions
Synchronous and asynchronous behavior of delegates
Thread
Thread object
BeginInvoke() and EndInvoke() methods
The keyword event
1.51 Exercises
7. Explain the concept of callback mechanism with an example.
8. Define sink objects.
9. Illustrate with an example, the usage of Advise() and UnAdvise() methods.
10. Define delegate with syntax. List out the information maintained by delegates.
11. List out the members of System.MulticastDelegate along with the purpose.
12. Give an example for the implementation of delegates.
13. Explain the concept of synchronous and asynchronous delegate invocation.
14. Define thread.
15. What are events? Explain with an example.
Module 4
Unit 2
CONTENTS:
1.60
2.38
2.39
2.40
2.41
2.42
2.43
2.44
2.45
2.46
2.47
Objectives
Introduction
The advanced keywords of C#
Building a Custom Indexer
Overloading operators
The Internal Representation of Overloading Operators
Creating Custom Conversion Routines
Defining Implicit Conversion Routines
Summary
Keywords
Exercises
2.1 Objectives
At the end of this lesson, the students will understand the following tasks in detail:

The usage of keywords like checked, unchecked, unsafe, volatile etc.

Meaning of indexer and its usage

Operator overloading and the way they are represented by CIL

Explicit and implicit conversions.
2.37 Introduction
This
unit
investigates
programmatically
advanced
account
for
constructs
of
C#.
You
overflow/underflow
will
learn
how
conditions
to
using
checked/unchecked keywords, how to create an unsafe code for directly manipulating
pointer types. Next, you learn how to construct and use an indexer method,
overloading operators (like +, - etc) and create custom conversion functions (the C#
equivalent to overloading the () operator under C++).
2.38 The Advanced Keywords of C#
Throughout our discussion till all these units, we have seen many C# keywords. In
addition to those, C# provides a set of keywords viz.

checked/unchecked

unsafe/stackalloc/ fixed/volatile/sizeof

lock
Except the keyword lock, that is used in multithreading, we will discuss other
keywords here in detail.
The checked Keyword
As you know, each numerical data type has a fixed upper and lower limit (which may
be obtained programmatically using the MaxValue and MinValue properties). Now,
when you are performing arithmetic operations on a specific type, it is possible that
you may accidentally overflow the maximum storage of the type (i.e., assign a value
that is greater than the maximum value) or underflow the minimum storage of the
type (i.e., assign a value that is less than the minimum value). These two situations
are together termed as overflow. Consider the following example for illustration.
namespace CheckedUnchecked
{
class Program
{
static void Main(string[] args)
{
// max value of byte is 250
Console.WriteLine("Max value of byte is {0}.", byte.MaxValue);
Console.WriteLine("Min value of byte is {0}.", byte.MinValue);
byte b1 = 100;
byte b2 = 250;
byte sum = (byte)(b1 + b2);
Console.WriteLine("sum = {0}", sum);
}
}
}
The value of sum will be 94 but not 350. The reason is simple. Given that a
System.Byte can hold a value only between 0 and 255 (inclusive, for a grand total of
256 slots), sum now contains the overflow value (350 – 256 = 94). As you have just
seen, if you take no corrective course of action, overflow occurs without exception. At
times, this hidden overflow may cause no harm whatsoever in your project. Other
times, this loss of data is completely unacceptable.
To handle overflow or underflow conditions in your application, you have two options.
The first choice is to handle all overflow conditions manually. That, is you can modify
the statements like –
byte b1 = 100;
byte b2 = 250;
int sum = b1 + b2; // Store sum in integer to prevent overflow.
But, if the values of b1 and b2 are input from the keyboard, and if you forget to
declare sum as integer, then there will be an unexpected output. To avoid this
problem, C# provides the checked keyword. When you wrap a statement (or a block of
statements) within the scope of the checked keyword, the C# compiler emits specific
CIL instructions that test for overflow conditions that may result when adding,
multiplying, subtracting, or dividing two numerical data types. If an overflow has
occurred, the runtime will throw a System.OverflowException type. Observe the
changes in the following program:
class Program
{
static void Main(string[] args)
{
// Overflow the max value of a System.Byte.
Console.WriteLine("Max value of byte is {0}.", byte.MaxValue);
byte b1 = 100;
byte b2 = 250;
try
{
byte sum = checked((byte)(b1 + b2));
Console.WriteLine("sum = {0}", sum);
}
catch(OverflowException e)
{ Console.WriteLine(e.Message); }
}
}
Here, you wrap the addition of b1 and b2 within the scope of the checked keyword. If
you wish to force overflow checking to occur over a block of code, the syntax would be:
try
{
checked
{
byte sum = (byte)(b1 + b2);
Console.WriteLine("sum = {0}", sum);
}
}
catch(OverflowException e)
{
Console.WriteLine(e.Message);
}
In either case, the code in question will be evaluated for possible overflow conditions
automatically, which will trigger an overflow exception if encountered.
The unchecked Keyword
Assume that, you have a block of code where silent overflow is acceptable. For this
purpose,
C#
provides
the
unchecked
keyword
to
disable
the
throwing
of
System.OverflowException on a case-by-case basis. The syntax is as same as that of
checked keyword. For example:
// Assuming /checked is enabled, this block will not trigger
// a runtime exception.
unchecked
{
byte sum = (byte)(b1 + b2);
Console.WriteLine("sum = {0}", sum);
}
So, to summarize the C# checked and unchecked keywords, remember that the
default behavior of the .NET runtime is to ignore arithmetic overflow. When you want
to selectively handle discrete statements, make use of the checked keyword. If you
wish to trap overflow errors throughout your application, enable the /checked flag.
Finally, the unchecked keyword may be used if you have a block of code where
overflow is acceptable (and thus should not trigger a runtime exception).
Working with unsafe Code
We have three keywords that allow the C# programmer to bypass the CLR’s memory
management scheme in order to make use of C or C++ style pointers. For this
purpose, C# provides following operators:
C# Pointer-Centric
Meaning
Operator
*
Used to create a pointer variable that represents a
direct location in memory. Like C /C++, this same
operator is used to represent pointer indirection.
&
Used to obtain the address of a pointer
->
Used to access fields of a type that is represented
by a pointer variable (the unsafe version of C# dot
operator)
[]
This allows you to index the slot pointed to by a
pointer variable.
There will be only two situations where we need to use unsafe code (usage of pointers:

You are looking to optimize selected parts of your application by bypassing the
CLR. For example, you want to build a function that copies an array using
pointer arithmetic.

You are attempting to trigger the functionality of a C-based *.dll (such as the
Win32 API or a custom C-based *.dll) and need to create pointer variables to
call various methods.
The unsafe Keyword
When you wish to work with pointers in C#, you must specifically declare a block of
“unsafe” code using the unsafe keyword:
unsafe
{
// Work with pointer types here!
}
In addition to declaring a scope of unsafe code, you are able to build structures,
classes, type members, and parameters that are “unsafe.” Here are a few examples to
gnaw on:
// This entire structure is unsafe & can be used only in an unsafe context.
public unsafe struct Node
{
public int Value;
public Node* Left;
public Node* Right;
}
// This struct is safe, but the Node* members are not. Technically, you // may
access 'Value' from outside an unsafe context, but not 'Left' and
// 'Right'.
public struct Node
{
public int Value;
// These can be accessed only in an unsafe context!
public unsafe Node* Left;
public unsafe Node* Right;
}
Methods (static or instance level) may be marked as unsafe as well. For example,
assume that you know a given static method will make use of pointer logic. To ensure
that this method can be called only from an unsafe context, you could define the
method as follows:
unsafe public static void SomeUnsafeCode()
{
// Work with pointer types here!
}
This configuration demands that the caller invoke SomeUnsafeCode() as so:
static void Main(string[] args)
{
unsafe
{
SomeUnsafeCode();
}
}
Conversely, if you would rather not force the caller to wrap the invocation within an
unsafe context, you could remove the unsafe keyword from the SomeUnsafeCode()
method signature and opt for the following:
public static void SomeUnsafeCode()
{
unsafe
{
// Work with pointers here!
}
}
this would simplify the call to this:
static void Main(string[] args)
{
SomeUnsafeCode();
}
Working with the * and & Operators
Once you have established an unsafe context, you are then free to build pointers to
data types using the * operator and obtain the address of said pointer using the &
operator. Using C#, the * operator is applied to the underlying type only, not as a
prefix to each pointer variable name. For example, the following code declares two
variables, both of type int* (a pointer to an integer):
// Wrong syntax in C#
int *p1, *p2;
// Correct syntax of C#.
int* p1, p2;
Consider the following example:
unsafe
{
int myInt;
// Define an int pointer, and assign it the address of myInt.
int* ptrToMyInt = &myInt;
// Assign value of myInt using pointer indirection.
*ptrToMyInt = 123;
// Print some stats.
Console.WriteLine("Value of myInt {0}", myInt);
Console.WriteLine("Address of myInt {0:X}", (int)&ptrToMyInt);
}
An Unsafe (and Safe) Swap Function
Declaring pointers to local variables simply to assign their value (as shown in the
previous example) is never required and not altogether useful. To illustrate a more
practical example of unsafe code, assume you wish to build a swap function using
pointer arithmetic:
unsafe public static void UnsafeSwap(int* i, int* j)
{
int temp = *i;
*i = *j;
*j = temp;
}
But the same task can be achieved by a safe mode in C# as:
public static void SafeSwap(ref int i, ref int j)
{
int temp = i;
i = j;
j = temp;
}
Field Access via Pointers (The -> Operator)
Now assume that you have defined a Point structure and wish to declare a pointer to a
Point type. Like C/C++, when you wish to invoke methods or trigger fields of a pointer
type, you will need to make use of the pointer-field access operator (->). This is the
unsafe version of the standard (safe) dot operator (.). In fact, using the pointer
indirection operator (*), it is possible to dereference a pointer to (once again) apply the
dot operator notation. Check out the following:
struct Point
{
public int x;
public int y;
public override string ToString()
{
return string.Format("({0}, {1})", x, y);
}
static void Main(string[] args)
{
unsafe
// Access members via pointer.
{
Point point;
Point* p = &point;
p->x = 100;
p->y = 200;
Console.WriteLine(p->ToString());
}
unsafe // Access members via pointer indirection.
{
Point point;
Point* p = &point;
(*p).x = 100;
(*p).y = 200;
Console.WriteLine((*p).ToString());
}
}
}
The stackalloc Keyword
In an unsafe context, you may need to declare a local variable that allocates memory
directly from the call stack (and is therefore not subject to .NET garbage collection). To
do so, C# provides the stackalloc keyword, which is the C# equivalent to the _alloca()
function of the C runtime library. Here is a simple example:
unsafe
{
char* p = stackalloc char[256];
for (int k = 0; k < 256; k++)
p[k] = (char)k;
}
Pinning a Type via the fixed Keyword
As you saw in the previous example, allocating a chunk of memory within an unsafe
context may be facilitated via the stackalloc keyword. By the very nature of this
operation, the allocated memory is cleaned up as soon as the allocating method has
returned (as the memory is acquired from the stack). However, assume a more
complex example. During our examination of the -> operator, you created a value type
named Point. Like all value types, the allocated memory is popped off the stack once
the executing scope has terminated. For the sake of argument, assume Point was
instead defined as a reference type:
class Point
// Now a class!
{
public int x;
public int y;
public override string ToString()
{
return string.Format("({0}, {1})", x, y);
}
}
As you are well aware, if the caller declares a variable of type Point, the memory is
allocated on the garbage collected heap. Now the question is, what if an unsafe context
wishes to interact with this object (or any object on the heap)? Given that garbage
collection can occur at any moment, imagine the pain of accessing the members of
Point at the very point in time at which a sweep of the heap is under way.
Theoretically, it is possible that the unsafe context is attempting to interact with a
member that is no longer accessible or has been repositioned on the heap after
surviving a generational sweep (which is an obvious problem).
To lock a reference type variable in memory from an unsafe context, C# provides the
fixed keyword. The fixed statement sets a pointer to a managed type and “pins” that
variable during the execution of statement. Without fixed, pointers to managed
variables would be of little use, since garbage collection could relocate the variables
unpredictably. (In fact, the C# compiler will not allow you to set a pointer to a
managed variable except in a fixed statement.) Thus, if you create a Point type (now
redesigned as a class) and want to interact with its members, you must write the
following code (or receive a compiler error):
unsafe public static void Main()
{
Point pt = new Point();
pt.x = 5;
pt.y = 6;
// pin pt in place so it will not be moved or GC-ed.
fixed (int* p = &pt.x)
{
// Use int* variable here!
}
// pt is now unpinned, and ready to be GC-ed.
Console.WriteLine ("Point is: {0}", pt);
}
In a nutshell, the fixed keyword allows you to build a statement that locks a reference
variable in memory, such that its address remains constant for the duration of the
statement. To be sure, any time you interact with a reference type from within the
context of unsafe code, pinning the reference is a must.
The volatile Keyword
When you define a volatile variable, you are performing the converse operation of
pinning a type in memory, in that you are telling the runtime that it is completely fine
to allow an outside agent to modify the item in question at any time. The syntax would
be –
volatile int x;
Basically, this keyword may be used to define a type filed that is accessed by multiple
threads without using the lock statement to serialize access.
The sizeof Keyword
As in C/C++, the C# sizeof keyword is used to obtain the size in bytes for a value type
(never a reference type), and it may only be used within an unsafe context. As you may
imagine, this ability may prove helpful when you are interacting with unmanaged Cbased APIs. Its usage is straightforward:
unsafe
{
Console.WriteLine("The size of short is {0}.", sizeof(short));
Console.WriteLine("The size of int is {0}.", sizeof(int));
Console.WriteLine("The size of long is {0}.", sizeof(long));
}
As sizeof will evaluate the number of bytes for any System.ValueType-derived entity,
you are able to obtain the size of custom structures as well. Assume you have defined
the following struct:
struct MyValueType
{
public short s;
public int i;
public long l;
}
Console.WriteLine("The size of MyValueType is {0}.", sizeof(MyValueType));
2.39 Building a Custom Indexer
As programmers, we are very familiar with the process of accessing discrete items
contained within a standard array using the index operator, for example:
// Declare an array of integers.
int[ ] myInts = { 10, 9, 100, 432, 9874};
// Use the [ ] operator to access each element.
for(int j = 0; j < myInts.Length; j++)
Console.WriteLine("Index {0} = {1} ", j, myInts[j]);
The C# language provides the capability to build custom classes and structures that
may be indexed just like a standard array. The method that provides the capability to
access items in this manner is termed an indexer.
Consider the following example:
// Indexers allow you to access items in an arraylike fashion.
public class Program
{
static void Main(string[] args)
{
// Assume the Garage type has an indexer method.
Garage carLot = new Garage();
carLot[0] = new Car("FeeFee", 200);
carLot[1] = new Car("Clunker", 90);
carLot[2] = new Car("Zippy", 30);
// Now obtain and display each item using indexer.
//Assume PetName, CurrSpeed are defined properties of class
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Car number: {0}", i);
Console.WriteLine("Name: {0}", carLot[i].PetName);
Console.WriteLine("Max speed: {0}", carLot[i].CurrSpeed);
}
}
}
2.40 Overloading Operators
Consider the following code snippet:
int a=20, b=30;
int c=a+b;
//now c will be 50
string s1= “Hello”, s2= “World”;
string s3= s1+s2;
//now s3 is “HelloWorld”
The + operator could able to perform addition of two numbers and concatenation of
two strings because, it has been overloaded internally. C# allows the programmer to
overload the intrinsic operators so as to work on objects of user-defined/custom types
(may be classes or structures etc.). This method is known as operator overloading.
To illustrate the process of overloading binary operators, assume the following simple
Point structure:
public struct Point
{
private int x, y;
public Point(int xPos, int yPos)
{
x = xPos;
y = yPos;
}
public override string ToString()
{
return string.Format("[{0}, {1}]", this.x, this.y);
}
// overloaded operator +
public static Point operator + (Point p1, Point p2)
{
return new Point(p1.x + p2.x, p1.y + p2.y);
}
// overloaded operator public static Point operator - (Point p1, Point p2)
{
return new Point(p1.x - p2.x, p1.y - p2.y);
}
static void Main(string[] args)
{
Point ptOne = new Point(100, 100);
Point ptTwo = new Point(40, 40);
Console.WriteLine("ptOne = {0}", ptOne);
Console.WriteLine("ptTwo = {0}", ptTwo);
Point p1=ptOne + ptTwo;
Point p2=ptOne-ptTwo;
Console.WriteLine("p1: {0} ", p1.ToString());
Console.WriteLine("p2: {0} ", p2.ToString());
Console.ReadLine();
}
}
In the similar manner, one can overload all binary, unary and relational operators.
2.41 Internal Representation of Overloaded Operators
Like any C# programming element, overloaded operators are represented using
specific CIL instructions. That is, any operator that you may overload equates to a
specially named method in terms of CIL. The following table lists few C# operator-toCIL mapping for the C# operators.
Intrinsic C#
Operator
CIL
Representation
Intrinsic C#
Operator
!
CIL
Representation
--
op_Decrement()
op_LogiaclNot()
++
op_Increment
Binary +
op_Addition()
Unary -
op_UnaryNegation()
Binary -
op_Subtraction()
Unary +
op_UnaryPlus()
Binary *
op_Multiply()
<
op_LessThan()
/
op_Division()
2.42 Creating Custom Conversion Routines
C# provides two keywords, explicit and implicit, that you can use to control how your
types respond during an attempted conversion. Assume you have the following
structure definitions:
public struct Rectangle
{
// Public for ease of use;
// however, feel free to encapsulate with properties.
public int Width, Height;
public void Draw()
{
Console.WriteLine("Drawing a rect.");
}
public override string ToString()
{
return string.Format("[Width = {0}; Height = {1}]", Width, Height);
}
}
public struct Square
{
public int Length;
public void Draw()
{
Console.WriteLine("Drawing a square.");
}
public override string ToString()
{
return string.Format("[Length = {0}]", Length);
}
// Rectangles can be explicitly converted into Squares.
public static explicit operator Square(Rectangle r)
{
Square s;
s.Length = r.Width;
return s;
}
}
Notice that this iteration of the Rectangle type defines an explicit conversion operator.
Like the process of overloading an operator, conversion routines make use of the C#
operator keyword (in conjunction with the explicit or implicit keyword) and must be
defined as static. The incoming parameter is the entity you are converting from, while
the return value is the entity you are converting to:
public static explicit operator Square(Rectangle r)
{...}
In any case, the assumption is that a square (being a geometric pattern in which all
sides are of equal length) can be obtained from the width of a rectangle. Thus, you are
free to convert a Rectangle into a Square as so:
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Custom Conversions *****\n");
// Create a 5 * 10 Rectangle.
Rectangle rect;
rect.Width = 10;
rect.Height = 5;
Console.WriteLine("rect = {0}", rect);
// Convert Rectangle to a 10 * 10 Square.
Square sq = (Square)rect;
Console.WriteLine("sq = {0}", sq);
Console.ReadLine();
}
While it may not be all that helpful to convert a Rectangle into a Square within the
same scope, assume you have a function that has been prototyped to take Square
types.
// This method requires a Square type.
private static void DrawSquare(Square sq)
{
sq.Draw();
}
Using your explicit conversion operation, you can safely pass in Square types for
processing:
static void Main(string[] args)
{
...
// Convert Rectangle to Square to invoke method.
DrawSquare((Square)rect);
}
2.43 Defining Implicit Conversion Routines
Now we will discuss implicit conversion. Consider following situation:
static void Main(string[] args)
{
...
// Attempt to make an implicit cast
Square s3;
s3.Length = 83;
Rectangle rect2 = s3;
}
As you might expect, this code will not compile, given that you have not provided an
implicit conversion routine for the Rectangle type. Now here is the catch: it is illegal to
define explicit and implicit conversion functions on the same type, if they do not differ
by their return type or parameter set. This might seem like a limitation; however, the
second catch is that when a type defines an implicit conversion routine, it is legal for
the caller to make use of the explicit cast syntax! To clear things up, let’s add an
implicit conversion routine to the Rectangle structure using the C# implicit keyword
(note that the following code assumes the width of the resulting Rectangle is computed
by multiplying the side of the Square by 2):
public struct Rectangle
{
...
public static implicit operator Rectangle(Square s)
{
Rectangle r;
r.Height = s.Length;
r.Width = s.Length * 2;
return r;
}
}
With this update, you are now able to convert between types as follows:
static void Main(string[] args)
{
...
// Implicit cast OK!
Square s3;
s3.Length= 83;
Rectangle rect2 = s3;
Console.WriteLine("rect2 = {0}", rect2);
DrawSquare(s3);
// Explicit cast syntax still OK!
Square s4;
s4.Length = 3;
Rectangle rect3 = (Rectangle)s4;
Console.WriteLine("rect3 = {0}", rect3);
...
}
Again, be aware that it is permissible to define explicit and implicit conversion
routines for the same type as long as their signatures differ. Thus, you could update
the Square as follows:
public struct Square
{
...
// Can call as: Square sq2 = (Square)90;
or as:
// Square sq2 = 90;
public static implicit operator Square(int sideLength)
{
Square newSq;
newSq.Length = sideLength;
return newSq;
}
// Must call as: int side = (int)mySquare;
public static explicit operator int (Square s)
{
return s.Length;
}
}
2.44 Summary
The purpose of this unit was to round out your understanding of the C# programming
Language. We began by examining a small set of lesser known keywords (like sizeof,
checked, unsafe etc.) and during the process came to learn how to work with raw
pointer types. Then we investigated various advanced type construction techniques
(indexer, overloading and custom conversion). As you have seen, each of these
constructs can be triggered from languages other than C#, directly or indirectly.
2.45 Keywords






The keywords like checked, unchecked, unsafe, stackalloc, fixed, volatile, sizeof
and lock.
Overflow
Pinning
Indexer
Operator overloading
Explicit and Implicit conversions
2.46 Exercises
1. Explain the purpose of checked keyword with an example.
2. What do you mean by unchecked? Illustrate with an example.
3. List out the situations where we need unsafe code.
4. Illustrate the usage of unsafe keyword with an example.
5. How do you make use of * and & operators while handling pointers in C#?
6. Write the functions for swapping two numbers in both safe mode and unsafe mode.
7. Explain the purpose of fixed keyword with an example.
8. Define an indexer. Write a program to illustrate indexer.
9. Write a program to depict the overloading of operators + and -.
10. Explain the usage of the keyword explicit with an example.
11. Write a code segment to illustrate the working of implicit conversion.
Module 4
Unit 3
CONTENTS:
1.61
3.2
Objectives
Introduction
3.3 An Overview of .NET Assemblies
3.4 Core Benefits of Assemblies
3.5 Building a Single File Test Assembly
3.6 Exploring the Manifest
3.7 Building a Multifile Assembly
3.8 Summary
3.10
Keywords
3.11
Exercises
3.1 Objectives
At the end of this lesson, the students will understand the following tasks in detail:

The concept of .NET assemblies

The basics of single file and multifile assemblies

The logical and physical views of assemblies

The benefits of using assemblies

A single file test assembly

The core of manifest and CIL instructions

An example for multifile assembly and its construction
3.34 Introduction
Each of the applications developed in the previous modules are along the lines of
traditional stand-alone applications, given that all programming logic in contained
within a single *.exe. One major aspect of .NET is the notion of binary reuse. .NET
provides the ability to access types located in external binaries in a languageindependent manner.
However, the .NET platform provides far greater language
integration. For example, the .NET platform supports cross-language inheritance (e.g.,
a Visual Basic .NET class deriving from a C# class).
To understand how this is
achieved requires a deeper understanding of assemblies. In this unit, you understand
the logical and physical layout of an assembly.
3.35 An Overview of .NET Assemblies
An assembly is a versioned, self-describing binary (*.dll or *.exe) containing some
collection of types (classes, interfaces, structures etc.) and optional resources (images,
string tables etc.). A .NET binary/assembly consists of five major elements:

A standard Windows file header (or Win32 header)

A CLR header that marks the file as a managed module

CIL code

Type metadata

The assembly manifest
The Win32 header is just above identical to that of an unmanaged binary and is
simply used to identify that the module is usable by the Windows operating system.
This header also identifies the type of application to be launched. The CLR header is a
block of information that all .NET files must support to be loaded by the CLR. This
header defines numerous flags that enable the runtime to understand the layout of
the managed file.
CIL code is a platform and CPU agnostic. At runtime, the internal CIL is compiled to
platform and CPU specific instructions. The type metadata completely describes each
type defined within the current assembly as well as the set of eternal types referenced
by this assembly. The manifest documents each module within the assembly,
establishes the version of the assembly and also documents any external assemblies
referenced by the current assembly. Given all these elements, a .NET assembly is a
completely self-describing entity.
Single File and Multifile Assemblies
An assembly can be composed of multiple modules. A module is a generic name for a
valid .NET file. Thus, an assembly can be viewed as a unit of versioning and
deployment (termed as logical DLL). In most situations, an assembly is composed of a
single module. In this case, there is a one-to-one correspondence between the (logical)
assembly and the underlying (physical) binary as shown in the following diagram.
A Single File Assembly
Test.dll
Manifest
Type Metadata
CIL Code
(Optional) Resources
When you create an assembly that is composed of multiple files, you gain a more
efficient way to download content. For example, assume you have a remote client tat
is referencing a multifile assembly composed of three modules, one of which is
automatically installed with the client. If the client references another of the remote
modules, the .NET runtime will download the file on demand. If each module is large
in size, then multifile assembly will help to more extent.
Note that, multifile assemblies are not literally linked together into a new (larger) file.
Rather, multifile assemblies are logically related by information contained in the
corresponding manifest. Multifile assemblies contain a single manifest that may be
placed in a sand-alone file, but is more typically bundled directly into the primary
module. The following diagram depicts a multifile assembly.
A Multifile Assembly
Test.dll
Bar.netmodule
Manifest
(References other
related files)
Type Metadata
Type Metadata
CIL Code
Qaaz.netmodule
CIL Code
CompanyLogo.bmp
Type Metadata
CIL Code
Two Views of an Assembly: Physical and Logical
As you begin to work with .NET binaries, it can be helpful to regard an assembly (both
single file and multifile) as having two conceptual views. When you build an assembly,
you are interested in the physical view. In this case, the assembly can be realized as
some number of files that contain your custom types and resources as shown below:
Physical View of an Assembly
Test.dll
Resource Files
Bar.netmodule
Manifest
As an assembly consumer, you are typically interested in a logical view of the
assembly as shown below. In this case, you can understand an assembly as a
versioned collection of public types that you can use in your current application.
Logical View of an Assembly
Classes
Enumerations
Delegates
Interfaces
Resources
Structures
3.36 Core Benefits of Assemblies
The assembly format of files has some benefits as explained below:

Assemblies Promote Code Reuse: As you have been building your console
applications over the previous chapters, it may have seemed that all of the
applications’ functionality was contained within the executable assembly you
were constructing. In reality, your applications were leveraging numerous types
contained within the always accessible .NET code library, mscorlib.dll (recall
that the C# compiler references mscorlib.dll automatically), as well as
System.Windows.Forms.dll. As you may know, a code library (also termed a
class library) is a *.dll that contains types intended to be used by external
applications. When you are creating executable assemblies, you will no doubt
be leveraging numerous system-supplied and custom code libraries as you
create the application at hand. Do be aware, however, that a code library need
not take a *.dll file extension. It is perfectly possible for an executable assembly
to make use of types defined within an external executable file. In this light, a
referenced *.exe can also be considered a “code library.” Regardless of how a
code library is packaged, the .NET platform allows you to reuse types in a
language-independent manner. For example, you could create a code library in
C# and reuse that library in any other .NET programming language. It is
possible to not only allocate types across languages, but derive from them as
well. A base class defined in C# could be extended by a class authored in Visual
Basic .NET. Interfaces defined in Pascal .NET can be implemented by structures
defined in C#, and so forth. The point is that when you begin to break apart a
single monolithic executable into numerous .NET assemblies, you achieve a
language-neutral form of code reuse.

Assemblies Establish a Type Boundary: In the module 2, you learned about
the formalities behind .NET namespaces. Recall that a type’s fully qualified
name is composed by prefixing the type’s namespace (e.g., System) to its name
(e.g., Console). Strictly speaking however, the assembly in which a type resides
further establishes a type’s identity. For example, if you have two uniquely
named assemblies (say, MyCars.dll and YourCars.dll) that both define a
namespace (CarLibrary) containing a class named SportsCar, they are
considered unique types in the .NET universe.

Assemblies Are Versionable Units: .NET assemblies are assigned a four-part
numerical version number of the form <major>.<minor>. <build>.<revision> (if
you do not explicitly provide a version number using the [AssemblyVersion]
attribute, the assembly is automatically assigned a version of 0.0.0.0). This
number, in conjunction with an optional public key value, allows multiple
versions of the same assembly to coexist in harmony on a single machine.
Formally speaking, assemblies that provide public key information are termed
strongly named. Using a strong name, the CLR is able to ensure that the correct
version of an assembly is loaded on behalf of the calling client.

Assemblies Are Self-Describing: Assemblies are regarded as self-describing in
part because they record every external assembly it must have access to in
order to function correctly. Thus, if your assembly requires System.Windows.
Forms.dll and System.Drawing.dll, they will be documented in the assembly’s
manifest. Recall that a manifest is a blob of metadata that describes the
assembly itself (name, version, external assemblies, etc.). In addition to
manifest data, an assembly contains metadata that describes the composition
(member names, implemented interfaces, base classes, constructors and so
forth) of every contained type. Given that an assembly is documented in such
vivid detail, the CLR does not consult the Win32 system registry to resolve its
location (quite the radical departure from Microsoft’s legacy COM programming
model). The CLR makes use of an entirely new scheme to resolve the location of
external code libraries.

Assemblies Are Configurable: Assemblies can be deployed as “private” or
“shared.” Private assemblies reside in the same directory (or possibly a
subdirectory) as the client application making use of them. Shared assemblies,
on the other hand, are libraries intended to be consumed by numerous
applications on a single machine and are deployed to a specific directory termed
the Global Assembly Cache (GAC). Regardless of how you deploy your
assemblies, you are free to author XML-based configuration files. Using these
configuration files, the CLR can be instructed to “probe” for assemblies under a
specific location, load a specific version of a referenced assembly for a particular
client, or consult an arbitrary directory on your local machine, your network
location, or a web-based URL.

Assemblies Define a Security Context: An assembly may also contain
security details. Under the .NET platform, security measures are scoped at the
assembly level.
For example, if AssemblyA wishes to use a class contained
within AssemblyB, then AssemblyB is the entity that chooses to provide access
(or not). The security constrains defined by an assembly are explicitly listed
within its manifest.
While a treatment of .NET security measures is outside
the mission of this text, simply be aware that access to an assembly’s contents
is verified using assembly metadata.

Assemblies Enable Side-by-Side Execution: Perhaps the biggest advantage of
the .NET assembly is the ability to install and load multiple versions of the
same assembly on a single machine. In this way, clients are isolated from other
incompatible versions of the same assembly. Further more, it is possible to
control which version of a (shared) assembly should be loaded using application
configuration files.
These files are little more than a simple text file describing
(via XML syntax) the version, and specific location, of the assembly to be loaded
on behalf of the calling application.
3.37 Building a Single File Test Assembly
Now, you have a better understanding of the benefits provided by .NET assemblies,
let’s build a minimal and complete code library using C#. Physically, this will be a
single file assembly named CarLibrary. Logically, this binary will contain a handful of
public types for consumption by other .NET binaries.
The design of our automobile library begins with an abstract base class named Car
that defines a number of protected data members exposed through custom properties.
This class has a single abstract method named TurboBoost() and makes use of a
single enumeration (EngineState).
Here is the initial definition of the CarLibrary
namespace.
using System;
namespace CarLibrary
{
// Represents the state of the engine.
public enum EngineState
{
engineAlive, engineDead
}
// The abstract base class in the hierarchy.
public abstract class Car
{
protected string petName;
protected short currSpeed;
protected short maxSpeed;
protected EngineState egnState = EngineState.engineAlive;
public abstract void TurboBoost();
public Car(){}
public Car(string name, short max, short curr)
{
petName = name;
maxSpeed = max;
currSpeed = curr;
}
public string PetName
{
get { return petName; }
set { petName = value; }
}
public short CurrSpeed
{
get { return currSpeed; }
set { currSpeed = value; }
}
public short MaxSpeed
{
get { return maxSpeed; }
}
public EngineState EngineState
{
get { return egnState; }
}
}
}
Now assume that you have two direct descendents of the Car type named MiniVan and
SportsCar. Each overrides the abstract TurboBoost() method in an appropriate
manner.
using System;
using System.Windows.Forms;
//needed for MessageBox definition
namespace CarLibrary
{
public class SportsCar : Car
{
public SportsCar(){ }
public SportsCar(string name, short max, short curr): base (name,
max, curr){ }
public override void TurboBoost()
{
MessageBox.Show("Ramming speed!", "Faster is better...");
}
}
public class MiniVan : Car
{
public MiniVan(){ }
public MiniVan(string name, short max, short curr): base (name,
max, curr){ }
public override void TurboBoost()
{
// Minivans have poor turbo capabilities!
egnState = EngineState.engineDead;
MessageBox.Show("Time to call AAA", "Your car is dead");
}
}
}
3.38 Exploring the Manifest
After creating a single file code library, the next task will be understanding assembly
construction. Recall that every assembly contains an associated manifest. The
manifest contains bits of metadata that specify the name and version of the assembly,
as well as a listing of all internal and external modules that compose the assembly as
a whole.
Additionally,
a
manifest
may
contain
cultural
information
(used
for
internationalization), a corresponding strong name (required by shared assemblies)
and optional resource information. The following table describes some of the key bits
of functionality lurking within an assembly manifest.
Manifest-Centric
Meaning
Information
Assembly Name
A text string specifying the assembly’s name.
Version Number
A major and minor version number, and a
revision and build number.
Strong name
information
In part, the strong name of an assembly
consists of a public key maintained by the
publisher of the assembly.
List of all modules in A hash of each module contained in the
assembly (in the case of a single file assembly,
the assembly
there will only be a single module listing).
Information on
referenced assemblies
A list of other assemblies that are statistically
referenced by the assembly.
.NET aware compilers (such as csc.exe) automatically create a manifest at compile
time. CIL instructions are created within the assembly manifest. The following table
lists the core CIL tokens created for a manifest.
Manifest Tag
Meaning
.assembly
Marks the assembly declaration, indicating that the
file is an assembly.
.file
Marks additional files in a multifile assembly.
.class extern
Classes exported by the assembly but declared in
another module (only used with a multifile assembly).
.manifestres
Indicates the manifest resources, if any.
.module
Module declaration, indicating that the file is a
module (i.e. a .NET binary with no assembly level
manifest) and not the primary assembly.
.assembly extern
The assembly reference indicates another assembly
containing items referenced by this module.
.publickey
Contains the actual bytes of the public key.
.publickeytoken
Contains a token of the actual public key.
3.39 Building a Multifile Assembly
Now that we have explored the internals of a single file assembly, let’s turn our
attention to the process of building a multifile assembly. Recall that a multifile
assembly will contain a particular *.dll or *.exe file that contains the assembly
manifest (often termed as the primary module).
Additionally, multifile assemblies
contain any number of *.netmodule files that are loaded on demand when referenced
by an external client. Do be aware that the use of *.netmodule is simply a naming
convention. If you wish, your auxiliary modules could simply take a *.dll file extension.
The Visual Studio.NET does not support a project workspace type that allows you to
build stand-alone *.netmodule files. Therefore, you need to drop down to the level of
the raw csc.exe compiler and specify the correct flags manually.
To keep things simple, let’s build some rather basic types contained within a multifile
assembly named AirVehicles. The primary airvehicles.dll module will contain CIL and
metadata for a single class type named Helicopter.
The related manifest (also
contained in airvehicles.dll) catalogues and additional *.netmodule file named
ufos.netmodule, which contains another class type named UFO. Although both class
types are physically contained in separate binaries, you will group them into a unified
namespace named AirVehicles. Finally, both classes are created using C#.
To begin, open notepad.exe and create a trivial class definition name UFO within a file
named ufo.cs:
using System;
namespace AirVehicles
{
public class UFO
{
public void AbductHuman()
{
Console.WriteLine(“Resistance is futile”);
}
}
}
To compile this class into a .NET module, open a command prompt and issue the
following command to the C# compiler:
csc.exe /t:module ufo.cs
If you now look in the folder containing the ufo.cs file, you should see a new file
named ufo.netmodule. Next, create a new file named helicopter.cs:
using System;
namespace AirVehicles
{
public class Helicopter
{
public void TakeOff()
{
Console.WriteLine(“Helicopter taking off!!”);
}
}
}
Given that AirVehicles.dll is the primary module of this multifile assembly, you will
need to specify the /t:library flag.
However, as you also want to encode the
ufo.netmodule binary into the assembly manifest, you must also specify the
/addmodule flag. The following command does the trick:
csc /t:library /addmodule:ufo.netmodule /out:airvehicles.dll helicopter.cs
At this point, your directory should contain the primary arivehicles.dll module as well
as the secondary ufo.netmodule binaries.
3.40 Summary
This chapter drilled into the details behind the innocent-looking .NET dlls and exes
located on your development machine. You began the journey by examining the core
concepts of the assembly: CLR headers, metadata, manifests and CIL. As illustrated,
.NET supports the notion of cross-language inheritance.
You have seen the concept and working of single file assembly and multifile
assemblies. Also, you have understood the core of manifest and CIL instructions.
3.41 Keywords

File Header

Module

CIL (Common Intermediate Language)

Metadata

Manifest

Physical View of Assembly

Logical View of Assembly

Code Library /Class Library

Language-Neutral Form

Fully Qualified Name

Public Key Value

Strongly named assembly

Global Assembly Cache (GAC)

Primary module
3.42 Exercises
1. What are the major elements of an assembly? Explain.
2. With a neat diagram, explain the concepts of single file and multifile
assemblies.
3. What do you mean by logical and physical views of an assembly? Explain.
4. Bring out the core benefits of assemblies.
5. Write a C# code to illustrate single file assembly and explain the same.
6. List out the information contained by the assembly manifest.
7. List out the core CIL tokens created for a manifest.
8. How do you build a multifile assembly? Explain.
Module 4
Unit 4
CONTENTS:
1.62
4.2
Objectives
Introduction
4.3 Understanding Private Assemblies
4.4
Probing for Private Assemblies (The Basics)
4.5
Private Assemblies and XML Configuration Files
4.6
Probing for Private Assemblies (The Details)
4.7 Understanding Shared Assembly
4.8 Understanding Strong Names
4.9 Building a Shared Assembly
4.10
Understanding Delayed Signing
4.11
Installing/Removing Shared Assembly
4.12
Using a Shared Assembly
4.13
Summary
4.14
Keywords
4.15
Exercises
7.1 Objectives
At the end of this lesson, the students will understand:

The meaning of private assemblies and XML configuration

The concept of probing

Shared assemblies and its detailed study

Strong Names and signing
4.34 Introduction
In the previous unit, you have understood the concept of assemblies. Once you
understand the logical and physical layout of an assembly, you learn the distinction
between private and shared assemblies as well as single file and multifile assemblies.
Here, you examine exactly how .NET runtime resolves the location of an assembly and
come to understand the role of the Global Assembly Cache (GAC) and application
configuration files (*.config). As you will see, *.config files may be used by a client
application to interact with the assembly binding process.
4.35 Understanding Private Assemblies
Formally speaking, every assembly is deployed as private or shared. Each variable has
the same underlying structure (i.e., some number of modules and an associated
manifest). Furthermore, each flavor of assembly provides the same kind of services
(access to some number of public types). The real differences between a private and
shared assembly boil down to naming conventions, versioning policies and deployment
issues.
Private assemblies are a collection of modules that is only used by the application with
which it has been deployed. For example, CarLibrary.dll seen in previous unit is a
private assembly. When you create and deploy a private assembly, the assumption is
that the collection of types is only used by the owning application and not shared with
other applications on the system.
Private assemblies are required to be located within the same directory as the client
application (termed the application directory) or a subdirectory thereof. For example,
when you set a reference to the CarLibrary.dll assembly, the Visual Studio .NET IDE
responded by making a full copy of the assembly that was placed in your project’s
application directory. This is the default behavior, as private assemblies are assumed
to be the deployment option of choice.
The resolution and loading of the private CarLibrary.dll happens by virtue of the fact
that the assembly is placed in the application directory. Uninstalling (or replicating)
an application that makes exclusive use of private assemblies is a no-brainer: simply
delete (or copy) the application folder. Unlike with COM applications, you do not need
to worry about dozens of orphaned registry settings. More important, you do not need
to worry that the removal of private assemblies will break any other applications on
the machine.
The full identity of a private assembly consists of the friendly name and numerical
version, both of which are recorded in the assembly manifest. The friendly name
simply is the name of the module that contains the assembly’s manifest minus the file
extension. For example, if you examine the manifest of the CarLibrary.dll assembly,
you find the following (your version will no doubt differ):
.assembly CarLibrary
{
...
.ver 1:0:454:30104
}
Given the isolated nature of a private assembly, it should make sense that the CLR
does not bother to make use of the version number when resolving its location. The
assumption is that private assemblies do not need to have any elaborate version
checking, as the client application is the only entity that “knows” of its existence.
Given this, it is (very) possible for a single machine to have multiple copies of the same
private assembly in various application directories.
4.36 Probing for Private Assemblies(The Basics)
The .NET runtime resolves the location of a private assembly using a technique termed
probing, which is much less invasive than it sounds. Probing is the process of mapping
an external assembly reference (i.e. [.assembly extern]) to the correct corresponding
binary file. For example, when the runtime reads the following line from the
CSharpCarClient manifest:
.assembly extern CarLibrary
{……}
a search is made in the application directory for a file named CarLibrary.dll. If a *.dll
binary cannot be located, an attempt is made to locate an *.exe version. If neither if
these files can be located in the application directory, the runtime throws an
exception.
However, XML configuration files (*.config) can be used to instruct the
runtime to probe in other locations beyond the application directory.
4.37 Private Assemblies and XML Configuration Files
While it is possible to deploy a .NET application by simply copying all required
assemblies to a single folder on the user’s hard drive, you will most likely wish to
define a number of subdirectories to group related content. For example, assume you
have an application directory named C:\MyApp that contains CSharpCarClient.exe.
Under
this
folder
might
be
a
subfolder
named
MyLibraries
that
contains
CarLibrary.dll.
Regardless of the intended relationship between these two directories, the CLR will not
probe
the
MyLibraries
subdirectory
unless
you
supply
a
configuration
file.
Configuration files contain various XML elements that allow you to influence the
probing process. By “law,” configuration files must have the same name as the
launching application and take a *.config file extension, and they must be deployed in
the client’s application directory. Thus, if you wish to create a configuration file for
CSharpCarClient.exe, it must be named CSharpCarClient.exe.config.
To illustrate the process, create a new directory on your C drive named MyApp using
Windows Explorer. Next, copy CSharpCarClient.exe and CarLibrary.dll to this new
folder, and run the program by double-clicking the executable. Your program should
run successfully at this point (remember, the assemblies are not registered!). Next,
create
a
new
subdirectory
under
C:\MyApp named MyLibraries,
and
move
CarLibrary.dll to this location.
Try to run your client program again. Because the CLR could not locate “CarLibrary”
directly within the application directory, you are presented with a rather nasty
unhandled FileNotFound exception. To rectify the situation, create a new configuration
file named CSharpCarClient.exe.config and save it in the same folder containing the
CSharpCarClient.exe application, which in this example would be C:\MyApp. Open
this file and enter the following content exactly as shown (be aware that XML is case
sensitive!):
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyLibraries"/>
</assemblyBinding>
</runtime>
</configuration>
.NET *.config files always open with a root element named <configuration>. The nested
<runtime> element may specify an <assemblyBinding> element, which nests a further
element named <probing>. The privatePath attribute is the key point in this example,
as it is used to specify the subdirectories relative to the application directory where the
CLR should probe.
Do note that the <probing> element does not specify which assembly is located under
a given subdirectory. In other words, you cannot say, “CarLibrary is located under the
MyLibraries subdirectory, but MathUtils is located under Bin subdirectory.” The
<probing> element simply instructs the CLR to investigate all specified subdirectories
for the requested assembly until the first match is encountered.
Multiple subdirectories can be assigned to the privatePath attribute using a
semicolon-delimited list. You have no need to do so at this time, but here is an
example that informs the CLR to consult the MyLibraries and MyLibraries\Tests client
subdirectories:
<probing privatePath="MyLibraries; MyLibraries\Tests"/>
Once you’ve finished creating CSharpCarClient.exe.config, run the client by doubleclicking
the
executable
in
Windows
Explorer.
You
should
find
that
CSharpCarClient.exe executes without a hitch (if this is not the case, double-check it
for typos).
Next, for testing purposes, change the name of your configuration file (in one way or
another) and attempt to run the program once again. The client application should
now fail. Remember that *.config files must be prefixed with the same name as the
related client application. By way of a final test, open your configuration file for editing
and capitalize any of the XML elements. Once the file is saved, your client should fail
to run once again (as XML is case sensitive).
4.38 Probing for Private Assemblies (The Details)
To wrap up our discussion of private assemblies, let’s formalize the specific steps
involved in binding to a private assembly at runtime.
First, a request to load an
assembly may be either explicit or implicit. An implicit load request occurs whenever
the manifest makes a direct reference to some external assembly. The external
references are marked with [.assembly extern] token:
//an implicit load request
.assembly extern CarLibrary
{
…….
}
An explicit load request occurs programmatically using the Load() or LoadFrom()
method of the System.Reflection.Assembly class type, typically for the purposes of late
binding and dynamic invocation of type members. The Load() method allows you to
specify the name, version, public key token and culture information syntactically. You
can see an example of an explicit load request in the following code:
// An explicit load request.
Assembly asm = Assembly.Load("CarLibrary");
Collectively, the name, version, public key token and cultural information is termed as
assembly reference (or simply AsmRef). The entity in charge of locating the correct
assembly based on an AsmRef is termed the assembly resolver, which is a facility of
the CLR. If the resolver determines the AsmRef refers to a private assembly (i.e. no
public key token is specified), the following steps are followed:
1. First, the assembly resolver attempts to locate the assembly in the client’s
application directory (looking for a *.dll file on the first pass, followed by an
*.exe file).
2. If the AsmRef cannot be resolved in looking in the application directory, the
assembly resolver will attempt to locate a configuration file in the application
directory. If a configuration file exists, the runtime will attempt to locate the
private assembly using the <probing> element.
3. If the assembly cannot be found within the application directory (or a
specified subdirectory), the search stops here and a FileNotFound exception
is raised.
Again, the location of a private assembly is fairly simple to resolve.
4.39 Understanding Shared Assemblies
Now that you understand how to deploy and configure a private assembly, you can
begin to examine the role of a shared assembly. Like a private assembly, a shared
assembly is a collection of types and (optional) resources. The most obvious difference
between shared and private assemblies is the fact that a single copy of a shared
assembly can be used by several applications on a single machine.
Consider all the applications created in this text that required you to set a reference to
System. Windows.Forms.dll. If you were to look in the application directory of each of
these clients, you would not find a private copy of this .NET assembly. The reason is
that System.Windows.Forms.dll has been deployed as a shared assembly. Clearly, if
you need to create a machine-wide class library, this is the way to go. As suggested in
the previous paragraph, a shared assembly is not deployed within the same directory
as the application making use of it. Rather, shared assemblies are installed into the
Global Assembly Cache (GAC). The GAC is located under a subdirectory of your
Windows directory named Assembly (e.g., C:\Windows\Assembly).
4.40 Understanding Strong Names
Before you can deploy an assembly to the GAC, you must assign it a strong name,
which is used to uniquely identify the publisher of a given .NET binary. Understand
that a “publisher” could be an individual programmer, a department within a given
company, or an entire company at large.
In some ways, a strong name is the modern day .NET equivalent of the COM globally
unique identifier (GUID) identification scheme. If you have a COM background, you
may recall that AppIDs are GUIDs that identify a particular COM application. Unlike
COM GUID values (which are nothing more than 128-bit numbers), strong names are
based (in part) on two cryptographically related keys (termed the public key and the
private key), which are much more unique and resistant to tampering than a simple
GUID.
Formally, a strong name is composed of a set of related data, much of which is
specified using assembly-level attributes:

The friendly name of the assembly (which you recall is the name of the
assembly minus the file extension)

The version number of the assembly (assigned using the [AssemblyVersion]
attribute)

The public key value (assigned using the [AssemblyKeyFile] attribute)

An optional culture identity value for localization purposes (assigned using the
[AssemblyCulture] attribute)

An embedded digital signature created using a hash of the assembly’s contents
and the private key value
To provide a strong name for an assembly, your first step is to generate public/private
key data using the .NET Framework 2.0 SDK’s sn.exe utility (which you’ll do
momentarily). The sn.exe utility responds by generating a file (typically ending with the
*.snk [Strong Name Key] file extension) that contains data for two distinct but
mathematically related keys, the “public” key and the “private” key. Once the C#
compiler is made aware of the location for your *.snk file, it will record the full public
key value in the assembly manifest using the .publickey token at the time of
compilation.
The C# compiler will also generate a hash code based on the contents of the entire
assembly (CIL code, metadata, and so forth). A hash code is a numerical value that is
unique for a fixed input. Thus, if you modify any aspect of a .NET assembly (even a
single character in a string literal) the compiler yields a unique hash code. This hash
code is combined with the private key data within the *.snk file to yield a digital
signature embedded within the assembly’s CLR header data. The process of strongly
naming an assembly is illustrated in the following figure:
CarLibrary.dll
Manifest with Public Key
Assembly
Hash Code
Type Metadata
Digital Signature
+
Private
Key Data
=
Digital
Signature
Understand that the actual private key data is not listed anywhere within the
manifest, but is used only to digitally sign the contents of the assembly (in conjunction
with the generated hash code). Again, the whole idea of making use of public/private
key cryptography is to ensure that no two companies, departments, or individuals
have the same identity in the .NET universe. In any case, once the process of
assigning a strong name is complete, the assembly may be installed into the GAC.
4.41 Building a Shared Assembly
To illustrate the process of assigning a strong name to an assembly, let us consider an
example. Assume you have created a new C# Class Library named SharedAssembly,
which contains the following class definition:
public class VWMiniVan
{
private bool x=false;
public VWMiniVan()
{
MessageBox.Show(“Using Version 2.0”, “Shared Car”);
}
public void PlayTune()
{
MessageBox.Show(“What a long drive …”);
}
public bool Busted
{
get { return x;}
set { x=value;}
}
}
To generate the key file, you need to make use of the sn.exe (strong name) utility.
Although this tool has numerous command line options, now, you have to use “-k”
flag, which instructs the tools to generate a new *.snk file that contains the
public/private key information.
The next step is to inform the C# compiler exactly where the *.snk file is located to
record the public key in the assembly manifest. When you create a new C# project
workspace, you will notice that one of your initial project files is named
“AssemblyInfo.cs”.
assembly itself.
This file contains a number of attributes that describe the
One attribute that may appear within this file is named
AssemblyKeyFile. Simply update the initial empty value with a string specifying the
location of your *.snk file, for example:
[assembly: AssemblyKeyFile(@ “C:\MyKey\myKey.snk”)]
Given that the version of a shared assembly is of prime importance, let us specify a
fixed numerical value. In the same AssemblyInfo.cs file, you will find another attribute
named AssemblyVersion. Initially the value is set of “1.0.*”:
[assembly: AssemblyVersion (“1.0.*”)]
Every new C# projects begins life versioned at 1.0.*. Recall that a .NET version number
is composed of the four parts (<major>.<minor>.<build>.<revision>). Until you say
otherwise, VS.NET automatically increments the build and revision numbers as part of
each compilation. To enforce a fixed value for the assembly’s build version, simply
update accordingly:
[assembly: AssemblyVersion(“1.0.0.0”)]
Using these two assembly-level attributes, the C# compiler now merges the necessary
information into the corresponding manifest to establish you strong name, which can
be seen using ildasm.exe.
4.42 Understanding Delayed Signing
When you are building your own custom .NET assemblies, you are able to assign a
strong name using your own personal *.snk file. However, given the sensitive nature of
a public/private key file, don’t be too surprised if your company/department refuses
to give you access to the master *.snk file. This is an obvious problem, given that we
(as developers) will often need to install an assembly into the GAC for testing
purposes. To allow this sort of testing (while not distributing the true *.snk file), you
are able to make use of delayed signing. We have no need to do so for the current
CarLibrary.dll; however, here is an overview of the process. Delayed signing begins by
the trusted individual holding the *.snk file extracting the public key value from the
public/private *.snk file using the -p command-line flag of sn.exe, to produce a new
file that only contains the public key value:
sn -p myKey.snk testPublicKey.snk
At this point, the testPublicKey.snk file can be distributed to individual developers for
the creation and testing of strongly named assemblies. To inform the C# compiler that
the assembly in question is making use of delayed signing, the developer must make
sure to set the value of the AssemblyDelaySign attribute to true in addition to
specifying the pseudo-key file as the parameter to the AssemblyKeyFile attribute. Here
are the relevant updates to the project’s AssemblyInfo.cs file:
[assembly: AssemblyDelaySign(true)]
[assembly: AssemblyKeyFile(@"C:\MyKey\testPublicKey.snk)]
Once an assembly has enabled delayed signing, the next step is to disable the
signature verification process that happens automatically when an assembly is
deployed to the GAC. To do so, specify the -vr flag (using sn.exe) to skip the
verification process on the current machine:
sn.exe -vr MyAssembly.dll
Once all testing has been performed, the assembly in question can be shipped to the
trusted individual who holds the “true” public/private key file to resign the binary to
provide the correct digital signature. Again, sn.exe provides the necessary behavior,
this time using the -r flag:
sn.exe -r MyAssembly.dll C:\MyKey\myKey.snk
To enable the signature verification process, the final step is to apply the -vu flag:
sn.exe -vu MyAssembly.dll
Understand, of course, that if you (or your company) only build assemblies intended
for internal use, you may never need to bother with the process of delayed signing.
However, if you are in the business of building .NET assemblies that may be
purchased by external parties, the ability to delay signing keep things safe and sane
for all involved.
4.43 Installing/Removing Shared Assembly
The final step is to install SharedAssembly.dll into the GAC.
The simplest way to
install a shared assembly into the GAC is to drag-and-drop the *.dll onto the active
window using the Windows Explorer. Also, the .NET SDK provides a command line
utility named gacutil.exe (the /i flag is used to install the binary.
Once an assembly has been installed into the GAC, you may right-click a given
assembly icon to pull up a property page fro the binary, as well as delete the item from
the GAC altogether (the GUI equivalent of supplying the /u flag when using
gacutil.exe).
4.44 Using a Shared Assembly
Now let us make use of our shared assembly. Create a new C# Console Application
named SharedAssemblyClient.
Like any external assembly, you will need to set a
reference to the SharedAssembly binary using the Add Reference dialog. Understand,
however that you do not navigate to the GAC directory when referencing shared
binaries. The GAC is a runtime entity that is not intended to be accessed directly
during the development cycle.
Rather, navigate to the \Debug folder of the
SharedAssembly project using the Browse button.
At this point, exercise you
VWMiniVan as:
public class SharedAsmClient
{
public static void Main(strin[] args)
{
VWMiniVan v= new VWMiniVan();
v.PlayTune();
}
}
Once you have run your application, check out the client’s application directory using
the Windows Explorer. Recall, than when you reference a private assembly, the IDE
automatically creates a local copy of the assembly for use by the client application.
However, when you reference an assembly that contains a public key value, VS.NET
will not generate a local copy, given the assumption that assemblies supporting a
public key are typically shared and hence placed in the GAC.
4.45 Summary
As you have seen, assemblies may be private or shared, given that private assemblies
are the default. When you wish to configure a shared assembly, you are making an
explicit choice and need to generate a corresponding strong name. Both private and
shared assemblies can be configured declaratively using a client side *.config file or
alternatively, a publisher policy *.dll. This unit wrapped up by quickly examining a set
of related assembly-centric details: the machine.config file, the .NET configuration
untility to simplify the process of building XML configuration files etc.
4.46 Keywords

















Global Assembly Cache (GAC)
Private Assembly
Application Directory
Friendly Name
Probing
Configuration Files
Explicit and implicit load request
Assembly Reference
Assembly Resolver
Public Key Token
Shared Assembly
Strong names
Globally Unique Identifier (GUID)
Cryptographically related keys (public key and private key)
Digital Signature
Hash Code
Delayed Signing
4.47 Exercises
1. Explain the concept of private assemblies.
2. Define probing.
3. How do you make use of XML configuration files to handle private assemblies?
Explain with a code segment.
4. Define implicit load request with syntax.
5. When an explicit load request occurs? Explain with the syntax.
6. List out the steps taken by resolver to identify a public key token.
7. Explain the concept of shared assemblies.
8. What do you mean by strong names? Explain.
9. List out the entities that formulate a strong name.
10. Explain the process of strongly naming an assembly with a diagram.
11. How do you build a shared assembly? Explain with a code segment.
12. What is the need for delayed signing? Explain.
13. With the help of attributes used, explain the procedure for delayed signing.
14. Briefly explain the procedure for using a shared assembly with an example.
Download