Upload
manjunathajnaik
View
188
Download
4
Tags:
Embed Size (px)
Citation preview
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
.Net Programming
Syllabus
Module 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.
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.
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.
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,
R.V.COLLEGE OF ENGINEERING Page 1
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Defining Program Constants, C# Iteration Constructs, C# Controls Flow Constructs, The Complete set of C# Operators.
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.
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.
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.
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.
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.
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.
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.
R.V.COLLEGE OF ENGINEERING Page 2
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Understanding the IConvertible Interface, Building a Custom Enumerator (IEnummerable and Enumerator), Building Cloneable objects (ICloneable), Building Comparable Objects (IComparable), Exploring the System.Collections Namespace.
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.
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.
An Overview of .NET Assemblies, Core Benefits of Assemblies, Building a Single File Test Assembly, Exploring the Manifest, Building a Multifile Assembly.
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.
R.V.COLLEGE OF ENGINEERING Page 3
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
.NET Programming
Module 1
Unit 1
CONTENTS:
1.1 Objectives1.2 Introduction1.3 Understanding previous states of affairs1.4 The .Net Solution1.5 The Building Block of the .Net Platform (CLR, CTS and CLS)1.6 The Role of the .Net Base Class Libraries1.7 What C# Brings to the Table1.8 An Overview of .Net Binaries (or Assemblies)1.9 The Role of the Common Intermediate Language1.10 The Role of .Net Type Metadata1.11 The Role of the Assembly Manifest 1.12 Compiling CIL to Platform Specific Instructions1.13 Summary1.14 Keywords1.15 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
R.V.COLLEGE OF ENGINEERING Page 4
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 object-
oriented 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
R.V.COLLEGE OF ENGINEERING Page 5
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 6
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 7
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
cross-language 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.
R.V.COLLEGE OF ENGINEERING Page 8
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 9
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 10
The Base Class Libraries
Data Access GUI Security XML/SOAP
Threading FILE I/O Debugging (et al.)
The Common Language Runtime
Common Type SystemCommon Language Specification
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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++,
R.V.COLLEGE OF ENGINEERING Page 11
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 12
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Note: The terms IL, MSIL (Microsoft Intermediate Language) and CIL (Common
Intermediate Language) are all describing the same exact entity.
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.
R.V.COLLEGE OF ENGINEERING Page 13
C# Source Code
Perl to Perl .NET Source Code
COBOL to COBOL. NET Source Code
C++ to MC++ Source Code
MSIL
and
Metadata
(DLL or EXE)
C# Compiler
Perl to Perl .NET Compiler
COBOL to COBOL. NET Compiler
C++ to MC++ Compiler
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 14
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Employee.gif
Figure 1.4 MultiFile Assembly
R.V.COLLEGE OF ENGINEERING Page 15
Assembly Metadata
Resources
CIL Code
Type Metadata
CIL
Type Metadata
Assembly Manifest
CIL
Type Metadata
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 platform-
agnostic. 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
R.V.COLLEGE OF ENGINEERING Page 16
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 17
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 18
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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?
R.V.COLLEGE OF ENGINEERING Page 19
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Objectives2.2 Introduction2.3 Understanding the common type system2.4 Intrinsic CTS Data Types2.5 Understanding the Common Languages Specification2.6 Understanding the Common Language Runtime2.7 A tour of the .Net Namespaces2.8 Increasing Your Namespace Nomenclature 2.9 Deploying the .NET Runtime2.10 Summary2.11 Keywords2.12 Exercises
2.1 Objectives
R.V.COLLEGE OF ENGINEERING Page 20
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 21
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 MeaningIs the class “sealed” or not?
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.
R.V.COLLEGE OF ENGINEERING Page 22
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 23
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 24
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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
32-bit 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
R.V.COLLEGE OF ENGINEERING Page 25
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 26
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
VB.NET Keyword C# Keyword Managed Extensions for C+
+ KeywordSystem.Byte Byte byte unsigned char
System.SByte SByte sbyte signed charSystem.Int16 Short short shortSystem.Int32 Integer int int or longSystem.Int64 Long long __int64
System.UInt16 UShort ushort unsigned shortSystem.UInt32 UInteger uint unsigned int or
unsigned longSystem.UInt64 ULong ulong unsigned __int64
System.Single Single float FloatSystem.Double Double double DoubleSystem.Object Object object Object^System.Char Char char wchar_t
System.String String string String^System.Decimal Decimal decimal DecimalSystem.Boolean Boolean bool Bool
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
R.V.COLLEGE OF ENGINEERING Page 27
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 28
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
}
R.V.COLLEGE OF ENGINEERING Page 29
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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
R.V.COLLEGE OF ENGINEERING Page 30
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 31
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 32
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
R.V.COLLEGE OF ENGINEERING Page 33
.NET source code written
in some .NET-
aware Language
Base Class Libraries
(mscorlib.dll and so on)
Some .NET compiler
DLL or EXE Assembly
(CIL, Metadata and Manifest)
.NET Execution Engine (mscoree.dll)
Class Loader
Jitter
Platform-Specific
Instructions
Execute the application
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 34
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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. For
example, consider following programs written in C#, VB.NET and MC++.
// C# code
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
R.V.COLLEGE OF ENGINEERING Page 35
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
#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 MeaningSystem 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.
System.Collections These namespaces define a number of stock container objects
System.Collections.Generic (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.
System.DataSystem.Data.OdbcSystem.Data.OracleClientSystem.Data.OleDbSystem.Data.SqlClient
These namespaces are used for interacting with databases using ADO.NET.
System.Diagnostics Here, you find numerous types that can be used to
R.V.COLLEGE OF ENGINEERING Page 36
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
programmatically debug and trace your source code.System.DrawingSystem.Drawing.Drawing2DSystem.Drawing.Printing
Here, you find numerous types wrapping graphical primitives such as bitmaps, fonts, and icons, as well as printing capabilities.
System.IOSystem.IO.CompressionSystem.IO.Ports
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.
System.Net This namespace (as well as other related namespaces) contains types related to network programming (requests/responses, sockets, end points, and so on).
System.ReflectionSystem.Reflection.Emit
These namespaces define types that support runtime type discovery as well as dynamic creation of types.
System.Runtime.InteropServices This namespace provides facilities to allow .NET types to interact with “unmanaged code” (e.g., C-based DLLs and COM servers) and vice versa.
System.Runtime.Remoting
This namespace (among others) defines types used to build solutions that incorporate the .NET remoting layer.
System.Security 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.
System.Threading This namespace defines types used to build multithreaded applications.
System.Web A number of namespaces are specifically geared toward the development of .NET web applications, including ASP.NET and XML web services.
System.Windows.Forms This namespace contains types that facilitate the construction of traditional desktop GUI applications.
System.Xml 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 –
R.V.COLLEGE OF ENGINEERING Page 37
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
R.V.COLLEGE OF ENGINEERING Page 38
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 39
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 40
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Objectives
R.V.COLLEGE OF ENGINEERING Page 41
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
3.2 Introduction
3.3Role of Command Line Compiler (csc.exe)
3.4Building C# Applications using csc.exe
3.5Working with csc.exe Response Files
3.6Generating Bug Reports
3.7Remaining C# Compiler Options
3.8The Command Line Debugger (cordbg.exe)
3.9Using 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
R.V.COLLEGE OF ENGINEERING Page 42
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 43
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 44
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
Meaning
/out This option is used to specify the name of the assembly to be
R.V.COLLEGE OF ENGINEERING Page 45
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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).
/target: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.
/target:library This option builds a single-file *.dll assembly./target:module This option builds a module.Modules are elements of multifile
assemblies./target:winexe 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;
R.V.COLLEGE OF ENGINEERING Page 46
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 47
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 48
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
semicolon-delimited 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
R.V.COLLEGE OF ENGINEERING Page 49
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
# 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.
R.V.COLLEGE OF ENGINEERING Page 50
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 51
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
Meaning
R.V.COLLEGE OF ENGINEERING Page 52
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
@ Allows to specify a response file used during compilation/? Or /help Prints the list of all command line flags of csc.exe/addmodule Used to specify the modules to add to a multifile assembly/baseaddress Used to specify the preferred base address at which to load a *.dll/bugreport Used to build text-based bug reports for the current compilation/checked Used to specify whether integer arithmetic that overflows the
bounds of the data type will cause an exception at run time/codepage Used to specify the code page to use for all source code files in
the compilation/debug Forces csc.exe to emit debugging information/define Used to define preprocessor symbols/doc Used to construct an XML documentation file/filealign Specifies the size of sections in the output file/fullpaths Specifies the absolute path to the file in compiler output/incremental Enables incremental compilation of source code files/lib Specifies the location of assemblies referenced via /reference/linkresource Used to create a link to a managed resource/main 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.
/nologo Suppresses compiler banner information when compiling the file/nostdlib Prevents the automatic importing of the core .NET library,
mscorlib.dll/noconfig Prevents the use of *.rsp files during the current compilation/nowarn Suppress the compiler’s ability to generate specified warnings/optimize Enable or disable optimization/out Specifies the name of the output file/recurse Searches subdirectories for source files to compile /reference Used to reference an external assembly/resource Used to embed .NET resources into the resulting assembly/target Specifies the format of the output file./unsafe Compiles code that uses the C# “unsafe” keyword/utf8output Displays compiler output using UTF-8 encoding/warn Used to sent warning level for the compilation cycle/warnaserror Used to automatically promote warnings to errors/win32icon Inserts an .ico file into the output file/win32res Inserts a Win32.resource into the output file
3.8 The Command-Line Debugger (cordbg.exe)
R.V.COLLEGE OF ENGINEERING Page 53
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
Meaning
b[reak] Set or display current breakpoints.del[ete] Remove one or more breakpoints.ex[it] Exit the debuggerg[o] Continue debugging the current process until hitting next
breakpoint.o[ut] Step out of the current function.p[rint] Print all loaded variables (local, arguments, etc.).Si Step into the next line.So 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
R.V.COLLEGE OF ENGINEERING Page 54
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 command-line 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 –
R.V.COLLEGE OF ENGINEERING Page 55
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 56
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 MeaningWindows Application Represents Windows Form applicationClass Library Used to build a single file assembly (*.dll)Windows Control Library Allows to build a single file assembly (*.dll) that
contains custom Windows Forms Controls. (ActiveX controls)
ASP.NET Web Application Used to build a web applicationASP.NET Web Service Used to build a .NET Web Service. Web Service is a
block of code, reachable using HTTP requests.Web Control Library Used to build customized Web controls. These GUI
widgets are responsible for emitting HTML to a requesting browser.
Console Application Just like command window. Windows Services Used to build NT/2000 services. Services are
background worker applications that are launched during OS boot process.
R.V.COLLEGE OF ENGINEERING Page 57
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Meaning#define, #undef Used to define and un-define conditional compilation
symbol.#if, #elif, #else, #endif Used to conditionally skip sections of source code.#line Used to control the line numbers emitted for errors and
warnings#error, #warning Used to issue errors and warnings for the current build#region, #endregion 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
R.V.COLLEGE OF ENGINEERING Page 58
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
…….
#region Unwanted
public class sub1
{
…….
}
public interface sub2
{
…………
}
#endregion
}
Now, when we put mouse curser on that region, it will be shown as –
R.V.COLLEGE OF ENGINEERING Page 59
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
R.V.COLLEGE OF ENGINEERING Page 60
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
R.V.COLLEGE OF ENGINEERING Page 61
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
#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
}
}
R.V.COLLEGE OF ENGINEERING Page 62
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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++)
R.V.COLLEGE OF ENGINEERING Page 63
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 64
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 65
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Unit 4
CONTENTS:
1.18 Objectives4.2 Introduction
4.3The Anatomy of a Basic C# Class
4.4Creating Objects: Constructor Basics
4.5The Composition of a C# Application
4.6Default Assignment and Variable scope
4.7The C# Member Initialization Syntax
4.8Basic Input and Output with the Console Class
4.9Defining 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
R.V.COLLEGE OF ENGINEERING Page 66
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
R.V.COLLEGE OF ENGINEERING Page 67
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 68
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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.
R.V.COLLEGE OF ENGINEERING Page 69
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 –
R.V.COLLEGE OF ENGINEERING Page 70
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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 GetCommand-
LineArgs() 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]);
}
R.V.COLLEGE OF ENGINEERING Page 71
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 72
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 73
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
R.V.COLLEGE OF ENGINEERING Page 74
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
// 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.
R.V.COLLEGE OF ENGINEERING Page 75
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
R.V.COLLEGE OF ENGINEERING Page 76
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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; //default value is null
public string s; //default value is null
R.V.COLLEGE OF ENGINEERING Page 77
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
public static void Main()
{
Test t=new Test();
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.
R.V.COLLEGE OF ENGINEERING Page 78
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
…….
}
R.V.COLLEGE OF ENGINEERING Page 79
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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)
R.V.COLLEGE OF ENGINEERING Page 80
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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
R.V.COLLEGE OF ENGINEERING Page 81
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 MeaningBackgroundColorForegroundColor
These properties set the background/foreground colors for the current output. They may be assigned any member of the ConsoleColor enumeration.
BufferHeightBufferWidth
These properties control the height/width of the console’s buffer area.
Clear() This method clears the buffer and console display area.Title This property sets the title of the current console.WindowHeight WindowWidth WindowTopWindowLeft
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;
R.V.COLLEGE OF ENGINEERING Page 82
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
{
R.V.COLLEGE OF ENGINEERING Page 83
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
...
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);
R.V.COLLEGE OF ENGINEERING Page 84
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Note If any mismatch occurs between the number of uniquely numbered curly-
bracket 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
Meaning
C or c 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
D or d Used to format decimal numbers. This flag may also specify the minimum number of digits used to pad the value.
E or e Used for exponential notation.F or f Used for fixed-point formatting.G or g Stands for general. This character can be used to format a number to
fixed or exponential format.N or n Used for basic numerical formatting (with commas).X or x 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)
{
R.V.COLLEGE OF ENGINEERING Page 85
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 86
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 87
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 88
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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); //0 to 9 will be printed.
We can use any type of complex 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
R.V.COLLEGE OF ENGINEERING Page 89
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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,
R.V.COLLEGE OF ENGINEERING Page 90
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Meaning&& Logical AND|| Logical OR! Logical NOT
R.V.COLLEGE OF ENGINEERING Page 91
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
The general form of if can be given as –
if(condition)
{
//true block
}
else
{
//false block
}
The general from of switch can be given as –
switch(variable/expression)
{
case val1: //statements;
break;
case val2: //statements;
break;
--------------
--------------
case val2: //statements;
break;
}
R.V.COLLEGE OF ENGINEERING Page 92
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
4.12 Complete set of C# Operators
Following is a set of operators provided by C#.
Operator Category OperatorsUnary +, -, !, ~, ++, --Multiplicative *, /, %Additive +, -Shift <<, >>Relational <, >, <=. >=, is, asEquality ==, !=Logical & (AND), ^ (XOR), | (OR)Conditional &&, ||, ?: (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
R.V.COLLEGE OF ENGINEERING Page 93
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 94
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Module 2
Unit 1
CONTENTS:
1.19 Objectives1.20 Introduction1.21 Understanding Value Types and Reference Types1.22 The Master Node: System.Object1.23 The System Data Types (and C# Aliases)1.24 Converting between value types reference types: Boxing
and Unboxing
1.25 Summary1.26 Keywords1.27 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
R.V.COLLEGE OF ENGINEERING Page 95
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 96
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
R.V.COLLEGE OF ENGINEERING Page 97
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 98
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 99
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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
R.V.COLLEGE OF ENGINEERING Page 100
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 101
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
}
}
R.V.COLLEGE OF ENGINEERING Page 102
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 103
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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–
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.
R.V.COLLEGE OF ENGINEERING Page 104
ms1 ms2
a s a
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
Reference type variables are pointing to the memory occupied by the allocated instance.
What is the base type? Must derive from System.ValueType.
Can derive from any other type (except System.ValueType), as long as that type is not “sealed”
Can this type function as a base to other types?
No. Value types are always sealed and cannot be extended.
Yes. If the type is not sealed, it may function as a base to other types.
What is the default parameter passing behavior?
Variables are passed by value (i.e., a copy of the variable is passed into the called function).
Variables are passed by reference (e.g., the address of the variable is passed into the called function).
Can this type override System.Object.Finalize()?
No. Value types are never placed onto the heap and therefore do not need to be finalized.
Yes, indirectly
Can we define constructors?
Yes, but the default constructor is reserved
Yes
When do variables of this type die?
When they fall out of the defining scope.
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
R.V.COLLEGE OF ENGINEERING Page 105
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
R.V.COLLEGE OF ENGINEERING Page 106
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
Meaning
Equals() 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() 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.
GetType() 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).
R.V.COLLEGE OF ENGINEERING Page 107
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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");
R.V.COLLEGE OF ENGINEERING Page 108
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 109
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
{
R.V.COLLEGE OF ENGINEERING Page 110
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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]
R.V.COLLEGE OF ENGINEERING Page 111
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
}
R.V.COLLEGE OF ENGINEERING Page 112
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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");
R.V.COLLEGE OF ENGINEERING Page 113
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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()
R.V.COLLEGE OF ENGINEERING Page 114
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 115
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 116
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
CLSCompliant?
System Type Range Meaning
sbyte No System.SByte -128 to 127 Signed 8-bit numberbyte Yes System.Byte 0 to 255 Unsigned 8-bit numbershort Yes System.Int16 -216 to 216-1 Signed 16-bit numberushort No System.UInt16 0 to 232-1 Unsigned 16-bit numberint Yes System.Int32 -232 to 232-1 Signed 32-bit numberuint No System.UInt32 0 to 264-1 Unsigned 32-bit numberlong Yes System.Int64 -264 to 264-1 Signed 64-bit numberulong No System.UInt64 0 to 2128-1 Unsigned 64-bit numberchar Yes System.Char U10000 to
U1ffffA Single 16-bit Unicode character
float Yes System.Single 1.5 x 10-45 to 32-bit floating point
R.V.COLLEGE OF ENGINEERING Page 117
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
3.4 x 1038 numberdouble Yes System.Double 5.0 x 10-324
to 1.7 x 10308
64-bit floating point number
bool Yes System.Boolean True or False
Represents truth or falsity
decimal Yes System.Decimal 1 to 1028 96-bit signed numberstring Yes System.String Limited by
system memory
Represents a set of Unicode characters
object Yes System.Object Anything(all types excluding interfaces) derive from object
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
R.V.COLLEGE OF ENGINEERING Page 118
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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!!”);
R.V.COLLEGE OF ENGINEERING Page 119
Object
Type
String
Array
Exception
Delegate
MulticastDelegate
Byte
Boolean
Char
Decimal
Double
Int16
Int64
Int32
UInt16
UInt32
UInt64
Void
DateTime
Guid
TimeSpan
SByte
Single
ValueType
Any type that derives from
ValueType is a structure not a
class
Enumerations and Structures
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 121
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 –
R.V.COLLEGE OF ENGINEERING Page 122
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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); //True
Console.WriteLine("{0}, {1}", b1, b2); //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
R.V.COLLEGE OF ENGINEERING Page 123
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
}
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
R.V.COLLEGE OF ENGINEERING Page 124
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 125
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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());
}
R.V.COLLEGE OF ENGINEERING Page 126
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 127
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 128
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Objectives2.14 Introduction2.15 Defining Custom Class Methods2.16 Understanding Static Methods2.17 Methods Parameter Modifiers2.18 Array Manipulation in C#2.19 String Manipulation in C#2.20 C# Enumerations2.21 Defining Structures in C#2.22 Defining Custom Namespaces2.23 Summary2.24 Keywords2.25 Exercises
R.V.COLLEGE OF ENGINEERING Page 129
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.13Introduction
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.14Defining 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
R.V.COLLEGE OF ENGINEERING Page 130
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Every method in C# specifies its level of accessibility using following access
modifiers:
Access Modifiers Meaningpublic Method is accessible from an object or any
subclass.private Method is accessible only by the class in which it
is defined. private is a default modifier in C#.protected Method is accessible by the defining class and all
its sub-classes.internal Method is publicly accessible by all types in an
assembly, but not outside the assembly.protected internal Method’s access is limited to the current assembly
or types derived from the defining class in the current assembly.
2.15Understanding 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”);
R.V.COLLEGE OF ENGINEERING Page 131
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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,
R.V.COLLEGE OF ENGINEERING Page 132
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.16Method Parameter Modifiers
R.V.COLLEGE OF ENGINEERING Page 133
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Meaning(none) 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. (call-by-value)
out The output parameters are assigned by the called method.
ref Reference to a parameter is passed to a method. (call-by-reference)
params 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.
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;
R.V.COLLEGE OF ENGINEERING Page 134
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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
R.V.COLLEGE OF ENGINEERING Page 135
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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;
R.V.COLLEGE OF ENGINEERING Page 136
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 137
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 138
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
}
R.V.COLLEGE OF ENGINEERING Page 139
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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]);
}
R.V.COLLEGE OF ENGINEERING Page 140
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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
R.V.COLLEGE OF ENGINEERING Page 141
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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()
R.V.COLLEGE OF ENGINEERING Page 142
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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
R.V.COLLEGE OF ENGINEERING Page 143
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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”.
R.V.COLLEGE OF ENGINEERING Page 144
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
2.17Array 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 –
R.V.COLLEGE OF ENGINEERING Page 145
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
R.V.COLLEGE OF ENGINEERING Page 146
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
}
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];
R.V.COLLEGE OF ENGINEERING Page 147
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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());
R.V.COLLEGE OF ENGINEERING Page 148
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 149
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 MeaningBinarySearch()
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()
{
R.V.COLLEGE OF ENGINEERING Page 150
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 151
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Cleared some elements:
67 0 0 0 12
2.18String 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 MeaningLength 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.
CompareTo() Compares two strings.Copy() Returns a fresh new copy of an existing string.
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 –
R.V.COLLEGE OF ENGINEERING Page 152
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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"));
R.V.COLLEGE OF ENGINEERING Page 153
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.\a Triggers a system alert (beep). For console applications, this can be an
audio clue to the user.\b Triggers a backspace.\f Triggers a form feed.\n Inserts a new line (on Win32 platforms).\r Inserts a carriage return.\t Inserts a horizontal tab into the string literal\u Inserts a Unicode character into the string literal.
R.V.COLLEGE OF ENGINEERING Page 154
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
\v Inserts a vertical tab into the string literal\0 Represents NULL character.
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"
R.V.COLLEGE OF ENGINEERING Page 155
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 156
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.19C# 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
{
R.V.COLLEGE OF ENGINEERING Page 157
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
}
R.V.COLLEGE OF ENGINEERING Page 158
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
}
R.V.COLLEGE OF ENGINEERING Page 159
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 MeaningFormat() Converts a value of a specified enumerated type to its
equivalent string representation according to the specified format
GetName()GetNames()
Retrieves a name (or an array containing all names) for the constant in the specified enumeration that has the specified value
GetUnderlyingType() Returns the underlying data type used to hold the values for a given enumeration
GetValues() Retrieves an array of the values of the constants in a specifiedEnumeration
IsDefined() Returns an indication of whether a constant with a specified
R.V.COLLEGE OF ENGINEERING Page 160
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
valueexists in a specified enumeration
Parse() 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));
R.V.COLLEGE OF ENGINEERING Page 161
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 162
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.20Defining 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
R.V.COLLEGE OF ENGINEERING Page 163
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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);
}
R.V.COLLEGE OF ENGINEERING Page 164
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 –
R.V.COLLEGE OF ENGINEERING Page 165
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 166
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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;
R.V.COLLEGE OF ENGINEERING Page 167
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
R.V.COLLEGE OF ENGINEERING Page 168
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
-------
}
R.V.COLLEGE OF ENGINEERING Page 169
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 access-
specifiers/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.
R.V.COLLEGE OF ENGINEERING Page 170
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 addition of two matrices.
(iii) Find the product of two matrices.
(iv) Find the subtraction of two matrices.
(v) 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:
R.V.COLLEGE OF ENGINEERING Page 171
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
1.28 Objectives3.2 Introduction
3.3Formal Definition of the C# Class
3.4Definition the “Default Public Interface” of a Type
3.5Recapping the Pillars of OOP
3.6The First Pillars: C#’s Encapsulation Services
3.7Pseudo Encapsulation: Creating Read-Only Fields
3.8Summary
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.15Introduction
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
R.V.COLLEGE OF ENGINEERING Page 172
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
encapsulation in detail. The other two concepts viz. inheritance and polymorphism
will be explored in next unit.
3.16Formal 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 –
R.V.COLLEGE OF ENGINEERING Page 173
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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) //two arguments
{
//some code
}
public float Draw(float sideA, float sideB, float sideC) //3 args
{
//some code
}
}
// overloaded member-methods: type of arguments are different
R.V.COLLEGE OF ENGINEERING Page 174
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
}
}
R.V.COLLEGE OF ENGINEERING Page 175
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 176
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
R.V.COLLEGE OF ENGINEERING Page 177
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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 –
R.V.COLLEGE OF ENGINEERING Page 178
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.17Defining 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
R.V.COLLEGE OF ENGINEERING Page 179
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
}
R.V.COLLEGE OF ENGINEERING Page 180
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Or
class Test //by default, it is internal
{
//body of the class
}
3.18Recapping 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():
R.V.COLLEGE OF ENGINEERING Page 181
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 182
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
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.
R.V.COLLEGE OF ENGINEERING Page 183
Object
Shape
Hexagon
Car
Radio
//Method of Car
void TurnOnRadio(bool on)
{
//Delegate to inner Radio
radioObj.Power(on);
}
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 –
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:
R.V.COLLEGE OF ENGINEERING Page 184
Object
Shape
Hexagon
void Draw()
Circle
Draw()
Draw()
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.19The 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
R.V.COLLEGE OF ENGINEERING Page 185
Circle Hexagon Rectangle
Draw() Draw() Draw()
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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) //mutator
{
Name=n;
}
}
class Test
{
public static void Main()
{
Emp p=new Emp();
p.SetName(“Ramu”);
R.V.COLLEGE OF ENGINEERING Page 186
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
}
set
{
Name=value; // value is a keyword
R.V.COLLEGE OF ENGINEERING Page 187
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
}
}
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
{
R.V.COLLEGE OF ENGINEERING Page 188
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
}
R.V.COLLEGE OF ENGINEERING Page 189
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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 //read-only property
{
get{ return SSN;}
}
}
Understanding static properties
R.V.COLLEGE OF ENGINEERING Page 190
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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,
R.V.COLLEGE OF ENGINEERING Page 191
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.20Pseudo-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; //assignment during declaration
R.V.COLLEGE OF ENGINEERING Page 192
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
public readonly string USN;
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
compile-time 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.
R.V.COLLEGE OF ENGINEERING Page 193
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
• 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
--------------
R.V.COLLEGE OF ENGINEERING Page 194
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
}
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.21Summary
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 read-only fields too.
3.22Keywords
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
R.V.COLLEGE OF ENGINEERING Page 195
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Objectives4.2 Introduction
4.3The Second Pillar: C#’s Inheritance Supports
4.4Keeping family secrets: The “Protected” Keyword
4.5Nested Type Definitions
4.6The Third Pillar: C#’s Polymorphic Support
4.7Casting Between
4.8Summary
R.V.COLLEGE OF ENGINEERING Page 196
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.16Introduction
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.
R.V.COLLEGE OF ENGINEERING Page 197
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
4.17The 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 –
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
R.V.COLLEGE OF ENGINEERING Page 198
Employee
Manager
SalesMan
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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
R.V.COLLEGE OF ENGINEERING Page 199
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
}
}
R.V.COLLEGE OF ENGINEERING Page 200
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
}
}
R.V.COLLEGE OF ENGINEERING Page 201
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.18Keeping 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
R.V.COLLEGE OF ENGINEERING Page 202
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
In some of the situations, we may don’t want our class to be inherited by any other
class. For example –
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!!!
{
…………..
}
R.V.COLLEGE OF ENGINEERING Page 203
Employee
SalesMan
Part-time SalesMan
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 204
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 –
R.V.COLLEGE OF ENGINEERING Page 205
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.19Nested Type Definitions
R.V.COLLEGE OF ENGINEERING Page 206
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.20The 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.
R.V.COLLEGE OF ENGINEERING Page 207
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
R.V.COLLEGE OF ENGINEERING Page 208
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
R.V.COLLEGE OF ENGINEERING Page 209
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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.
R.V.COLLEGE OF ENGINEERING Page 210
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 211
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
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()
{
R.V.COLLEGE OF ENGINEERING Page 212
Object
Shape
Hexagon
Circle
virtual void Draw()
Draw()
Draw()
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 213
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
R.V.COLLEGE OF ENGINEERING Page 214
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 215
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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):
R.V.COLLEGE OF ENGINEERING Page 216
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 217
Object
Shape
Hexagon
Circle
Oval
Abstract Draw() method defined
Draw() method implemented
Completely new Draw() method
hiding Circle.Draw()
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 218
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.21Casting 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 –
R.V.COLLEGE OF ENGINEERING Page 219
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 220
Object
Employee
Manager
SalesMan
Part_time
SalesMan
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 221
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.22Summary
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.23Keywords
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
R.V.COLLEGE OF ENGINEERING Page 222
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Objectives1.31 Introduction1.32 Meaning of Errors and Exceptions1.33 The Role of .NET Exception Handling1.34 The System. Exception Base Class1.35 Throwing a Generic Exception1.36 Catching Exception1.37 CLR System – Level Exception (System.SystemException)1.38 Custom Application-Level Exception (System.ApplicationException)
1.39 Handling Multiple Exceptions 1.40 The Finally Block 1.41 Dynamically Identifying Application and System Level
R.V.COLLEGE OF ENGINEERING Page 223
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Exceptions
1.42 Summary1.43 Keywords1.44 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
R.V.COLLEGE OF ENGINEERING Page 224
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 225
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
Property
Meaning
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 read-only property.
Message Returns the textual description of a given error. The
R.V.COLLEGE OF ENGINEERING Page 226
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
TargetSite 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.
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)
R.V.COLLEGE OF ENGINEERING Page 227
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 228
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
R.V.COLLEGE OF ENGINEERING Page 229
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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.
R.V.COLLEGE OF ENGINEERING Page 230
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 231
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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()
{
R.V.COLLEGE OF ENGINEERING Page 232
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Test ob=new Test();
Console.WriteLine("Enter a number:");
int d=int.Parse(Console.ReadLine()); //assume d is 123
ob.Fun(d);
}
}
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)
R.V.COLLEGE OF ENGINEERING Page 233
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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
R.V.COLLEGE OF ENGINEERING Page 234
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
The .NET base class libraries define many classes derived from System.Exception.
For example, the System namespace defines core error objects such as
ArgumentOutOfRangeException, 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 database-
centric 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 application-
specific exceptions. Since the System.SystemException base class represents
exceptions thrown from the CLR, we may naturally assume that we should derive
R.V.COLLEGE OF ENGINEERING Page 235
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 –
R.V.COLLEGE OF ENGINEERING Page 236
System.ApplicationException
System.Object
System.Exception
Your Application’s
Custom Exceptions
System.SystemException
Exceptions from
.NET BCL
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 237
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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);
}
}
……………………..
R.V.COLLEGE OF ENGINEERING Page 238
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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)
{
}
}
R.V.COLLEGE OF ENGINEERING Page 239
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
R.V.COLLEGE OF ENGINEERING Page 240
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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!!
R.V.COLLEGE OF ENGINEERING Page 241
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 242
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 243
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
R.V.COLLEGE OF ENGINEERING Page 244
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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. Alternatively, if we want to generalize our catch blocks in such a way that all
application level exceptions are handled apart from possible system-level
exceptions:
try
{
//do something
}
R.V.COLLEGE OF ENGINEERING Page 245
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 246
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 247
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
1.45 Objectives2.26 Introduction2.27 Understanding Object Lifetime2.28 The CIL of new 2.29 The Basics of Garbage Collection2.30 Finalization a type2.31 The Finalization Process2.32 Building an AD Hoc Destruction Method2.33 Garbage Collection Optimizations2.34 The System. GC Type2.35 Summary2.36 Keywords2.37 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.25Introduction
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.
R.V.COLLEGE OF ENGINEERING Page 248
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 249
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
2.27The 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:
R.V.COLLEGE OF ENGINEERING Page 250
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
2.28The 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
R.V.COLLEGE OF ENGINEERING Page 251
c1 c2 c3Unused Heap
SpaceCar c1=new Car(“Ford”);
Car c2=new Car(“Swift”);
Car c3=new Car(“Zen”);
New object pointer
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
– 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:
Now, a possible object graph may look like –
R.V.COLLEGE OF ENGINEERING Page 252
A B C D E F G
Managed Heap
New Object pointer
A
GED
B
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 –
2.29Finalizing 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 an option for overriding the virtual
R.V.COLLEGE OF ENGINEERING Page 253
A B D E G
Managed Heap
New Object pointer
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
System.Object.Finalize() method. The default implementation of Finalize() method
does nothing!! 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
{
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 place when an application domain or
AppDomain is unloaded by CLR. Application domain can be assumed to be
R.V.COLLEGE OF ENGINEERING Page 254
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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()");
R.V.COLLEGE OF ENGINEERING Page 255
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
}
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.30The 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.
R.V.COLLEGE OF ENGINEERING Page 256
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.31Building 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 user-
defined 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
}
}
R.V.COLLEGE OF ENGINEERING Page 257
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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()
{
R.V.COLLEGE OF ENGINEERING Page 258
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
……..
R.V.COLLEGE OF ENGINEERING Page 259
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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.32Garbage Collection Optimizations
R.V.COLLEGE OF ENGINEERING Page 260
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 261
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
To illustrate how an object’s generation affects the collection process, consider the
following diagram –
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.
R.V.COLLEGE OF ENGINEERING Page 262
A B C D E F G
A B E
Gen 0
Gen 1
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 263
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
R.V.COLLEGE OF ENGINEERING Page 264
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
}
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
R.V.COLLEGE OF ENGINEERING Page 265
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 266
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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");
R.V.COLLEGE OF ENGINEERING Page 267
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 268
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 269
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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?
R.V.COLLEGE OF ENGINEERING Page 270
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Module 3
Unit 3
CONTENTS:
1.46 Objectives3.2 Introduction
3.3Defining Interfaces Using C#
3.4Invoking Interface Members at the object level
3.5Exercising the shapes hierarchy
3.6Understanding Explicit Interface Implementation
3.7Interfaces as Polymorphic Agents
3.8 Building Interface Hierarchies
3.9Summary
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
R.V.COLLEGE OF ENGINEERING Page 271
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Passing interfaces as parameters to methods
Explicit interface implementation
Interfaces as polymorphic agents
Building interfaces hierarchies
Interfaces with multiple base interfaces
3.24Introduction
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.25Defining 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.
R.V.COLLEGE OF ENGINEERING Page 272
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 273
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 274
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 275
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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.
R.V.COLLEGE OF ENGINEERING Page 276
Object
Triangle
Shape
Circle
Hexagon
IPointy
IPointy
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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()
{
R.V.COLLEGE OF ENGINEERING Page 277
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.26Invoking 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
R.V.COLLEGE OF ENGINEERING Page 278
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
{
R.V.COLLEGE OF ENGINEERING Page 279
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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...");
}
R.V.COLLEGE OF ENGINEERING Page 280
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.27Exercising 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)
R.V.COLLEGE OF ENGINEERING Page 281
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
...
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
{
R.V.COLLEGE OF ENGINEERING Page 282
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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!");
}
}
R.V.COLLEGE OF ENGINEERING Page 283
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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]);
}
R.V.COLLEGE OF ENGINEERING Page 284
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
}
Notice that the triangle is never drawn, as it is not IDraw3D-compatible
3.28Understanding 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();
R.V.COLLEGE OF ENGINEERING Page 285
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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();
R.V.COLLEGE OF ENGINEERING Page 286
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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...");
}
R.V.COLLEGE OF ENGINEERING Page 287
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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:
R.V.COLLEGE OF ENGINEERING Page 288
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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.29Interfaces 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
R.V.COLLEGE OF ENGINEERING Page 289
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
can be implemented by numerous types, even if they are not within the same class
hierarchy in the first place.
3.30Building 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();
R.V.COLLEGE OF ENGINEERING Page 290
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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:
R.V.COLLEGE OF ENGINEERING Page 291
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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:
R.V.COLLEGE OF ENGINEERING Page 292
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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()
R.V.COLLEGE OF ENGINEERING Page 293
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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.31Summary
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
R.V.COLLEGE OF ENGINEERING Page 294
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 295
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
1.47 Objectives4.2 Introduction
4.3Understanding the IConvertible Interface
4.4 Building a Custom Enumerator (IEnummerable and Enumerator)
4.5Building Cloneable objects (ICloneable)
4.6Building Comparable Objects (IComparable)
4.7Exploring the System.Collections Namespace
4.8Summary
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.25Introduction
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
R.V.COLLEGE OF ENGINEERING Page 296
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
predefined interfaces that are implemented by various collection classes (ArrayList,
Stack etc.) defined by System.Collections namespace.
4.26Understanding 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);
R.V.COLLEGE OF ENGINEERING Page 297
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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.27Building a Custom Enumerator (IEnumerable and IEnumerator)
R.V.COLLEGE OF ENGINEERING Page 298
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
{
R.V.COLLEGE OF ENGINEERING Page 299
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 300
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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);
R.V.COLLEGE OF ENGINEERING Page 301
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 302
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
public IEnumerator IEnumerable.GetEnumerator()
{
// Return the array object's IEnumerator.
return carArray.GetEnumerator();
}
4.28Building 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 );
R.V.COLLEGE OF ENGINEERING Page 303
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
}
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();
}
R.V.COLLEGE OF ENGINEERING Page 304
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 );
}
}
R.V.COLLEGE OF ENGINEERING Page 305
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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();
}
R.V.COLLEGE OF ENGINEERING Page 306
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 307
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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()
{
R.V.COLLEGE OF ENGINEERING Page 308
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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-by-
member) 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);
}
R.V.COLLEGE OF ENGINEERING Page 309
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.29Building Comparable Objects (IComparable)
R.V.COLLEGE OF ENGINEERING Page 310
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 311
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 312
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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).
R.V.COLLEGE OF ENGINEERING Page 313
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
R.V.COLLEGE OF ENGINEERING Page 314
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Console.ReadLine();
}
4.30Exploring 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
Interface
Meaning
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
R.V.COLLEGE OF ENGINEERING Page 315
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
IDictionary.
IEnumerable Returns the IEnumerator interface for a given object.
IEnumerator Generally supports foreach-style iteration of 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.
Following figure illustrates the relationship between each type of the interfaces:
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
R.V.COLLEGE OF ENGINEERING Page 316
IHashCodeProvider IEnumerator
IDictionaryEnumeratorIComparer
IDictionaryIList
IEnumerable
ICollection
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
of the core classes in the System.Collections namespace and the key interfaces
they support.
System.Collections Class
Meaning Key Implemented
Interfaces
ArrayList Represents a dynamically sized array of objects.
IList, ICollection,
IEnumerable, and ICloneable
Hashtable Represents a collection of objects identified by a numerical key. Custom types stored in a Hashtable should always override
System.Object.GetHashCode().
IDictionary, ICollection, IEnumerable, and ICloneable
Queue Represents a standard first-in, first-out (FIFO) queue.
ICollection, ICloneable, and IEnumerable
SortedList Like a dictionary; however, the elements can also be accessed by ordinal position (e.g., index).
IDictionary, ICollection, IEnumerable, and ICloneable
Stack A last-in, first-out (LIFO) queue providing push and pop (and peek) functionality.
ICollection, ICloneable, and
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,
R.V.COLLEGE OF ENGINEERING Page 317
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
R.V.COLLEGE OF ENGINEERING Page 318
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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
R.V.COLLEGE OF ENGINEERING Page 319
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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());
R.V.COLLEGE OF ENGINEERING Page 320
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.31Summary
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.32Keywords
IConvertible.ToXXXX() method IFormatProvider Interface GetFormat() method IConvertible.GetTypeCode() method
R.V.COLLEGE OF ENGINEERING Page 321
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Objectives
R.V.COLLEGE OF ENGINEERING Page 322
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
1.49 Introduction1.50 Understanding Callback Interfaces1.51 Understanding the .NET Delegate Type1.52 Members of System.Multicast Delegate1.53 The Simplest Possible Delegate Example1.54 Building More Elaborate Delegate Example1.55 Understanding Asynchronous Delegates1.56 Understanding (and Using) Events 1.57 Summary1.58 Keywords1.59 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
R.V.COLLEGE OF ENGINEERING Page 323
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 324
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 325
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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;
R.V.COLLEGE OF ENGINEERING Page 326
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
}
}
}
R.V.COLLEGE OF ENGINEERING Page 327
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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();
}
}
R.V.COLLEGE OF ENGINEERING Page 328
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 object-oriented 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.
R.V.COLLEGE OF ENGINEERING Page 329
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 330
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
Combine() This static method adds a method to the list maintained by the delegate. In C#, you trigger this method using the overloaded += operator as
a shorthand notation.
GetInvocationList() This method returns an array of System.Delegate types, each representing a particular method that may be invoked.
Remove()
RemoveAll()
These static methods remove a method (or all methods) from the invocation list. In C#, the Remove() method can be called indirectly using
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:
R.V.COLLEGE OF ENGINEERING Page 331
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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();
R.V.COLLEGE OF ENGINEERING Page 332
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
}
}
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; }
}
R.V.COLLEGE OF ENGINEERING Page 333
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
{
...
R.V.COLLEGE OF ENGINEERING Page 334
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
...
}
R.V.COLLEGE OF ENGINEERING Page 335
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 336
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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!!”;
R.V.COLLEGE OF ENGINEERING Page 337
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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.
R.V.COLLEGE OF ENGINEERING Page 338
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 339
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 340
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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!");
}
R.V.COLLEGE OF ENGINEERING Page 341
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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
R.V.COLLEGE OF ENGINEERING Page 342
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Objectives2.38 Introduction
R.V.COLLEGE OF ENGINEERING Page 343
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
2.39 The advanced keywords of C#2.40 Building a Custom Indexer2.41 Overloading operators2.42 The Internal Representation of Overloading Operators2.43 Creating Custom Conversion Routines2.44 Defining Implicit Conversion Routines2.45 Summary2.46 Keywords2.47 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.37Introduction
This unit investigates advanced constructs of C#. You will learn how to
programmatically account for overflow/underflow conditions 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.
R.V.COLLEGE OF ENGINEERING Page 344
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
R.V.COLLEGE OF ENGINEERING Page 345
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 346
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 347
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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);
}
R.V.COLLEGE OF ENGINEERING Page 348
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
Operator
Meaning
* 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:
R.V.COLLEGE OF ENGINEERING Page 349
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 350
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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();
}
}
R.V.COLLEGE OF ENGINEERING Page 351
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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#
R.V.COLLEGE OF ENGINEERING Page 352
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)
R.V.COLLEGE OF ENGINEERING Page 353
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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;
R.V.COLLEGE OF ENGINEERING Page 354
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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());
}
}
R.V.COLLEGE OF ENGINEERING Page 355
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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!
{
R.V.COLLEGE OF ENGINEERING Page 356
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
R.V.COLLEGE OF ENGINEERING Page 357
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 358
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 C-based 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.39Building 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:
R.V.COLLEGE OF ENGINEERING Page 359
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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
R.V.COLLEGE OF ENGINEERING Page 360
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.40Overloading 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:
R.V.COLLEGE OF ENGINEERING Page 361
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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);
}
R.V.COLLEGE OF ENGINEERING Page 362
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.41Internal 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-
to-CIL mapping for the C# operators.
R.V.COLLEGE OF ENGINEERING Page 363
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.42Creating 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()
{
R.V.COLLEGE OF ENGINEERING Page 364
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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;
}
R.V.COLLEGE OF ENGINEERING Page 365
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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);
R.V.COLLEGE OF ENGINEERING Page 366
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.43Defining Implicit Conversion Routines
Now we will discuss implicit conversion. Consider following situation:
static void Main(string[] args)
{
R.V.COLLEGE OF ENGINEERING Page 367
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
...
// 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;
}
}
R.V.COLLEGE OF ENGINEERING Page 368
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
R.V.COLLEGE OF ENGINEERING Page 369
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
// 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.44Summary
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
R.V.COLLEGE OF ENGINEERING Page 370
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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 Objectives3.2 Introduction
R.V.COLLEGE OF ENGINEERING Page 371
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
3.3An Overview of .NET Assemblies
3.4Core Benefits of Assemblies
3.5Building a Single File Test Assembly
3.6Exploring the Manifest
3.7Building a Multifile Assembly
3.8Summary
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.34Introduction
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 language-
independent manner. However, the .NET platform provides far greater language
integration. For example, the .NET platform supports cross-language inheritance
R.V.COLLEGE OF ENGINEERING Page 372
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
(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.35An 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.
R.V.COLLEGE OF ENGINEERING Page 373
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
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.
R.V.COLLEGE OF ENGINEERING Page 374
Manifest
Type Metadata
CIL Code
(Optional) Resources
A Single File Assembly
Test.dll
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 375
Manifest (References other
related files)
Type Metadata
CIL Code
Type Metadata
CIL Code
Type Metadata
CIL Code
CompanyLogo.bmp
Test.dll
Bar.netmodule
Qaaz.netmodule
A Multifile Assembly
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
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.
R.V.COLLEGE OF ENGINEERING Page 376
Test.dll
Bar.netmodule Manifest
Resource Files
Physical View of an Assembly
Classes
Interfaces Resources
Enumerations
Logical View of an Assembly
Delegates
Structures
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
3.36Core 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
R.V.COLLEGE OF ENGINEERING Page 377
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 378
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 379
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
3.37Building 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;
R.V.COLLEGE OF ENGINEERING Page 380
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
R.V.COLLEGE OF ENGINEERING Page 381
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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()
R.V.COLLEGE OF ENGINEERING Page 382
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
{
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.38Exploring 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
R.V.COLLEGE OF ENGINEERING Page 383
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
Information
Meaning
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 the assembly
A hash of each module contained in the assembly (in the case of a single file 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.
R.V.COLLEGE OF ENGINEERING Page 384
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
.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.
R.V.COLLEGE OF ENGINEERING Page 385
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
3.39Building 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:
R.V.COLLEGE OF ENGINEERING Page 386
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
{
R.V.COLLEGE OF ENGINEERING Page 387
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.40Summary
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.
R.V.COLLEGE OF ENGINEERING Page 388
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 389
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Module 4
Unit 4
CONTENTS:
1.62 Objectives4.2 Introduction
4.3Understanding 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.7Understanding Shared Assembly
4.8Understanding Strong Names
4.9Building 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
R.V.COLLEGE OF ENGINEERING Page 390
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.34Introduction
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.35Understanding 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
R.V.COLLEGE OF ENGINEERING Page 391
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 392
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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.36Probing 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.37Private Assemblies and XML Configuration Files
R.V.COLLEGE OF ENGINEERING Page 393
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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!):
R.V.COLLEGE OF ENGINEERING Page 394
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
<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"/>
R.V.COLLEGE OF ENGINEERING Page 395
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
Once you’ve finished creating CSharpCarClient.exe.config, run the client by double-
clicking 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.38Probing 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
{
…….
}
R.V.COLLEGE OF ENGINEERING Page 396
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 397
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
4.39Understanding 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.40Understanding 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
R.V.COLLEGE OF ENGINEERING Page 398
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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
R.V.COLLEGE OF ENGINEERING Page 399
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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:
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.
R.V.COLLEGE OF ENGINEERING Page 400
Manifest with Public Key
Type Metadata
CIL
Digital Signature
CarLibrary.dll
Assembly Hash Code
Digital Signature
Private
Key Data+ =
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
4.41Building 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;}
}
}
R.V.COLLEGE OF ENGINEERING Page 401
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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”. This file contains a number of attributes that describe the
assembly itself. 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”)]
R.V.COLLEGE OF ENGINEERING Page 402
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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)]
R.V.COLLEGE OF ENGINEERING Page 403
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
[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
R.V.COLLEGE OF ENGINEERING Page 404
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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();
}
R.V.COLLEGE OF ENGINEERING Page 405
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
}
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
R.V.COLLEGE OF ENGINEERING Page 406
TOPICS IN ENTEPRISE ARCHITECTURE –II, 5TH MCA (10MCA53)
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.
R.V.COLLEGE OF ENGINEERING Page 407