C#.Net Core (NareshIT)
C#.Net Core (NareshIT)
NET
What is Application Software?
Ans: Application software is commonly defined as any program or number of programs designed for end-users. In
that sense, any end user program can be called an “application.” People often use the term “application software”
to talk about bundles or groups of individual software applications, using a different term, “application program” to
refer to individual applications. Examples of application software include items like Notepad, WordPad, Microsoft
Word, Microsoft Excel, or any of the Web Browsers used to navigate the Internet, etc.
Another way to understand application software is, in a very basic sense, every program that you use on
your computer is a piece of application software. The operating system, on the other hand, is system software.
Historically, the application was generally born as computers evolved into systems where you could run a particular
codebase on a given operating system. Even social media platforms have come to resemble applications, especially
on our mobile phone devices, where individual applications are given the nickname “apps.” So while the term
“application software” can be used broadly, it’s an important term in describing the rise of sophisticated computing
environments.
“A computer programming language is a language used to write computer programs, which involves a
computer performing some kind of computation or algorithm and possibly control external devices such as printers,
disk drives, robots, and so on.”
Anyone can come up with ideas, but a developer will be able to turn those ideas into something concrete.
Even if you only want to work on the design aspects of software, you should have some familiarity with coding and
be able to create basic prototypes. There are a huge variety of programming languages that we can learn.
Very early computers, such as Colossus is thus regarded as the world's first programmable, electronic,
digital computer, although it was programmed by switches and plugs and not by a stored program.
Slightly later, programs could be written in machine language, where the programmer writes each
instruction in a numeric form the hardware can execute directly. For example, the instruction to add the value in
two memory location might consist of 3 numbers: an “opcode” that selects the “add” operation, and two memory
1
locations. The programs, in decimal or binary form, were read in from punched cards, paper tape, and magnetic tape
or toggled in on switches on the front panel of the computer. Machine languages were later termed first-generation
programming languages (1GL).
The next step was development of so-called second-generation programming languages (2GL) or assembly
languages, which were still closely tied to the instruction set architecture of the specific computer. These served to
make the program much more human-readable and relieved the programmer of tedious and error-prone address
calculations.
The first high-level programming languages, or third-generation programming languages (3GL), were
written in the 1950s. John Mauchly's Short Code, proposed in 1949, was one of the first high-level languages ever
developed for an electronic computer. Unlike machine code, Short Code statements represented mathematical
expressions in understandable form. However, the program had to be translated into machine code every time it
ran, making the process much slower than running the equivalent machine code.
At the University of Manchester, Alick Glennie developed Autocode in the early 1950s. As a programming
language, it used a compiler to automatically convert the language into machine code. The first code and compiler
was developed in 1952 for the Mark 1 computer at the University of Manchester and is considered to be the first
compiled high-level programming language.
In 1954, FORTRAN was invented at IBM by John Backus. It was the first widely used high-level general
purpose programming language to have a functional implementation, as opposed to just a design on paper. It is still
a popular language for high-performance computing and is used for programs that benchmark and rank the world's
fastest supercomputers.
Another early programming language was devised by Grace Hopper in the US, called FLOW-MATIC. It was
developed for the UNIVAC I at Remington Rand during the period from 1955 until 1959. Hopper found that business
data processing customers were uncomfortable with mathematical notation, and in early 1955, she and her team
wrote a specification for an English programming language and implemented a prototype. The FLOW-MATIC
compiler became publicly available in early 1958 and was substantially complete in 1959. FLOW-MATIC was a major
influence in the design of COBOL.
Pascal is an imperative and procedural programming language, designed by Niklaus Wirth as a small,
efficient language intended to encourage good programming practices using structured programming and data
structuring. It is named in honor of the French mathematician, philosopher and physicist Blaise Pascal. Pascal
enabled defining complex data types and building dynamic and recursive data structures such as lists, trees and
graphs. Pascal has strong typing on all objects, which means that one type of data cannot be converted or interpreted
as another without explicit conversions.
2
C is a general-purpose, imperative procedural computer programming language supporting structured
programming, lexical variable scope, and recursion, with a static type system. By design, C provides constructs that
map efficiently to typical machine instructions. It has found lasting use in applications previously coded in assembly
language. Such applications include operating systems, various application software for computers that range from
super computers to PLCs and embedded systems. A successor to the programming language B, C was originally
developed at Bell Labs by Dennis Ritchie between 1972 and 1973 to construct utilities running on UNIX. It was applied
to re-implementing the kernel of the UNIX operating system. During the 1980s, C gradually gained popularity. It has
become one of the most widely used programming languages, with C compilers from various vendors available for
the majority of existing computer architectures and operating systems. C has been standardized by the ANSI since
1989 (ANSI C) and by the International Organization for Standardization (ISO).
C++ is a general-purpose programming language developed by Danish computer scientist Bjarne Stroustrup
at Bell Labs since 1979 as an extension of the C programming language, or "C with Classes" as he wanted an efficient
and flexible language similar to C that also provided high-level features for program organization. The language has
expanded significantly over time, and modern C++ now has object-oriented, generic, and functional features in
addition to facilities for low-level memory manipulation. It is almost always implemented as a compiled language,
and many vendors provide C++ compilers, including the Free Software Foundation, LLVM, Microsoft, Intel, Oracle,
and IBM, so it is available on many platforms. C++ has also been found useful in many contexts, with key strengths
being software infrastructure and resource-constrained applications, including desktop applications, video games,
servers (e.g. e-commerce, Web search, or SQL Servers), and performance-critical applications (e.g. telephone
switches or space probes).
Python is an interpreted, high-level, general-purpose programming language. Created by Guido van Rossum
and first released in 1991. Its language constructs and object-oriented approach aim to help programmers write
clear, logical code for small and large-scale projects. Python is dynamically typed and garbage-collected. It supports
multiple programming paradigms, including structured (particularly, procedural), object-oriented, and functional
programming. Python was conceived in the late 1980s as a successor to the ABC language. Python 2.0 released in
2000 and Python 3.0, released in 2008, was a major revision of the language that is not completely backward-
compatible, i.e. Python 2 code does not run unmodified on Python 3. The Python 2 language was officially
discontinued in 2020 (first planned for 2015) and now only Python 3.5.x and later are supported.
Java is a general-purpose programming language that is class-based and object-oriented, and designed to
have as few implementation dependencies as possible. It is intended to let application developers write once, run
anywhere (WORA), meaning that compiled Java code can run on all platforms that support Java without the need of
recompilation. Java applications are typically compiled to byte code that can run on any Java virtual machine (JVM)
regardless of the underlying computer architecture. The syntax of Java is similar to C and C++. Java was originally
developed by James Gosling at Sun Microsystems (which has since been acquired by Oracle) and released in 1995 as
a core component of Sun Microsystems' Java platform.
3
C# (pronounced see sharp, like the musical note C♯, but written with the number sign) is a general-purpose,
multi-paradigm programming language encompassing strong typing, lexically scoped, imperative, declarative,
functional, generic, object-oriented (class-based), and component-oriented programming disciplines. It was
developed around 2000 by Microsoft as part of its .NET initiative and later approved as an international standard by
ECMA in 2002 and ISO in 2003. C# was designed by Anders Hejlsberg, and its development team is currently led by
“Mads Torgersen”. The most recent version is 8.0, which was released in 2019 alongside Visual Studio 2019 version
16.3 (a preview version of C# 9.0 was released in 2020 with Visual Studio 2019 version 16.6).
What is .NET?
Ans: .NET is a free, cross-platform, open source developer platform for building many different types of applications
like Desktop, Web, Mobile, Games and IOT by using multiple languages, editors, and libraries.
What is a Platform?
Ans: It is the environment in which a piece of software is executed. A platform can also be called as the stage on
which computer programs can run. Platform can refer to the type of processor (CPU) on which a given operating
system runs, the type of operating system on a computer or the combination of the type of hardware and the type
of operating system running on it. An example of a common platform is Microsoft Windows running on x86
architecture. Other well-known desktop computer platforms include Linux/Unix and macOS
What is Cross-platform?
Ans: In computing, cross-platform software (also multi-platform software or platform-independent software) is
computer software that is implemented to run on multiple platforms. For example, a cross-platform application may
run on Microsoft Windows, Linux, and macOS. Cross-platform programs may run on as many as all existing platforms,
or on few platforms.
4
2. The Common Type System (CTS): A set of data types and operations that are shared by all CTS-compliant
programming languages. According to this all .NET Languages has to adopt the rule “Uniform Data Type
Structure” i.e. similar data types must be same in size in all Languages of .NET.
3. The Metadata: Information about program structure is language - independent, so that it can be referenced
between languages and tools’, making it easy to work with code written in a language the developer is not
aware.
4. The Virtual Execution System (VES): The VES loads and executes CLI-compatible programs. All compatible
.NET languages compile to Common Intermediate Language (CIL), which is an intermediate code that is
abstracted from the platform hardware. When the code is executed, the platform-specific VES will compile
the CIL to the machine language according to the specific hardware and operating system.
1. The Class Library provides a set of APIs and types for common functionality. It provides types for strings,
dates, numbers, etc. The Class Library includes APIs for reading and writing files, connecting to databases,
drawing, and more.
2. The Common Language Runtime (CLR) is the heart of .NET Framework and the execution engine that
handles running applications. It provides services like thread management, garbage collection, type-safety,
exception handling, and more.
5
.NET applications can be written in any .NET Language like C#, F#, or Visual Basic. Source Code we write by
using some .NET Language is compiled into a language-agnostic Common Intermediate Language (CIL) and the
compiled code is stored as assemblies (files with a “.dll” or “.exe” extension). When we run the applications, CLR
takes the assemblies and uses a just-in-time compiler (JIT) to turn it into machine code that can execute on the
specific architecture of the computer it is running on.
6
How many versions do we have for .NET Framework?
Ans: There are multiple versions of .NET Framework but each new version adds new features but retains features
from previous versions. List of .NET Framework Versions:
.NET Framework 1.0 .NET Framework 1.1 .NET Framework 2.0 .NET Framework 3.0
.NET Framework 3.5 .NET Framework 4 .NET Framework 4.5 .NET Framework 4.5.1
.NET Framework 4.5.2 .NET Framework 4.6 .NET Framework 4.6.1 .NET Framework 4.6.2
.NET Framework 4.7 .NET Framework 4.7.1 .NET Framework 4.7.2 .NET Framework 4.8
Can customers continue using the .NET Framework and get support?
Ans: Yes. Many products both within and outside Microsoft rely on .NET Framework. The .NET Framework is a
component of Windows and receives the same support as Windows version which it ships with or on which it is
installed. .NET Framework 4.8 is the latest version of .NET Framework and will continue to be distributed with future
releases of Windows. As long as it is installed on a supported version of Windows, .NET Framework 4.8 will continue
to also be supported.
Architecture of .NET Core: The two main components of .NET Core are CoreCLR and CoreFX, respectively, which are
comparable to the Common Language Runtime (CLR) and the Framework Class Library (FCL) of the .NET Framework's
Common Language Infrastructure (CLI) implementation.
1. CoreFX is the foundational class libraries for .NET Core. It includes types for collections, file systems,
console, JSON, XML and many others.
7
2. CoreCLR is the .NET execution engine in .NET Core, performing functions such as garbage collection and
compilation to machine code. As a CLI implementation of Virtual Execution System (VES), CoreCLR is a
complete runtime and virtual machine for managed execution of .NET programs and includes a just-in-time
compiler called RyuJIT.
Note: .NET Core releases have a single product version, that is, there is no separate CLR version.
What is CoreFX?
Ans: CoreFX, also referred to as the Unified Base Class Library, consists of the basic and fundamental classed that
form the core of the .Net Core platform. These set of libraries comprise the System.* (and to a limited extent
Microsoft.*) namespaces. Majority of the .NET Core APIs are also available in the .NET Framework, so you can think
of CoreFX as an extension of the .NET Framework Class Library.
What is CoreCLR?
Ans: CoreCLR is the .NET execution engine in .NET Core which is a complete runtime and virtual machine for managed
execution of .NET programs and includes a just-in-time compiler called RyuJIT, performing functions such as garbage
collection and compilation to machine code. CoreCLR is built from the same code base of the Framework CLR.
What is Roslyn?
Ans: Roslyn is the codename-that-stuck for the open-source compiler for C# and Visual Basic.NET. It is an open
source, cross-platform, public language engine for C# and VB. The conversations about Roslyn were already ongoing
when “Mads Torgersen” joined Microsoft in 2005 - just before .NET 2.0 would ship. That conversation was about
rewriting C# in C# which is a normal practice for programming languages. But there was a more practical and
important motivation: the creators of C# were not programming in C# themselves, they were coding in C++.
.NET CORE FAQ’s
What is .NET Core?
Ans: The .NET Core platform is a new .NET stack that is optimized for open source development. .NET Core has two
major components. It includes a runtime that is built from the same codebase as the .NET Framework CLR. The .NET
Core runtime includes the same GC and JIT (RyuJIT), but doesn’t include features like Application Domains or Code
Access Security. .NET Core also includes the base class libraries. These libraries are the same code as the .NET
Framework class libraries, but have been factored to enable to ship as smaller set of libraries. .NET Core refers to
several technologies including ASP.NET Core, Entity Framework Core, and more.
8
• Performance: Delivers high performance with features like hardware intrinsics, tiered compilation, and
Span<T>.
• Consistent across environments: Runs your code with the same behavior on multiple operating systems
and architectures, including x64, x86, and ARM.
• Command-line tools: Includes easy-to-use command-line tools that can be used for local development and
for continuous integration.
• Flexible deployment: You can include .NET Core in your app or install it side-by-side (user-wide or system-
wide installations). Can be used with Docker containers.
9
Ans: .NET Core and .NET Framework share many of the same components and you can share code across the two.
Some key differences include:
• .NET Core is cross-platform and runs on Linux, macOS, and Windows. .NET Framework only runs on
Windows.
• .NET Core is open-source and accepts contributions from the community. The .NET Framework source code
is available, but does not take direct contributions.
• The majority of .NET innovation happens in .NET Core.
• .NET Framework is included in Windows and automatically updated machine-wide by Windows Update.
.NET Core is shipped independently.
What's the difference between .NET Core, .NET Framework, and Xamarin?
Ans: difference between .NET Core, .NET Framework and Xamarin are:
• .NET Framework is the “traditional” flavor of .NET that's distributed with Windows. Use this when you are
building a desktop Windows or UWP app, or working with older ASP.NET 4.8.
• .NET Core is cross-platform .NET that runs on Windows, Mac, and Linux. Use this when you want to build
console or web apps that can run on any platform, including inside Docker containers.
• Xamarin is used for building mobile apps that can run on iOS, Android, or Windows Phone devices.
Red Hat supports .NET Core on Red Hat Enterprise Linux (RHEL). Red Hat builds .NET Core from source and makes it
available in the Red Hat Software Collections. Red Hat and Microsoft collaborate to ensure that .NET Core works well
on RHEL (Red Hat Enterprise Linux).
10
Ans: GitHub is a code hosting platform for collaboration and version control. It is a repository (usually abbreviated
to “repo”) is a location where all the files for a particular project are stored which lets you (and others) work together
on projects. Each project has its own repo, and you can access it with a unique URL. Git is an open-source version
control system that was started by “Linus Torvalds” - the same person who created Linux. Git is similar to other
version control systems—Subversion, CVS, and Mercurial to name a few.
EOL (end of life) releases have reached end of life, meaning it is not longer supported and recommended moving to
a supported version.
LTS (long-term support) releases have an extended support period. Use this if you need to stay supported on the
same version of .NET Core for longer.
.NET 5 (.NET Core vNext)
.NET 5 is the next step forward with .NET Core. This new project and direction are a game-changer for .NET. With
.NET 5, your code and project files will look and feel the same no matter which type of app you’re building. You’ll
have access to the same runtime, API and language capabilities with each app. The project aims to improve .NET in
a few key ways:
• Produce a single .NET runtime and framework that can be used everywhere and that has uniform runtime
behaviors and developer experiences.
• Expand the capabilities of .NET by taking the best of .NET Core, .NET Framework, Xamarin and Mono.
• Build that product out of a single code-base that developers (Microsoft and the community) can work on
and expand together and that improves all scenarios.
Microsoft skipped the version 4 because it would confuse users that are familiar with the .NET Framework,
which has been using the 4.x series for a long time. Additionally, they wanted to clearly communicate that .NET 5 is
the future for the .NET platform. They are also taking the opportunity to simplify naming. They thought that if there
is only one .NET going forward, they don’t need a clarifying term like “Core”. The shorter name is a simplification
and also communicates that .NET 5 has uniform capabilities and behaviors. Feel free to continue to use the “.NET
Core” name if you prefer it.
Runtime experiences:
11
Mono is the original cross-platform implementation of .NET. It started out as an open-source alternative to
.NET Framework and transitioned to targeting mobile devices as iOS and Android devices became popular. Mono is
the runtime used as part of Xamarin.
CoreCLR is the runtime used as part of .NET Core. It has been primarily targeted at supporting cloud
applications, including the largest services at Microsoft, and now is also being used for Windows desktop, IoT and
machine learning applications.
Taken together, the .NET Core and Mono runtimes have a lot of similarities (they are both .NET runtimes
after all) but also valuable unique capabilities. It makes sense to make it possible to pick the runtime experience you
want. They are in the process of making CoreCLR and Mono drop-in replacements for one another and will make it
as simple as a build switch to choose between the different runtime options.
.NET Schedule: .NET 5 will ship in November 2020, and then they intend to ship a major version of .NET once a year,
every November:
The .NET 5 project is an important and exciting new direction for .NET. You will see .NET become simpler
but also have broader and more expansive capability and utility. All new development and feature capabilities will
be part of .NET 5, including new C# versions. We see a bright future ahead in which you can use the same .NET APIs
and languages to target a broad range of application types, operating systems, and chip architectures. It will be easy
to make changes to your build configuration to build your applications differently, in Visual Studio, Visual Studio for
Mac, Visual Studio Code, and Azure DevOps or at the command line.
12
C# Programming Langauge
C# (pronounced see sharp, like the musical note C♯, but written with the number sign is a general-purpose,
programming language encompassing strong typing, lexically scoped, imperative, declarative, functional, generic,
object-oriented (class-based), and component-oriented programming disciplines. It was developed around 2000 by
Microsoft as part of its .NET initiative and later approved as an international standard by Ecma in 2002 and ISO in
2003.
The name "C Sharp" was inspired by the musical notation where a sharp indicates that the written note
should be made a semitone higher in pitch. This is similar to the language name of C++, where "++" indicates that a
variable should be incremented by 1 after being evaluated. The sharp symbol also resembles a ligature of four "+"
symbols (in a two-by-two grid), further implying that the language is an increment of C++. Due to technical limitations
of display and the fact that the sharp symbol is not present on most keyboard layouts, the number sign was chosen
to approximate the sharp symbol in the written name of the programming language.
C# was designed by Anders Hejlsberg, and its development team is currently led by Mads Torgersen. C# has
Procedural, Object Oriented syntax based on C++ and includes influences from several programming languages, most
importantly Delphi and Java with a particular emphasis on simplification. The most recent stable version is 8.0, which
was released in 2019 and preview version of 9.0 was released in My 20, 2020.
History: During the development of the .NET Framework, the class libraries were originally written using a managed
code compiler system called "Simple Managed C" (SMC). In January 1999, Anders Hejlsberg formed a team to build
a new language at the time called Cool, which stood for "C-like Object Oriented Language". Microsoft had considered
keeping the name "Cool" as the final name of the language, but chose not to do so for trademark reasons. By the
time .NET project was publicly announced at the July 2000 Professional Developers Conference, the language had
been renamed C#, and the class libraries and ASP.NET runtime had been ported to C#. Anders Hejlsberg is C#'s
principal designer and lead architect at Microsoft, and was previously involved with the design of Turbo Pascal,
Borland Delphi and Visual J++. In interviews and technical papers he has stated that flaws in most major
programming languages (e.g. C++, Java, Delphi, and Smalltalk) drove the design of the C# language.
Design Goals: The Ecma standard lists these design goals for C#.
13
➢ Partial types
➢ Anonymous methods
➢ Iterators
➢ Nullable value types
➢ Getter/setter separate accessibility
➢ Static classes
➢ Delegate inference
➢ Null coalescing operator
14
➢ Expanded expression bodied members
➢ Ref locals and returns
➢ Discards
➢ Binary Literals and Digit Separators
➢ Throw expressions
15
To write a program we generally follow 2 different approaches in the industry:
1. Procedural Programming Approach
2. Object Oriented Programming Approach
Procedural Programming Approach: This is a very traditional approach followed by the industry to develop
applications till 70's. E.g.: COBOL, Pascal, FORTRAN, C, etc.
In this approach a program is a collection of members like variables and functions, and the members that
are defined inside the program should be explicitly called for execution and we do that calling from “main” function
because it is the entry point of any program that is developed by using any programming language.
Note: the drawbacks of procedural programming languages are they don't provide security and re-usability.
Object Oriented Programming Approach: This came into existence in 70's to overcome the drawbacks of Procedural
Programming Language’s by providing Security and Re-usability.
E.g.: CPP, Python, Java, C#, etc.
In an Object Oriented Programming approach also a program is a Collection of members like variables and
functions only, but the main difference between Object Oriented Languages and Procedural Languages is, here to
protect the members of a program we put them under a container or wrapper known as a "class".
What is a class?
Ans: it is a user-defined type very much similar to structures we have learnt in C language, i.e. by using these we can
define new types, whereas the difference between the two are, structure in “C” language can contain only variables
in it but class of Object Oriented languages can contain both variables and functions also.
16
struct Student class Employee
{ {
int Id; int Id;
char Name[25]; string Name, Job;
float Marks, Fees; float Salary;
}; -Can be defined with functions also
};
In the above case int, float and char are pre-defined structures whereas string is a pre-defined class which
we are calling them as types, same as that Student and Employee are also types (user-defined). The other difference
between int, float, char and string types, as well as Student and Employee types is the 1st 4 are scalar types which
can hold 1 and only 1 value under them whereas the next 2 are complex types which can hold more than 1 value
under them.
Note: In the above case “i” is a copy of pre-defined type int for which memory gets allocated and the above rule of
types can't be consumed directly, applies both to pre-defined and user-defined types also.
int i; //i is a copy of pre-defined type int
string s; //s is a copy of pre-defined type string
Student ss; //ss is a copy of user-defined type Student
Employee emp; //emp is a copy of user-defined type Employee
Note: Generally copies of scalar types like int, float, char, bool, string, etc. are known as variables, whereas copies
of complex types which we have defined like Student and Employee are known as Objects or Instances.
Conclusion: After defining a class or structure if we want to consume them, first we need to create a copy of them
and then only the memory which is required for execution gets allocated and by using that copy (Object or Instance)
only we can call members that are defined under them.
Note: CPP is the first Object Oriented Programming Language which came into existence, but still it suffers from a
criticism that it is not fully Object Oriented Language; because in CPP Language we can't write main function inside
of the class and according to the standards of Object Oriented Programming each and every Member of the Program
should be inside of the class.
17
The reason why we write main function outside of class is, if it is defined inside of the class then it becomes
a member of that class and members of a class can be called only by using object of that class, but un-fortunately
we create object of class inside main function only, so until and unless object of class is created main function can't
be called and at the same time until and unless main function starts it's execution, object creation will not take place
and this is called as "Circular Dependency" and to avoid this problem, in CPP Language we write main function
outside of the class.
Object Oriented Programming in Java: Java language came into existence in the year 1995 and here also a class is a
collection of members like variables and methods. While designing the language, designers have taken it as a
challenge that their language should not suffer from the criticism that it is not fully Object Oriented so they want
“main” method of the class to be present inside of the class only and still execute without the need of class object
and to do that they have divided members of a class into 2 categories, like:
• Non-static Members
• Static Members
Every member of a class is by default a non-static member only and what we have learnt till now in C or
CPP Language is also about non-static members only, whereas if we prefix any of those members with static keyword,
we call them as Static Members.
class Test
{
int x = 100; //Non-Static Member
static int y = 200; //Static Member
}
Note: Static members of the class doesn't require object of that class for both initialization and execution also,
whereas non-static members require it, so in Java Language "main method" is defined inside of the class only but
declared as static, so even if it is inside of the class also it can start the execution without the need of class object.
Object Oriented Programming in C#: C# Language came into existence after Java and was influenced by Java, so in
C# Language also the programming style will be same as Java i.e. defining Main method inside the class by declaring
it as "static".
Note: In Java or C# Languages if at all the class contains only Main method in it we don't require to create object or
instance of that class to run the class.
18
Writing programs by using CSharp Language: CSharp language has lot of standards to be followed while writing
code, as following:
1. It's a case sensitive language so we need to follow the below rules and conventions:
I. All keywords in the language must be in lower case (rule).
II. While consuming the libraries, names will be in Pascal case (rule). E.g.: WriteLine, ReadLine
III. While defining our own classes and members to name them we can follow any casing pattern but Pascal
case is suggested (convention).
2. A CSharp program should be saved with “.cs” extension.
3. We can use any name as a file name under which we write the program but class name is suggested to be
used as file name also.
4. To write programs in C# we use an IDE (Integrated Development Environment) known as Visual Studio.Net
but we can also write them by using any text editor like Notepad also.
Syntax to define a class:
[<modifiers>] class <Name>
{
-Define Members here [] => Optional
} <> => Any
• modifiers are some special keywords that can be used on a class like public, internal, static, abstract, partial,
sealed, etc.
• class is a keyword to tell that we are defining a class just like we used struct keyword to define a structure
in C Language.
• <Name> refers to name of the class for identification.
• Members refer to contents of the class like fields, methods, etc.
Syntax to define Main Method in the class:
static void Main( [string[] args] )
{
-Stmt's
}
• static is a keyword we use to declare a member as static member and if a member is declared as static,
instance of the class is not required to call or execute it. In C# Main method should be declared static to
start the execution from there.
• void is a keyword to specify that, Main method is non-value returning.
• Main is name of the method, which can't be changed and more over it should be in Pascal Case.
• If required (optional) we can pass parameters to Main method but it should be of type string array only.
• Stmt's refers to the logic we want to implement.
Writing the first program in CSharp using Notepad:
19
Step 2: Saving the program.
Create a folder on any drive of your computer with the name “CSharp” and save the above file into that folder
naming it as “First.cs”.
System is a namespace and a namespace is a logical container for types like: Class, Structure, Interface,
Enum and Delegate, and we use these namespaces in a language for 2 reasons:
1. Grouping related types i.e. types that are designed for developing similar kind of App's are grouped together
under a namespace for easy access and identification as following:
2. To overcome the naming collision i.e. if a project contains multiple types with the same name we can
overcome conflict between names by putting them under separate namespaces, as following:
Note: Every pre-defined type in our Libraries is defined under some namespace and we can also define types under
namespaces and we will learn this process while working with Visual Studio IDE.
20
If a type is defined under any namespace then, when ever and where ever we want to consume the type
we need to prefix namespace name to type name and this is the reason why in our previous program we have
referred to “Console” class as “System.Console”. To overcome the problem of prefixing namespace name every time
before the type, we are provided with an option of “importing a namespace” which is done by “using directive” as
following:
Syntax: using <namespace>;
using System;
using Microsoft.VisualBasic;
Note: We can import any no. of namespaces as above but each import should be a separate statement.
What is a directive?
Ans: directive in our language is an instruction that is given to the compiler which it has to follow, by importing the
namespace we are telling the C# compiler that types consumed in the program are from the imported namespace.
To test the process of importing a namespace write the below code in Notepad and execute:
using System;
class Second
{
static void Main()
{
Console.Clear();
Console.WriteLine("Importing a namespace.");
}
}
Note: If there are multiple namespaces containing a type with same name then it's not possible to consume those
types by importing the namespace, and in such cases it's mandatory to refer to each type by prefixing the namespace
name to them as following:
E.g.: NSP1.First NSP2.First
using static directive:
This is a new feature introduced in “C# 6.0” which allows us to import a type and then consume all the static
members of that type without a type name prefix.
Syntax: using static <namespace.type>;
using static System.Console;
To test the process of importing a class write below code in Notepad and execute:
21
Data Types in C#
• All the above types are known as primitive/pre-defined types i.e. they are defined under the libraries of our
language which can be consumed from anywhere.
• All C# Types after compilation of source code gets converted into IL Types and in IL Format these types are
either classes or structures defined under the "System" namespace. String and Object types are classes,
whereas rest of the other 15 types, are structures.
• short, int, long & sbyte types can store signed integer (Positive or Negative) values where as ushort, uint,
ulong & byte types can store un-signed integer (Pure Positive) values only.
• Guid is a type used for storing Unique Identifier values that are loaded from SQL Server DB, which is a 32
byte alpha-numeric string holding a Global Unique Identifier value and it will be in the following format:
00000000-0000-0000-0000-000000000000.
22
• The size of char type has been increased to 2 bytes for giving support to Unicode characters i.e. characters
of languages other than English.
• We are aware that every English language character has a numeric value representation known as ASCII;
characters of languages other than English also have that numeric value representation and we call it as
Unicode.
char ch = 'A'; => ASCII => Binary
char ch = 'अ'; => Unicode => Binary
• Just like ASCII values converts into binary for storing by a computer; Unicode values also converts into
binary, but the difference is ASCII requires 1 byte of memory for storing its value whereas Unicode requires
2 bytes of memory for storing its value.
• String is a variable length type i.e. it doesn't have any fixed size and its size varies based on the value that
is assigned to it.
• Object is a parent of all the types, so capable of storing any type of value in it and more over it is also a
variable length type.
Syntax to declare fields and variables in a class:
[<modifiers>] [const] [readonly] <type> <name> [=default value] [,...n]
class Test
{
int x; //Field (Global Scope)
static void Main()
{
int x = 100; //Variable (Local Scope)
}
}
• "<type>" refers to the type of field or variable we want to declare and it can be any of the 17 types we
discussed above.
• "<name>" refers to the name of the field or variable and it should be unique within the location.
E.g.: int i; float f; bool b; char c; string s; object o; DateTime dt; Guid id;
• Fields and variables can be initialized with any value at the time of their declaration and if they are not
initialized then every field has a default value which is "0" for all numeric types, "false" for bool type, "\0"
for char type, "00000000-0000-0000-0000-000000000000" for Guid type, "01/01/0001 00:00:00" for
DateTime type and "null" for string and object types.
Note: Variables doesn't have any default value so it's must to initialize them while declaration or before
consumption. E.g.: int x = 100;
• Modifiers are generally used to define the scope of a field i.e. from where it can be accessed, and the default
scope for every member of a class in our language is private which can be either changed to public or
internal or protected.
23
• "const" is a keyword to declare a constant and those constants values can't be modified once after their
declaration:
const float pi = 3.14f; //Declaration and Initialization
• "readonly" is a keyword to declare a field as readonly and these readonly field values also can't be modified,
but after initialization:
readonly float pi; //Declaration
pi = 3.14f; //Initialization
Note: decimal values are by default treated as double by the compiler, so if we want to use them as float the value
should be suffixed with character "f" and "m" to use the value as decimal.
float pi = 3.14f; double pi = 3.14; decimal pi = 3.14m
using System;
class TypesDemo {
static int x; //Field
static void Main() {
Console.Clear();
Console.WriteLine("Field x value is: " + x + " and it's type is: " + x.GetType());
int y = 20; //Variable
Console.WriteLine("Variable y value is: " + y + " and it's type is: " + y.GetType());
float f = 3.14f; //Variable
Console.WriteLine("Variable f value is: " + f + " and it's type is: " + f.GetType());
double d = 3.14; //Variable
Console.WriteLine("Variable d value is: " + d + " and it's type is: " + d.GetType());
decimal de = 3.14m; //Variable
Console.WriteLine("Variable de value is: " + de + " and it's type is: " + de.GetType());
}
}
Note: GetType() is a pre-defined method which returns the type of a variable or field or instance on which it is called.
Data Types are divided into 2 categories:
1. Value Types 2. Reference Types
Value Types:
• All fixed length types comes under the category of value types. E.g.: integer types, decimal types, bool type,
char type, DateTime type and Guid type.
• Value types will store their values on "Stack" and stack is a Data Structure that works on a principal “First
in Last out (FILO)” or “Last in First out (LIFO)”.
• Each and every program when it starts the execution, a Stack will be created and given to that program for
storing its values and in the end of program's execution Stack is destroyed.
• Every program will be having its own stack for storing values that are associated with the program and no
2 programs can share the same stack.
24
• Stack is under the control of O.S. and memory allocation is performed only in fixed length i.e. once allocated
that is final which can't either be increased or decreased also.
Reference Types:
• All variable length types comes under the category of reference types and these types will store their values
on "Heap" memory and their address or reference is stored on Stack. E.g.: String and Object.
• Heap memory doesn't have any limitations like stack and it provides a beautiful feature like, Dynamic
Memory Management and because of that, all programs in execution can share the same heap.
• In older programming languages like C and CPP, Heap Memory is under developer's control, whereas in
modern programming languages like Java and .NET, Heap memory is under control of special component
known as "Garbage Collector", so we call Heap memory in these languages as "Managed Heap".
Nullable Value Types: These are introduced in C# 2.0 for storing null values under value types because, by default
value types can't store null values under them where as reference types can store null values under them.
string str = null; //Valid
object obj = null; //Valid
int i = null; //Invalid
decimal d = null; //Invalid
To overcome the above problem nullable value types came into picture and if we want a value type as
nullable we need to suffix the type with "?" and declare it as following:
int? i = null; //Valid
decimal? d = null; //Valid
Implicitly typed variables: This is a new feature introduced in C# 3.0, which allows declaring variables by using "var"
keyword, so that the "type" of that variable is identified based on the value that is assigned to it, for example:
var i = 100; //i is of type int
var f = 3.14f; //f is of type float
var b = true; //b is of type bool
var s = "Hello"; //s is of type string
25
Dynamic Type: This is a new type introduced in C# 4.0, which is very similar to implicitly typed variables we discussed
above, but here in place of "var" keyword we use dynamic.
Differences between "var" and "dynamic"
var dynamic
Type identification is performed at compilation time. Type identification is performed at runtime.
Once the type is identified can't be changed to a new We can change the type of dynamic with a new value in
type again. every statement.
var v = 100; //v is of type int dynamic d = 100; //d is of type int
v = 34.56; //Invalid d = 34.56; //d is of type double (Valid)
Can't be declared with-out initialization. Declaration time initialization is only optional.
var v; //Invalid dynamic d; //Valid
d = 100; //d is of type int
d = false; //d is of type bool
d = "Hello"; //d is of type string
d = 34.56; //d is of type double
Can be used for declaring variables only. Can be used for declaring variables and fields also.
using System;
class VarDynamic
{
static void Main()
{
var i = 100;
Console.WriteLine(i.GetType());
var c = 'A';
Console.WriteLine(c.GetType());
var f = 45.67f;
Console.WriteLine(f.GetType());
var b = true;
Console.WriteLine(b.GetType());
var s = "Hello";
Console.WriteLine(s.GetType());
Console.WriteLine("----------------------------------------------------------------------------------------------------------");
dynamic d;
d = 100;
Console.WriteLine(d.GetType());
d = 'Z';
Console.WriteLine(d.GetType());
d = 34.56;
Console.WriteLine(d.GetType());
d = false;
Console.WriteLine(d.GetType());
d = "Hello";
Console.WriteLine(d.GetType());
26
}
}
Boxing and Un-Boxing:
Boxing is a process of converting values types into reference types:
int i = 100;
object obj = i; //Boxing
Unboxing is a process of converting a reference type which is created from a value type back into value type, but
un-boxing requires an explicit conversion:
int j = Convert.ToInt32(obj); //Un-Boxing
Note: “Convert” is a predefined class in “System” namespace and “ToInt32” is a static method under that class, and
this class also provides other methods for conversion like “ToDouble”, “ToSingle”, “ToDecimal”, “ToBoolean”, etc,
to convert into different types.
Taking input from end user’s into a program:
using System;
class AddNums {
static void Main() {
Console.Clear();
double d3 = d1 + d2;
ReadLine method of the Console class is used for reading the input from end user’s into our programs and this
method will perform 3 actions when used in the program, those are:
1. Waits at the command prompt for the user to enter a value.
2. Once the user finishes entering his value, immediately the value will be read into the program.
3. Returns the value as string by performing boxing because return type of the method is string.
public static string ReadLine()
27
Note: after reading the value as string in our program we need to convert it back into it’s original type by performing
an un-boxing which can be done in either of the ways:
string s1 = Console.ReadLine(); string s2 = Console.ReadLine();
double d1 = Convert.ToDouble(s1); double d2 = double.Parse(s2);
or or
double d1 = Convert.ToDouble(Console.ReadLine()); double d2 = double.Parse(Console.ReadLine());
Parse(String): this method is used to convert the string representation of a value to its equivalent type on which the
method is called.
string s1 = "100" ; int i = int.Parse(s1);
string s2 = "34.56"; double d = double.Parse(s2);
string s3 = "true"; bool b = bool.Parse(s3);
String Interpolation: String interpolation provides a more readable and convenient syntax to create formatted
strings than a string composite formatting feature. An interpolated string is a string literal that might contain
interpolation expressions. When an interpolated string is resolved to a result string, items with interpolation
expressions are replaced by the string representations of the expression results. This feature is available starting
with C# 6.0.
Operators in C#: An operator is a special symbol that tells the compiler to perform a specific mathematical or logical
operation when used between a set of operands. C# has a rich set of built-in operators as following:
Console.WriteLine(typeof(double));
double d = 34.56;
if(d is double)
Console.WriteLine("Hello World");
else
Console.WriteLine("Hello How Are You");
int x = 100;
28
Console.WriteLine(x == 100 ? "India" : "America");
Console.WriteLine(x == 200 ? "India" : "America");
int? a = 50;
Console.WriteLine(a ?? 100);
int? b = null;
Console.WriteLine(b ?? 100);
}
}
Conditional Statements in C#: It’s a block of code that executes based on a conditional and they are divided into 2
categories.
1. Conditional Branching
2. Conditional Looping
Conditional Branching: These statements allows us to branch the code depending on whether certain conditions are
met or not. C# has 2 constructs for branching code, the “if” statement which allow us to test whether a specific
condition is met or not, and the switch statement which allows us to compare an expression with a number of
different values.
Note: Curly braces are optional if the conditional block contains single statement in it or else it’s mandatory.
using System;
class IfDemo
{
static void Main()
{
Console.Write("Enter 1st number: ");
double d1 = double.Parse(Console.ReadLine());
Console.Write("Enter 2nd number: ");
double d2 = double.Parse(Console.ReadLine());
29
}
}
Syntax of “switch case” Condition:
switch (<expression>)
{
case <value>:
<stmts>;
break;
[<multiple case blocks>]
default:
<stmts>;
break;
}
Note: In C and CPP languages using a break statement after each “case block” is only optional whereas it is
mandatory in case of C# language, which should be used after default block also.
using System;
class SwitchDemo {
static void Main() {
Console.Write("Enter Student No. (1-3): ");
int Sno = int.Parse(Console.ReadLine());
switch(Sno)
{
case 1:
Console.WriteLine("Student 1");
break;
case 2:
Console.WriteLine("Student 2");
break;
case 3:
Console.WriteLine("Student 3");
break;
default:
Console.WriteLine("No student exists with given number.");
break;
}
}
}
Conditional Looping: C# provides 4 different loops that allow us to execute a block of code repeatedly until a certain
condition is met and those are:
1. for loop 2. while loop 3. do..while loop 4. foreach loop
30
2. Condition: This decides when the loop has to end.
3. Iteration: This takes the loop to the next level either in forward or backward direction.
Syntax of “for loop”:
for (initializer;condition;iteration) {
-<statements>;
}
Example:
for(int i = 1;i <= 100;i++)
{
Console.WriteLine(i);
}
Syntax of “while loop”:
while (<condition>) {
-<statements>;
}
Example:
int i = 1;
while(i <= 100) {
Console.WriteLine(i);
i++;
}
Syntax of “do..while loop”:
do {
-<statements>;
}
while (<condition>);
Example:
int i = 1;
do {
Console.WriteLine(i);
i++;
}
while (i <= 100);
Note: the minimum no. of executions in case of a “for loop” and “while loop” are “0” because in both these cases
the loop starts its execution only when the given condition is satisfied whereas the minimum no. of executions in
case of a “do...while loop” is “1” because in this case after executing the loop for first time, then only it will check
for the condition to continue the loop’s execution.
Syntax of “foreach loop”:
foreach(type var_name in array_name|collection_name)
{
-<statements>;
}
Note: foreach loop is specially designed for accessing values from an array or collection.
31
Jump Statements: these are statements which will transfer the control from 1 line of execution to another line. C#
has no. of statements that allows jumping to another line in a program, those are:
1. goto 2. break 3. continue 4. return
goto: it allows us to jump directly to another specified line in the program, indicated by a label which is an identifier
followed by a colon.
goto xxx:
Console.WriteLine(“Hello World”);
xxx:
Console.WriteLine(“Goto Called.”);
break: it is used to exit from a case in a switch statement and also used to exit from any conditional loop statement
which will switch the control to the statement immediately after end of the loop.
for (int i = 1;i <= 100;i++) {
Console.WriteLine(i);
if (i == 50)
break;
}
Console.WriteLine(“End of the loop.”);
continue: it is used only in a loop which will jump the control to iteration part of the loop without executing any
other statement that is present next to it.
for (int i = 1;i <= 100;i++)
{
if (i == 7 || i = 77)
continue;
Console.WriteLine(i);
}
return: this is used to terminate the execution of a method in which it is used and jumps out of that method, while
jumping out it can also carry a value out of that method which was only optional.
using System;
class Table
{
static void Main()
{
Console.Write("Enter a number: ");
bool Status = uint.TryParse(Console.ReadLine(), out uint x);
if(Status == true)
{
if(x == 0)
return;
Console.Clear();
for(uint i=1;i<=10;i++)
{
Console.WriteLine("{0} * {1} = {2}", x, i, x * i);
}
}
else
32
Console.WriteLine("Please enter un-signed integer value as input.");
} //End of the method
} //End of the class
Array: it is a set of similar type values that are stored in a sequential order either in the form of a row or rows &
columns. In C# language also we access the values of an array by using the index only which will start from “0” and
ends at the “no. of items - 1”. In C# arrays can be declared either as fixed length or dynamic, where a fixed length
array can store a pre-defined no. of items whereas the size of a dynamic array increases as we add new items to it.
1-Dimensional Array’s: these arrays will store data in the form of a row and are declared as following:
Example:
int[] arr = new int[5]; //Declaration and Initialization with default values
or
int[] arr; //Declaration
arr = new int[5]; //Initialization with default values
or
int[] arr = { <list of values> }; //Declaration and Initialization with given set of values
using System;
class SDArray1 {
static void Main() {
Console.Clear();
int x = 0;
int[] arr = new int[6];
33
foreach loop: this loop is specially designed for accessing values from an array or a collection. When we use foreach
loop for accessing values, the loop starts providing access to values of the array or collection by assigning the values
to loop variable in a sequential order as following:
Differences between for loop and foreach loop in accessing values of an array or collection:
1. In case of a “for loop”, the loop variable refers to index of the array whereas in case of a “foreach loop”,
the loop variable refers to values of the array.
2. By using a “for loop” we can either access or assign values to an array whereas by using a “foreach loop”
we can only access the values from an array.
3. In case of a “for loop”, the data type of loop variable is always int only irrespective of the type of values in
the array, whereas in case of a “foreach loop”, the data type of loop variable will be same as the type of
values in the array.
Array Class: this is a pre-defined class under the System namespace which provides with a set of members in it to
perform actions on an array, those are:
Sort(Array arr) => void //Method
Reverse(Array arr) => void //Method
Copy(Array src, Array dest, int n) => void //Method
GetLength(int dimension) => int //Method
Length => int //Property (Field)
using System;
class SDArray2 {
static void Main() {
Console.Clear();
int[] arr = { 54, 79, 59, 8, 42, 22, 93, 3, 73, 38, 67, 48, 18, 61, 32, 86, 15, 27, 81, 96 };
for(int i=0;i<arr.Length;i++)
Console.Write(arr[i] + " ");
Console.WriteLine();
Array.Sort(arr);
34
foreach(int i in arr)
Console.Write(i + " ");
Console.WriteLine();
Array.Reverse(arr);
foreach(int i in arr)
Console.Write(i + " ");
Console.WriteLine();
using System;
class TDArray {
static void Main() {
int x = 0; int[,] arr = new int[4, 5];
//Accessing values of TD Array by using foreach loop
foreach(int i in arr)
Console.Write(i + " ");
Console.WriteLine();
35
}
}
}
Assigning values to 2-D Array at the time of its declaration:
int[,] arr = {
{ 11, 12, 13, 14, 15 },
{ 21, 22, 23, 24, 25 },
{ 31, 32, 33, 34, 35 },
{ 41, 42, 43, 44, 45 }
};
Jagged Arrays: these are also 2-Dimensional arrays only which will store the data in the form of rows and columns
but the difference is in-case of a 2-Dimensional array all the rows will be having equal no. of columns whereas in
case of a jagged array the column size varies from row to row. Jagged arrays are also known as “array of array”
because here each row is considered as a single dimensional array and multiple single dimensional arrays with
different sizes are combined together to form a new array.
Syntax: <type>[][] <array_name> = new <type>[rows][]
Example:
int[][] arr = new int[4][]; //Declaration
or
int[][] arr = { <list of values> }; //Declaration & Initialization with given set of values
Note: in case of a jagged array we can’t initialize the array with default values at the time of its declaration i.e. first
we need to specify the no. of rows and then pointing to each row we need to specify the no. of columns to that row,
as following:
int[][] arr = new int[4][]; //Declaration
arr[0] = new int[5]; //Initialization of 1st row
arr[1] = new int[6]; //Initialization of 2nd row
arr[2] = new int[8]; //Initialization of 3rd row
arr[3] = new int[4]; //Initialization of 4th row
using System;
class JArrayDemo {
static void Main() {
Console.Clear();
36
arr[1] = new int[6];
arr[2] = new int[8];
arr[3] = new int[4];
//Accessing values of Jagged Array by using nested foreach loop
foreach(int[] iarr in arr) {
foreach(int x in iarr)
Console.Write(x + " ");
Console.WriteLine();
}
Console.WriteLine("--------------------------------------");
37
}
}
}
Assigning values to Jagged Array at the time of its declaration:
int[][] arr = {
new int[5] { 11, 12, 13, 14, 15 },
new int[6] { 21, 22, 23, 24, 25, 26 },
new int[8] { 31, 32, 33, 34, 35, 36, 37, 38 },
new int[4] { 41, 42, 43, 44 },
};
Implicitly typed arrays: Just like we can declare variables by using “var” keyword we can also declare arrays by using
the same “var” keyword and here also the type identification is performed based on the values that are assigned to
the array.
var iarr = new[] { 10, 20, 30, 40, 50 }; //Implicitly type integer array
var sarr = new[] { "Red", "Blue", "Green", "Yellow", "Magenta" }; //Implicitly typed string array
var darr = new[] { 12.34, 34.56, 56.78, 78.90 90.12 }; //Implicitly typed double array
var jarr = new[] {
new[] { 11, 12, 13, 14, 15 },
new[] { 21, 22, 23, 24, 25, 26 },
new[] { 31, 32, 33, 34, 35, 36, 37, 38 },
new[] { 41, 42, 43, 44 }
new[] { 51, 52, 53, 54, 55, 56, 57 }
}; //Implicitly typed jagged integer array
Command Line Arguments: Arguments which are passed by the user or programmer to the Main method are known
as Command-Line Arguments. Main method is the entry point for the execution of a program and this Main method
can accepts an array of strings.
using System;
class Params {
static void Main(string[] args) {
foreach(string str in args)
Console.WriteLine(str);
}
}
After compilation of the program execute the program at Command Prompt as following:
<drive>:\CSharp> Params 100 Hello 34.56 A true ⏎
Note: We can pass any no. of values as well as any type of values as Command Line Arguments to the program, but
each value should be separated with a space and all those values we passed will be captured in the string array (args)
of main method. In the above case (100, Hello, 34.56, A, true) are 5 values we have supplied to the Main method of
Params class.
Adding a given set of numbers that are passed as Command Line Arguments:
using System;
38
class AddParams {
static void Main(string[] args) {
double Sum = 0;
foreach(string str in args)
Sum = Sum + double.Parse(str);
Console.WriteLine("Sum of given {0} no's is: {1}", args.Length, Sum);
}
}
After compilation of the program execute the program at Command Prompt as following:
<drive>:\CSharp> AddParams 10 20 30 ⏎
<drive>:\CSharp> AddParams 34.56 28.93 98.45 63.28 ⏎
<drive>:\CSharp> AddParams 938.387 534 348.378 836 174.392 ⏎
<drive>:\CSharp> AddParams 18 48.37 75 56.43 97 85.19 ⏎
Note: the current version of Visual Studio is 16 which come as Visual Studio 2019.
To open Visual Studio, go to Windows Search and search for Visual Studio 2019 and click on it to open,
which will launch as following:
Applications that are developed under VS are known as projects, where each project is a collection of items
like Class, Interface, Structure, Enum, Delegate, Html Files, XML Files, and Text Files etc. To create a Project click on
"Create a new project" option in the above Page which opens a new window as following:
39
In the above window under "All languages" DropDownList select "C#", under "All platforms" DropDownList
select "Windows" and under "All project_types" DropDownList select "Console" which will display the options as
following:
Now select "Console App (.Net Core)" in the above window and click “Next” button which opens a new
window as following:
In that above window under ProjectName TextBox enter the name of project as "FirstProject", under
Location TextBox enter or select our personal folder location i.e. "<drive>:\CSharp" and click on "Create" button:
This action will create a new project with a default class Program under a file “Program.cs” which will be as
following:
using System;
namespace FirstProject
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
40
When types are defined in Visual Studio by default all those types will be under a namespace whose name
will be same as project name i.e. “FirstProject” in our case and from now each and every type of the project will be
defined with-in this namespace only. We have already learnt earlier, that a namespace is a logical container of types
like class, structure, interface, enum and delegate.
To run the class either hit Ctrl + F5 or go to “Debug” menu and select the option “Start Without Debugging”
which will save, compile and executes the program by displaying output “Hello World!” on the console window
because we have opened a “Console App.” Project. To close that console window it will display a message “Press
any key to continue . . .”, so once we hit any key it will close the window and takes us back to the studio. We can
also run the class by hitting F5 or clicking on the button in the ToolBar or go to “Debug” menu and
select the option “Start Debugging”, but in this case it save compile and executes the program but we can’t we the
output because the console window gets closed immediately and in this case to view the output we need to hold
console window and to do that use “Console.ReadLine();” method after “Console.WriteLine("Hello World!");” in
Main method of the program.
Adding new items in the project:
Under VS we find a window in the RHS known as Solution Explorer used for organizing the complete
application, which allows us to view, add and delete items under the projects, if it is not visible in the RHS then go
to “View” menu and select “Solution Explorer” which will launch on RHS that looks as following:
To add new classes under project, open Solution Explorer, right click on the project, select Add => choose
“New Item” option, which opens the "Add New Item" window as following:
In this window select "Class" template, specify a name to it in the bottom or leave the existing name and
click on Add button, which adds the class under project with the name “Class1.cs”. The new class added, also comes
under the Namespace, i.e. “FirstProject”. Now write the below code in the class Class1:
41
method in them, choose your class i.e. “FirstProject.Class1” and run. If the “Startup Object” doesn’t list your class,
again open Solution Explorer, right click on the project, select the option “Edit Project File” which opens an “XML
File” and in that we find “<StartupObject></StartupObject>” tags with the existing Startup class name listed, change
that name to your class name: “<StartupObject>FirstProject.Class1</StartupObject>” and run.
Object Oriented Programming
This is an approach that we use in the industry for developing Application, introduced in 70's replacing the
traditional Procedural Programming Approach. Procedural Programming Approach doesn't provide Security and Re-
usability, where as these 2 are the major strength of Object Oriented Programming Approach.
Any language to be called as Object Oriented needs to satisfy 4 important principals that are prescribed
under the standards of Object Oriented Programming and they are:
1. Encapsulation => hiding the data
2. Abstraction => hiding the complexity
3. Inheritance => re-usability
4. Polymorphism => behaving in different ways based on input received
Encapsulation: this is all about hiding of data or members of a program by wrapping them under a container known
as a Class, which provides security for all its contents.
Abstraction: this is all about hiding the complexity of code and then providing with a set of interfaces to consume
those functionalities, for example functions/methods in a program are good example for this, because here we are
only aware of how to call them but we are never aware of the underlying logic behind that implementation.
Inheritance: this provides re-usability i.e. members that are defined in 1 class can be consumed from other classes
by establishing parent/child relation between the classes.
Polymorphism: behaving in different ways based on the input received is known as polymorphism i.e. whenever the
input changes then the output or behavior also changes accordingly.
Class: It's a user-defined type which is in-turn a collection of members like:
• Fields
• Methods
• Constructors
• Destructors or Finalizers
42
• Properties
• Indexers
• Events
• De-constructors (Introduced in C# 7.0)
Method: It is a named block of code which performs an action whenever it is called and after completion of that
action it may or may not return any result of that action, and they are divided into 2 categories:
1. Value returning method (Function)
2. Non-value returning method (Sub-Routine)
Syntax to define a method:
[<modifiers>] void|type <Name>( [<Parameter List>] )
{
-Stmt's or Logic [] => Optional
} <> => Any
Modifiers are some special keywords which can be used on a method if required like public, internal, protected,
static, virtual, abstract, override, sealed, partial, etc.
void|type is to tell whether our method is value returning or non-value returning i.e. void implies that the method
is non-value returning, whereas if we want our method to return any value then we need to specify the type of value
it has to return by using the “type”.
Note: the return type of a method need not be any pre-defined type like int, float, char, bool, DateTime, Guid, string,
object, etc, but it can also be any user-defined type also.
[<Parameter list>]: if required we can pass parameters to our methods for execution, and parameters of a method
will make an action dynamic, for example:
GetLength(0) => Returns rows
GetLength(1) => Returns columns
43
How to call a method that is defined in a class?
Ans: Methods are of 2 types:
1. Non-Static 2. Static
Note: By default every method of a class is non-static only, and if we want to make it as static we need to prefix the
“static” modifier before the method as we are doing in case of Main method.
To call a method that is defined under any class we require to create instance of that class provided the
methods are non static, whereas if the methods are static we can call them directly by using class name, for example
WriteLine and ReadLine are static methods in class Console which we are calling in our code as Console.WriteLine
and Console.ReadLine.
Example:
Program p = new Program(); //Declaration and Initialization
or
Program p; //Declaration
p = new Program(); //Initialization
Note: with-out using “new” keyword we can’t create the instance of a class in Java and .NET Languages.
If instance is created in the same class it should be created under a static block; generally we create
instances in Main method because of 2 reasons:
1. Entry Point of the program.
2. It is a static block.
If instance is created in another class then it can be created in any block of that class i.e. either static or
non-static also.
To try all the above, create a new project in Visual Studio naming it as “OOPSProject” and write the below
code under the default class “Program”:
class Program {
//Non-value returning method without parameters
public void Test1() { //Static in behavior
int x = 5;
for (int i = 1; i <= 10; i++)
Console.WriteLine("{0} * {1} = {2}", x, i, x * i);
}
44
public void Test2(uint x, uint ub) { //Dynamic in behavior
for (uint i = 1; i <= ub; i++)
Console.WriteLine("{0} * {1} = {2}", x, i, x * i);
}
//Value returning method without parameters
public string Test3() { //Static in behavior
string str = "Hello world";
str = str.ToUpper();
return str;
}
To test the second, add a new class in the project naming it as “TestProgram.cs” and write the below code
under class:
class TestProgram {
public void CallMethods() {
Program p = new Program();
45
p.Test1(); p.Test2(9, 15);
Console.WriteLine(p.Test3());
Console.WriteLine(p.Test4("america"));
}
static void Main() {
//Creating an un-named instance and calling the method
new TestProgram().CallMethods();
Console.ReadLine();
}
}
Note: Un-named instances are created and used when we want to call any single member of a class or when we
want to use that instance only for 1 time.
Code files in a project: When we want to add a new class under any project we first open the “Add New Item”
window and in that we choose “Class Item Template”, which when added will add a file with a class template in it,
same as that we also find “Code File Item Template” which when added will add a blank file and we need to write
everything manually in it, just like we write code using Notepad.
Defining multiple classes in a file: It’s possible to define “n” no. of classes under a single “.cs” file, but “Main” method
can be defined under 1 class only. Even if it is not mandatory it is advised to use the “Class Name” under which we
defined “Main” method as the “File Name”.
User-defined return types to a Methods: The return type of a method need not be any pre-defined type, but can
also be any user-defined type also i.e. a type which is defined representing some complex data.
To test all the above, add a “Code File” under the project naming it as “UserDefinedTypes.cs” and write the
below code in it:
using System;
namespace OOPSProject {
class Emp {
public int Eno;
public string Name, Job;
public double Salary;
public bool Status;
}
class UserDefinedTypes {
public Emp GetEmpDetails(int Eno) {
Emp e = new Emp();
e.Eno = Eno;
e.Name = "Ajay";
e.Job = "Salesman";
e.Salary = 6000.00;
e.Status = true;
return e;
}
static void Main() {
UserDefinedTypes udt = new UserDefinedTypes();
46
Emp obj = udt.GetEmpDetails(1008);
Console.WriteLine(obj.Eno + " " + obj.Name + " " + obj.Job + " " + obj.Salary + " " + obj.Status);
Console.ReadLine();
}
}
}
In the above case “Emp” is a new type (User-Defined and Complex) and that type is used as a return type
for our method “GetEmpDetails”.
Parameters of a Method:
We define parameters to methods for making actions dynamic i.e. as discussed earlier every method is an
action and to make those actions dynamic we define parameters to methods.
• Input parameters will bring values into the method for execution.
• Output parameters will carry results out of the method after execution.
• InOut Parameters are a combination of above 2 i.e. these parameters will 1st bring a value into the method
for execution and after execution, the same parameter will carry results out of the method.
By default every parameter is an input parameter whereas if we want to declare any parameter as output
we need to prefix “out” keyword and to declare a parameter as InOut we need to prefix “ref” keyword before the
parameter, as following:
public void Test(int a, out int b, ref int c)
To test Output Parameters add a new class in the Project naming it as “OutputParameters.cs” and write the
below code in it:
class OutputParameters {
public void Math1(int a, int b, out int c, out int d) { //Before C# 7.0
c = a + b;
d = a * b;
}
public (int, int) Math2(int a, int b) { //From C# 7.0
int c = a + b;
int d = a * b;
return (c, d);
}
static void Main() {
OutputParameters obj = new OutputParameters();
47
obj.Math1(100, 25, out Sum1, out Product1);
Console.WriteLine("Sum of given no's is: " + Sum1);
Console.WriteLine("Product of given no's is: " + Product1);
Console.WriteLine("----------------------------------------------------------------------------------------------------------");
obj.Math1(165, 15, out int Sum2, out int Product2); //From C# 7.0
Console.WriteLine("Sum of given no's is: " + Sum2);
Console.WriteLine("Product of given no's is: " + Product2);
Console.WriteLine("----------------------------------------------------------------------------------------------------------");
class InOutParameters {
public void Factorial(ref uint fact) {
if (fact == 0 || fact == 1) {
fact = 1;
}
else {
uint result = 1;
for(uint i = 2;i <= fact;i++) {
result = result * i;
}
fact = result;
}
}
static void Main() {
InOutParameters p = new InOutParameters();
uint num = 8;
Console.Write($"Factorial of {num} is: ");
p.Factorial(ref num);
Console.WriteLine(num);
Console.ReadLine();
}
}
48
Params KeyWord: By prefixing this keyword before an array parameter of any method we get a chance to call that
method without explicitly creating an array and pass to the method, but we can directly pass a set of values in a
“Comma-Seperated” list.
public void AddParams(params double[] args)
For example WriteLine method of Console class is defined as below:
public static void WriteLine(string format, params object[] args)
Note: In the above case x is a mandatory parameter whereas y and z are optional parameters and while defining
methods with mandatory and optional parameters, mandatory parameters should be in the 1 st place of parameter
list, followed by optional parameters in the last.
To test “params” keyword and “default valued parameters” add a new class in the project naming it as
“MethodParameters.cs” and write the below code in it:
class MethodParameters {
public void AddParams(params double[] args) { //Method using params keyword on an array parameter
double Sum = 0;
foreach (double d in args) {
Sum = Sum + d;
}
Console.WriteLine($"Sum of given {args.Length} no's is: {Sum}");
}
//Method with default valued parameters
public void AddNums(int x, int y = 50, int z = 25) {
Console.WriteLine($"Sum of the 3 no's is: { x + y + z}");
}
static void Main() {
MethodParameters obj = new MethodParameters();
//Calling a method which uses array as parameter with params keyword
obj.AddParams();
obj.AddParams(56.19);
obj.AddParams(23.45, 78);
obj.AddParams(45, 67.82, 92);
obj.AddParams(32.91, 87, 56.23, 17);
Console.WriteLine("----------------------------------------------------------------------------------------------------------");
49
//Calling a method which has default valued parameters
obj.AddNums(100);
obj.AddNums(100, 100);
obj.AddNums(100, z: 100);
obj.AddNums(100, 100, 100);
Console.ReadLine();
}
}
Understanding the difference between variable, instance and reference of a class: to understand about a variable,
instance and reference of a class add a new class in our Project naming it as “First.cs” and write below code in it:
class First {
public int x = 100;
static void Main() {
First f; //f is a variable of class
f = new First(); //f is a instance of class
Console.WriteLine(f.x);
Console.ReadLine();
}
}
Every member of a class, if it is non-static can be accessed from the main Method only by using instance of
that class. So in the above case to print the value of “x”, we created instance of class First under Main method.
A variable of class is a copy of class which is not initialized so it doesn't have any memory allocation and
can't be used for calling or accessing the members.
An instance of class is a copy of class which is initialized by using “new” keyword and for an instance memory
is allocated, so by using this instance we can access or call members of that class.
Dereferencing an Instance: it is possible to dereference the instance of any class by assigning “null” to the instance
and once “null” is assigned to instance we can't use that instance for calling members of class and if we try to do so,
we get a runtime error. To test this, re-write the code under “Main Method” of class “First” as below:
Note: once null is assigned to a instance, internally the memory which is allocated for that instance is not de-
allocated immediately, but only gets marked as un-used and all those un-used objects memory will be de-allocated
by “Garbage Collector” whenever it comes into picture.
Creating multiple instances to a class: it is possible to create multiple instances to a class and each instance we
create for the class will be having a separate memory allocation for its members as following:
50
First f1 = new First();
First f2 = new First();
Instances are unique i.e. any modifications that we perform on the members of 1 instance will not reflect
to the members of other instances of the class, and to test this rewrite the code under “Main method” of class First
as below:
Reference of a class: we can initialize the variable of a class by using any existing instance of that class and we call it
as a reference of the class. References of class will not have any memory allocation like instances, i.e. they will be
consuming the memory of instance using which they are initialized, so a reference is just a pointer to an instance, as
following:
Because an instance and reference are accessing the same memory, changes that are performed on the
members by using the instance will reflect when those members are accessed by using reference and vice versa. To
test this, rewrite code under “Main method” of class First as below:
Note: when an instance and references are accessing the same memory and if “null” is assigned to any 1 of them,
then the 1 to whom null is assigned can't access the memory any more, but still the others can access it as is for
calling the members. To test this re-write code under “Main method” of class First as below:
51
First f1 = new First();
First f2 = f1;
f1 = null;
Console.WriteLine(f2.x); //Valid
Console.WriteLine(f1.x); //Invalid (Causes runtime error)
Console.ReadLine();
Variable of Class: this is a copy of class which is not initialized, so by using this we can't call any members of that
class.
Instance of Class: this is a copy of class which is initialized by using the "new" keyword and by using this we can call
members of that class.
Reference of Class: this is a copy of class which is initialized by using any existing instance of that class and this works
same as an instance. By using the reference also we can call members of that class.
What happens internally when we create the instance of a class?
Ans: When we create the instance of any class internally following actions will take place:
1. Reads the classes to identify their members.
2. Invokes the constructors of all those classes.
3. Allocates the memory that is required for execution.
Constructor
This is a special method present under a class responsible for initializing the data members (fields) of that
class. This method is invoked automatically when we create the instance of class. The name of constructor method
is exactly the same name of the class and more over it's a non-value returning method. Every class requires a
constructor in it, if we want to create the instance of that class or else we can't create the instance of any class.
Note: While defining a class it's the responsibility of developers to define a constructor explicitly under his class, and
if they fails to do so, on-behalf of the developers an implicit constructor gets defined in those classes; so till now we
are creating instances of the classes we defined, by using those implicit constructors only.
52
Syntax to define a Constructor Explicitly:
[<modifiers>] <Class Name>( [<Parameter List>] )
{
-Stmt's to execute
}
To test defining a constructor explicitly, add a new class in the project naming it as “ConDemo.cs” and write
the below code in it:
class ConDemo {
public ConDemo() {
Console.WriteLine("Constructor is called.");
}
public void Demo() {
Console.WriteLine("Method is called.");
}
static void Main() {
ConDemo cd1 = new ConDemo();
ConDemo cd2 = new ConDemo();
ConDemo cd3 = cd2;
cd1.Demo(); cd2.Demo(); cd3.Demo();
Console.ReadLine();
}
}
Constructor of a class must be explicitly called for execution and we do that while creating the instance of
class as following:
If constructor is called then only memory allocation is performed, so instances of a class will have memory
allocation because they call the constructor explicitly whereas reference of class will not have memory allocation
because they do not call the constructor.
Constructors are defined implicitly or explicitly?
Ans: Either Or
53
Constructors can also be parameterized i.e. just like a method can be defined with parameters, constructor
can also be defined with parameters. If constructor is defined with parameters we call it as “Parameterized
Constructor” where as a constructor without any parameters is called as “Default/Parameter-less Constructor”.
Default constructors can be defined either explicitly or will be defined implicitly provided there is no explicit
constructor defined under that class, whereas implicit constructors will never be parameterized i.e. if a constructor
is parameterized then it is very sure that it is an explicit constructor.
Note: if Constructors of a class are parameterized then values to those parameters should be sent while creating
instance of that class.
To test Parameterized Constructors, add a new class in our project naming it as “ParamConDemo.cs” and
write the below code in it:
class ParamConDemo
{
public ParamConDemo(int i)
{
Console.WriteLine("Parameterized constructor is called: " + i);
}
static void Main()
{
ParamConDemo cd1 = new ParamConDemo(10);
ParamConDemo cd2 = new ParamConDemo(20);
ParamConDemo cd2 = new ParamConDemo(20);
Console.ReadLine();
}
}
Why to define a constructor explicitly in our class when there are implicit constructors?
Ans: We define constructors explicitly in our class for various reasons like:
1. Implicit constructors are parameter-less which will initialize fields of a class either with a default value of
that type or a fixed given value even if we create multiple instances of class, whereas if constructors are
defined explicitly (parameterized), then we get a chance of passing new values to the fields every time the
instance of class is created. To test this, add a new class under our project naming it as “Second.cs” and
write the below code in it:
class Second
{
public int x;
public Second(int x)
{
this.x = x;
}
}
Note: “this” is a keyword which refers to the class and by using this we can access non-static members of a class
from other non-static blocks when there is a naming conflict.
54
Earlier we have defined a class "First" with a public field “x” in it, and we have initialized it with a static value
“100”, and in the above class also we have a public field “x” which was initialized thru a Constructor, so in the 1st
case even if we create multiple instances of class First; under every instance the value of “x” will be “100” only
whereas in case of class Second for each instance of class we create we can pass a new value for initialization because
initialization is performed thru the constructor. To test that add a new class in our project naming it as
“TestClasses.cs” and write the below code in it:
class TestClasses {
static void Main() {
First f1 = new First();
First f2 = new First();
First f3 = new First();
Console.WriteLine(f1.x + " " + f2.x + " " + f3.x);
2. Every class requires some values for execution and the values that are required for a class to execute should
be passed to the class with the help of a constructor.
3. Just like parameters of a method will make a method dynamic, same as that parameters of constructor will
make the whole class dynamic.
Static Modifier
It is a keyword using which we can declare a class and its members as static i.e. if static keyword is prefixed
before a class or its members then they will become static or else by default every class and its member is non-static
only.
Members that require instance of a class for initialization and execution are known as non-static or instance
members, whereas members that doesn't require instance of the class for initialization and execution are known as
static members.
Non-Static Fields Vs Static Fields:
• If a field is explicitly declared by using static modifier it is a static field, whereas rest of every other field is
non-static only.
class Test
{
int x = 100; //Non-Static
55
static int y = 200; //Static
static void Main()
{
int z = 300; //Static
}
}
Note: variables declared under static blocks are also static.
• Static fields of a class are initialized immediately once the execution of that class starts whereas non-static
fields are initialized only after creating the instance of that class as well as each and every time a new
instance is created.
• In the life cycle of a class a static field gets initialized 1 & only 1 time whereas a non-static field gets initialized
for “0” times if no instances are created & “n” times if “n” instances are created.
• The initialization of non-static fields is associated with a constructor call, so the best place to initialize non-
static fields is a constructor.
Note: static fields can also be initialized thru constructor but still we never do that because, it's a single copy thru
out the life cycle of class and every new instance will override the old values.
Constant Fields: If a field is explicitly declared by using “const” keyword we call it as a constant field and those
constant field can't be modified once after their declaration, so it is must to initialize them at the time of declaration
only because they do not have a default value.
E.g.: const float pi = 3.14f;
The behavior of a constant field will be very similar to the behavior of a static field i.e. initialized immediately
once the execution of class starts maintaining a single copy thru-out the life cycle of a class and the only difference
between static and constant fields is static fields can be modified but not constant fields.
ReadOnly Fields: If a field is explicitly declared by using readonly keyword we call it as a readonly field and like
constant fields, readonly fields also can't be modified, but after their initialization i.e. it's not mandatory to initialize
readonly fields at the time of declaration because they can also be initialized after their declaration i.e. under a
constructor.
E.g.: readonly bool flag;
The behavior of readonly fields will be similar to the behavior of non-static fields i.e. they are initialized only
after creating the instance of class and maintain a separate copy for each instance that is created.
The only difference between non-static and readonly fields is non-static fields can be modified but not
readonly fields.
The difference between constant and readonly fields is constant is a single fixed value for the whole class
whereas readonly is a fixed value specific to each instance of class.
To test all the above add a new class in our project naming it as “Fields.cs” and write the below code in it:
class Fields {
int x;
static int y = 200;
const float pi = 3.14f;
readonly bool flag;
56
public Fields(int x, bool flag) {
this.x = x;
this.flag = flag;
}
static void Main() {
Console.WriteLine("Static field y is: " + y);
Console.WriteLine("Constant field pi is: " + pi);
y = 500; //Can be modified
//pi = 5.67f; //Can't be modified & error if un-commented
Console.WriteLine("Modified static field y is: " + y);
Console.WriteLine("--------------------------------------------------");
Fields s1 = new Fields(50, true);
Fields s2 = new Fields(150, false);
Console.WriteLine("Non-Static Fields: " + (s1.x + " " + s2.x));
Console.WriteLine("ReadOnly Fields: " + (s1.flag + " " + s2.flag));
s1.x = 100; s2.x = 300; //Can be modified
//s1.flag = false; s2.flag = true; //Can't be modified & Error if un-commented
Console.WriteLine("Modified Non-Static Fields: " + (s1.x + " " + s2.x));
Console.ReadLine();
}
}
Note: While accessing fields of a class from other classes use class name for accessing static and constant fields
whereas use instance of class for accessing non-static and readonly fields.
• Static field initializes immediately once the execution of class starts maintaining a single copy thru out the
life cycle of class and its value is modifiable.
• Constant field also initializes immediately once the execution of class starts maintaining a single copy thru
out the life cycle of class and its value is non-modifiable.
• Non-static field initializes only after creating the instance of class, as well as for each instance of the class
that is created, maintaining a separate copy for each instance and its value is modifiable.
• Readonly field also initializes only after creating the instance of class, as well as for each instance of the
class that is created, maintaining a separate copy for each instance and its value is non-modifiable.
Non-Static Methods Vs Static Methods:
If a method is explicitly declared by using static keyword, then it is a static method whereas rest of every
other method is non-static only.
While defining methods, if a method is non-static and if we want to consume any static members of class
in it, we can consume them directly where as if the method is static, we can consume non-static members of class
in that method only by using class instance.
57
Static Member => Non Static Block //Direct Access
Non Static Member => Non Static Block //Direct Access
Non Static Member => Static Block //Can be accessed only by using the class instance
using System;
namespace OOPSProject {
class Methods {
int x = 200;
static int y = 100;
public void Add() {
Console.WriteLine(x + y);
}
public static void Sub() {
Methods m = new Methods();
Console.WriteLine(m.x - y);
}
}
class TestMethods {
static void Main() {
Methods obj = new Methods();
obj.Add(); //Add is non-static so calling it with instance
Methods.Sub(); //Sub is static so calling it with class name
Console.ReadLine();
}
}
}
Non-Static Constructor Vs Static Constructor:
• A Constructor if explicitly declared by using static modifier is a static Constructor whereas rest of the other
are non-static only and till now every Constructor we defined is non-static only.
• Static Constructors are implicitly called whereas non-static Constructors must be explicitly called.
• As we are aware that Constructors are responsible for initializing fields in a class; Non-Static Constructor
will initialize Non-Static and Readonly Fields, whereas Static Constructor will initialize Static and Constant
fields.
• Static Constructor executes immediately once the execution of class starts and more over it is the first block
of code to execute in a class, whereas Non-Static Constructor gets executed only after creating the instance
of class as well as each and every time a new instance is created i.e. Static Constructor executes 1 and only
1 time in the life cycle of a class whereas Non-Static Constructor get executed for “0” times if no instances
are created and “n” times if “n” instances are created.
58
• Static Constructor can't be parameterized because they are implicitly called and more over it's the first
block of code to execute in a class, so we don't have any chance of sending values to its parameter's where
as parameterized Non-Static Constructors can be defined.
Note: We have already learnt earlier that, every class will contain an implicit constructor if not defined explicitly and
those implicit constructors are defined based on the following criteria:
1. Non-static constructor will be defined in every class except in a static class.
2. Static constructor will be defined only if the class contains any static fields.
class Test //Case 1
{
}
*After compilation there will be a non-static constructor in class.
class Test { //Case 2
int i = 10;
}
*After compilation there will be a non-static constructor in class.
class Test { //Case 3
static int i = 100;
}
*After compilation there will be both static and non-static constructors also.
static class Test //Case 4
{
}
*After compilation there will not be any constructor in class.
static class Test { //Case 5
static int i = 100;
}
*After compilation there will be a static constructor in class.
To test all the above add a new class in the project naming it as “Constructors.cs” and write the below code
in it:
class Constructors {
static Constructors() {
Console.WriteLine("Static constructor is called.");
}
Constructors() {
Console.WriteLine("Non-static constructor is called.");
}
static void Main() {
Console.WriteLine("Main method is called.");
Constructors c1 = new Constructors();
Constructors c2 = new Constructors();
Constructors c3 = new Constructors();
Console.ReadLine();
}
}
59
Static Class: These are introduced in C# 2.0. If a class is explicitly declared by using static modifier we call it as a static
class and this class can contain only static members in it. We can't create the instance of static class and more over
it is not required also.
Entity
Any living or non-living object which is associated with a set of attributes is known as an entity and
application development is all about dealing and managing these entities only. To develop an application we follow
the below process:
Step 1: Identify each and every entity that is associated with the application.
➢ School Application: Student, Teacher
➢ Banking Application: Customer, Employee
Step 4: Design an application by using any Programming Language of our choice which should act as an UI (User
Interface) between the End User and Database in managing the data present under DB by adopting following
guidelines:
1. Define a class where each class should represent an entity.
2. Define properties where each property should be a representation for each attribute.
3. Each instance of the class we create will be a unique representation for each entity.
Inheritance
It is a process of consuming members that are defined in one class from other classes by establishing
parent/child relation-ship between the classes, so that child class can consume members of its parent class as if they
are owner of those members.
60
Note: Child class even if it can consume members of its parent class as an owner, still it can't access any private
members of their parent.
class Class1 {
public Class1() {
Console.WriteLine("Class1 constructor is called.");
}
public void Test1() {
Console.WriteLine("Method 1");
}
public void Test2() {
Console.WriteLine("Method 2");
}
}
Now add another class in the project naming it as “Class2.cs” and write the below code in it:
61
c.Test1(); c.Test2(); c.Test3(); c.Test4();
Console.ReadLine();
}
}
Rules and regulations that has to be followed while working with Inheritance:
Rule 1: In inheritance parent class Constructor must be accessible to child class or else inheritance will not be
possible. The reason why parent's Constructor should be accessible to child is, because when ever child class instance
is created, control first jumps to child class Constructor and child class Constructor will in turn call its parent class
Constructor for execution and to test this, place a break point at child class’s Main method and debug the code by
hitting F11.
The reason why child Constructor calls its parent class Constructor is, because if child class wants to
consume members of its parent class, they must be initialized first and then only child classes can consume them
and we are already aware that members of a class are initialized by its own Constructor.
Note: Constructors are never inherited i.e. Constructors are specific to any class which can initialize members of that
particular class only but not of parent or child classes.
When we create the instance of any class, it will first read all its parent classes to gather the information of
members that are present under those classes, so in our previous case when the instance of Class2 is created it
gathers information of Class1 also as following:
Rule 2: In inheritance child class can access members of their parent class whereas parent classes can never access
members of their child class which are purely defined under the child class. To test this re-write the code under Main
method of child class i.e. Class2 as following:
Class1 p = new Class1();
p.Test1(); p.Test2(); //Valid
//p.Test3(); p.Test4(); //Invalid and in-accessible
Console.ReadLine();
Rule 3: Earlier we have learnt that variable of a class can be initialized by using instance of same class to make it as
a reference, for example:
Class2 c1 = new Class2();
Class2 c2 = c1;
Same as the above we can also initialize variables of parent class by using its child classes instance as
following:
Class2 c = new Class2();
Class1 p = c;
In this case both parent class reference and child class instance will be accessing the same memory, but
owner of that memory is child class instance.
62
In the above case even if parent class reference is initialized by the child class instance and consuming the
memory of child class instance, now also it is not possible to access the member's which are purely defined under
the child class and to test that rewrite the code under Main method of child class i.e. Class2 as following:
Class2 c = new Class2();
Class1 p = c;
p.Test1(); p.Test2(); //Valid
//p.Test3(); p.Test4(); //Invalid and in-accessible now also
Console.ReadLine();
Note: We can never initialize child class variables by using parent class instance either implicitly or explicitly also.
Class1 p = new Class1(); //Creating parent class instance
Class2 c = p; //Invalid (Implicit conversion and compile time error)
Class2 c = (Class2)p; //Invalid (Explicit conversion and runtime error)
We can initialize any child class variable by using a parent class reference which is initialized by using the
same child class instance by performing an explicit conversion.
Note: in the above case the new reference “obj” also start's accessing the same memory allocated for the instance
“c” and with the new reference we call the members of both “Class1” and “Class2” also.
63
Rule 4: Each and every class that is pre-defined or user-defined has a default parent class i.e. Object class of System
namespace. Object is the ultimate parent of all classes in .NET Framework class hierarchy providing low level services
to child classes. So every class by default contains 4 methods that are inherited from the “Object” Class and those
are “Equals”, “GetHashCode”, “GetType” and “ToString”, and these 4 methods can be called or consumed from any
class. To test this re-write code under Main method of child class (Class2) as following:
Rule 5: Both Java and .Net Language's doesn't provide the support for Multiple and Hybrid inheritances thru classes,
and what they support is Single, Multi-Level and Hierarchical inheritances because multiple inheritance suffers from
ambiguity problem, for example:
Note: CPP Language supports all 5 types of inheritances because it is the 1st Object Oriented Programming Language
that came into existence and at the time of its introduction, this problem was not anticipated.
Rule 6: In the first rule of inheritance we have discussed that whenever the instance of child class is created it will
implicitly call its parent class constructor for execution, but this implicit calling will take place only if parent classes
Constructor is “default or parameter less”, whereas if at all the parent classes Constructor is parameterized then
child class constructor can't implicitly call parent class Constructor for execution because it requires parameter
values for execution. To resolve the above problem developer needs to explicitly call parent classes Constructor from
child class constructor using "base" keyword and pass all the required parameter values.
To test the above re-write constructor of parent class i.e. Class1 as following:
public Class1(int i) {
Console.WriteLine("Class1 constructor is called: " + i);
64
}
Now when we run child class i.e. Class2, we get an error stating that there is no value sent to formal
parameter “i” of Class1 (Parent Class) and to resolve this problem re-write constructor of Class2 as following:
public Class2(int x) : base(x) {
Console.WriteLine("Class2 constructor is called.");
}
In the above case child classes constructor is also parameterized so while creating the instance of child class
we need to explicitly pass all the required values to its constructor and those values are first loaded into the
constructor and from there those values are passed to parent classes constructor thru the base keyword, and to test
this go to Main method of Class2, and re-write the code in it as following:
Class2 c = new Class2(50); Console.ReadLine();
How do we use inheritance in application development?
Ans: Inheritance is a process which comes into picture in the initial stages of an application development. As
discussed above, if we want to develop an Application we need to follow the below process:
65
}
public class Student : Person {
int Class; char Section; float Marks, Fees;
}
public class Staff : Person {
public double Salary; public string Designation;
}
public class Teaching : Staff {
string Subject, Qualification;
}
public class NonTeaching : Staff {
int MgrId; string Dname;
}
Polymorphism
Behaving in different ways depending upon the input received is known as Polymorphism i.e. when ever
input changes then automatically the output or behaviour also changes accordingly. This can be implemented in our
language in 3 different ways:
1. Overloading
2. Overriding
3. Hiding/Shadowing
Overloading: This is again of different types like Method Overloading, Operator Overloading, Constructor
Overloading, Indexer Overloading and Deconstructor Overloading.
Method Overloading: It is an approach of defining multiple methods in a class with the same name by changing their
parameters. Changing parameters means we can change any of the following:
Note: in overloading a return type change with out parameter change is not taken into consideration, for example:
public string Show() => Invalid
To test method overloading, add a new class in the project naming it as OverloadMethods.cs and write the following
code in it:
class OverloadMethods {
public void Show() {
Console.WriteLine(1);
66
}
public void Show(int i) {
Console.WriteLine(2);
}
public void Show(string s) {
Console.WriteLine(3);
}
public void Show(int i, string s) {
Console.WriteLine(4);
}
public void Show(string s, int i) {
Console.WriteLine(5);
}
static void Main() {
OverloadMethods obj = new OverloadMethods();
obj.Show(); obj.Show(10); obj.Show("Hello"); obj.Show(10, "Hello"); obj.Show("Hello", 10);
Console.ReadLine();
}
}
What is Method Overloading?
Ans: It's an approach of defining a method with multiple behaviours and those behaviours will vary based on the
number, type and order of parameters. For example IndexOf is an overloaded method under String class which
returns the index position of a characater or string based on input values of that method, for example:
string str = "Hello World";
str.IndexOf('o'); => 4 => Returns the first occurance of character
str.IndexOf('o', 5); => 7 => Returns the next occurance of character
* WriteLine method of Console class is also overloaded for printing any type of value that is passed as input to the
method, as following:
• WriteLine()
• WriteLine(int value)
• WriteLine(bool value)
• WriteLine(double value)
• WriteLine(string value)
• WriteLine(string format, params object[] values)
• +13 more overloads
Inheritance based overloading: It's an approach of overloading parent classes’ methods under the child class, and
to do this child class doesn't require taking any permission from the parent class, for example:
Class1
public void Test()
Class2 : Class1
public void Test(int i)
67
Method Overriding: it’s an approach of re-implementing parent classes’ methods under child class exactly with the
same name and signature.
Every virtual method of parent class can be overriden by child class, if required by using "override" modifier
as following: Class2 : Class1 => public override void Show() //Overriding
Note: overriding virtual methods of parent class under child class is not mandatory for child class.
In overriding, parent class defines a method in it as virtual and gives it to the child class for consumption,
so that it's giving a permission to the child class either to consume the method “as is” or override the method as per
it's requirement, if at all the original behaviour of that method is not satisfactory to the child class.
To test inheritance based method overloading and method overriding, add a new class in the project naming
it as LoadParent.cs and write the following code in it:
class LoadParent {
public void Test() {
Console.WriteLine("Parent Class Test Method Is Called.");
}
public virtual void Show() {
Console.WriteLine("Parent Class Show Method Is Called.");
}
public void Display() {
Console.WriteLine("Parent Class Display Method Is Called.");
}
}
Now add another class in the project naming it as LoadChild.cs and write the following code in it:
68
}
static void Main() {
LoadChild c = new LoadChild();
c.Test(); //Executes parent class Test method
c.Test(10); //Executes child class Test method
c.Show(); //Executes parent class Show method
c.Display(); //Executes parent class Display method
Console.ReadLine();
}
}
Inheritance-Based Overloading: In the above classes Test method of parent class has been overloaded in child class
and then by using child class instance we are able to call both parent and child classes methods also, from the child
class.
Method Overriding: In the above classes Show method of parent class is declared virtual which gives a chance for
child classes to override that method but the child class did not override the method, so a call to that method by
using child classes instance will invoke the parent classes Show method only and this proves us overriding is only
optional and to confirm that run the child class LoadChild and watch the output of Show method.
In this case if child class overrides the parent classes virtual method then a call to that method by using
child class instance will execute or invoke its own method but not of the parent classes, and to test that add a new
method in class LoadChild as following:
Now if we run the child class i.e. LoadChild and watch the output of Show method we will notice child
classes Show method getting executed in place of the parent classes Show method and this is what we call as
changing the behaviour.
Can we override any parent classes’ methods under child classes with out declaring them as virtual?
Ans: No.
Can we re-implement any parent classes’ methods under the child classes with out declaring them as virtual?
Ans: Yes.
We can re-implement a parent class method under the child class by using 2 different approaches:
• Overriding
• Hiding/Shadowing
Method Hiding/Shadowing: This is also an approach of re-implementing parent classes methods under child class
exactly with the same name and signature just like overriding but the difference between the 2 is; in overriding child
class can re-implement only virtual methods of parent class where as in-case of hiding/shadowing child class can re-
implement any method of the parent class i.e. even if the method is not declared as virtual also re-implementation
can be performed.
69
Class1 => public void Display()
In the above case using "new" keyword while re-implementing the method in child class is only optional
and if we don't use it, compiler gives a warning message at the time of compilation, telling that there is already a
method with the same name in parent class and your new method in child class will hide that old method, so by
using "new" keyword we are informing compiler that we are intentionally defining a new method with the same
name and signature under our child class.
Before testing hiding/shadowing first run the child class i.e. LoadChild and watch the output of Display
method and here we notice that parent classes Display method getting executed, now add a new method in the
child class LoadChild as following:
//Hiding/Shadowing parent's Display method in child
public new void Display() {
Console.WriteLine("Child Class Display Method Is Called.");
}
Now run the child class LoadChild again and watch the difference in output i.e. in this case child classes
Display() method is called in place of parent class Display() method.
In the above 2 classes we have performed the following:
LoadParent
public void Test()
public virtual void Show()
public void Display()
LoadChild : LoadParent
public void Test(int i) => Overloading
public override void Show() => Overriding
public new void Display() => Hiding/Shadowing
In case of Overriding and Hiding, after re-implementing the parent classes methods under child class,
instance of child class starts calling it's own methods but not of parent class, whereas if required there is still a chance
of calling those parent class methods from child class in 2 different ways:
1. By creating the parent classes instance under the child class we can call parent class methods from child
class and to test that re-write code under Main method of child class i.e. LoadChild as following:
LoadParent p = new LoadParent();
p.Show(); //Executes parent class Show method
p.Display(); //Executes parent class Display method
70
2. By using base keyword also we can call parent class methods from child class, but keywords like "this" and
"base" can't be used in static blocks.
To test this first add 2 new methods under the child class i.e. LoadChild as following:
In the above case Display is considered as pure child class member only because it's re-implemented by
child class with out taking any permission from parent, so parent will never recognize it.
In static or compile-time polymorphism, the decision which polymorphic method has to be executed for a
method call is performed at compile time. Method overloading is an example for this and here compiler identifies
which overloaded method it has to execute for a particular method call at the time of program compilation by
checking the type and number of parameters that are passed to the method and if no method matchs the method
call it will give an error.
71
In dynamic or run-time polymorphism, the decision which polymorphic method has to be executed for a
method call is made at runtime rather than compile time. Run-time polymorphism is achieved by method overriding
because method overriding allows us to have methods in the parent and child classes with the same name and the
same parameters. By runtime polymorphism, we can point to any child class by using the reference of the parent
class which is initialized by child class object, so the determination of the method to be executed is based on the
instance being referred to by reference.
Static Polymorphism Dynamic Polymorphism
1. Occurs at compile-time. 1. Occurs at runtime.
2. Achieved through static binding. 2. Achieved through dynamic binding.
3. Method overloading should exist. 3. Method overriding should exist.
4. Inheritance is not involved. 4. Inheritance is involved.
5. Happens in the same class. 5. Happens between parent-child classes.
6. Reference creation thru instance is not required. 6. Requires parent class reference creation thru child
class instance.
Operator Overloading
It's an approach of defining mutiple behaviours to an operator, which varies based on the operands
between which we use the operator. For example: "+" is an addition operator when used between numeric operands
and it is a concatenation operator when used between string operands.
Number + Number => Addition
String + String => Concatenation
The behaviour for an operator is pre-defined i.e. developers of the language have already implemented,
logic that has to be executed when an operator is used between 2 operands under the libraries of a language with
the help of a special method known as "Operator Method".
72
Note: same as the above we can also define these operator methods for using an operator between new types of
operands.
To test Operator Overloading, Method Overriding and Method Hiding/Shadowing add a new class
“Matrix.cs” under the project and write the following code:
class Matrix {
int a, b, c, d; //Declaring attributes for a 2 * 2 Matrix
public Matrix(int a, int b, int c, int d) {
//Initializing attributes of the Matrix
this.a = a; this.b = b; this.c = c; this.d = d;
}
//Overriding the ToString() method inherited from Object class to return values of the Matrix in 2 * 2 format
public override string ToString() {
return a + " " + b + "\n" + c + " " + d + "\n";
}
//Implementing the + operator so that it can be used between 2 Matrix operands
public static Matrix operator +(Matrix obj1, Matrix obj2) {
Matrix obj = new Matrix(obj1.a + obj2.a, obj1.b + obj2.b, obj1.c + obj2.c, obj1.d + obj2.d);
return obj;
}
//Implementing the - operator so that it can be used between 2 Matrix operands
public static Matrix operator -(Matrix obj1, Matrix obj2) {
Matrix obj = new Matrix(obj1.a - obj2.a, obj1.b - obj2.b, obj1.c - obj2.c, obj1.d - obj2.d);
return obj;
}
//Re-Implementing the == operator using Hiding/Shadowing so that it can be used between 2 Matrix's, to perform
values == comparision b’coz original implementation for the operator in Object class is reference == comparision.
public static bool operator ==(Matrix obj1, Matrix obj2) {
if (obj1.a == obj2.a && obj1.b == obj2.b && obj1.c == obj2.c && obj1.d == obj2.d) {
return true;
}
else {
return false;
}
}
//Re-Implementing the != operator using Hiding/Shadowing so that it can be used between 2 Matrix's, to perform
values != comparision b’coz original implementation for the operator in Object class is reference != comparision.
public static bool operator !=(Matrix obj1, Matrix obj2) {
if (obj1.a != obj2.a || obj1.b != obj2.b || obj1.c != obj2.c || obj1.d != obj2.d) {
return true;
}
else {
return false;
}
}
}
73
ToString is a method defined in parent class "Object" and by default that method returns "Name" of the
type to which an instance belongs when we call it on any type's instance.
ToString() method is declared as virtual under the class “Object” so any child class can override it as per
their requirements as we performed it in our Matrix class to change the behaviour of that method, so the new
method will return values that are associated with Matrix but not the type name.
The "==" and "!=" operators are also implemented in the parent class “Object”, but their original behaviour
is to perform a reference equal and reference not-equal comparision between type instances but not values equal
and values non-equal comparision. We can also change the behaviour of those operator methods by using the
concept of hiding (but not overriding because they are not declared as virtual) as we have done in our Matrix class,
so that now the 2 operators will perform values equal and values not-equal comparision in place of reference equal
and reference not-equal comparision.
To consume all the above add a new class TestMatrix.cs and write the following code:
class TestMatrix {
static void Main() {
//Creating 4 instances of Matrix class with different values
Matrix m1 = new Matrix(20, 19, 18, 17); Matrix m2 = new Matrix(15, 14, 13, 12);
Matrix m3 = new Matrix(10, 9, 8, 7); Matrix m4 = new Matrix(5, 4, 3, 2);
In the above case when we call WriteLine method by passing Matrix class instance as a parameter to it, will
internally invoke the overloaded WriteLine Method which takes "Object" as a parameter and that method will
internally call ToString() method on that instance, and because we have overwritten the ToString method in our
Matrix class, a call to it in WriteLine method will invoke Matrix classes ToString method which will return the values
that are associated with Matrix instance and prints them in a 2 * 2 Matrix format.
Constructor Overloading
Just like methods in a class can be overloaded, constructors in a class also can be overloaded and it is called
as constructor overloading. It's an approach of defining multiple constructors under a class and if constructors of a
74
class are overloaded then instance of that class can be created by using any available constructor i.e. it is not
mandatory to call any particular constructor for instance creation. To test this, add a new code file under the project
naming it as TestOverloadCons.cs and write the following code in it:
using System;
namespace OOPSProject {
class OverloadCons {
int i; bool b;
public OverloadCons() {
//Initializes both i & b with default values
}
public OverloadCons(int i) {
//Initializes i with given value and b with default value
this.i = i;
}
public OverloadCons(bool b) {
//Initializes i with default value and b with given value
this.b = b;
}
public OverloadCons(int i, bool b) {
//Initializes both i & b with given values
this.i = i; this.b = b;
}
public void Display() {
Console.WriteLine("Value of i is: {0} and value of b is: {1}", i, b);
}
}
class TestOverloadCons {
static void Main() {
OverloadCons c1 = new OverloadCons(); c1.Display();
OverloadCons c2 = new OverloadCons(10); c2.Display();
OverloadCons c3 = new OverloadCons(true); c3.Display();
OverloadCons c4 = new OverloadCons(10, true); c4.Display();
Console.ReadLine();
}
}
}
By overloading constructors in a class we get a chance to initialize fields of that class in 3 different ways:
1. With a default or “parameter-less” constructor defined in class we can initialize all fields of that class with
default values.
2. With a parameterized constructor defined in class we can initialize all fields of that class with given values.
3. With a parameterized constructor defined in class we can initialize some fields of that class with default
values and some fields with given values.
75
Note: If a class contains multiple attributes in it and if we want to initialize them in a “mix & match” combination
then we overload constructors, and the no. of constructors to be defined will be 2 power “n” where “n” is the no. of
attributes. In our above class we have 2 attributes so we have defined 4 constructors.
Copy Constructor
It is a constructor using which we can create a new instance of the class with the help of an existing instance
of the same class, which copies the attribute values from the existing instance into the new instance and the main
purpose of this constructor is to initialize a new instance with the values from an existing instance. The “Formal
Parameter Type” of a copy constructor will be the same “Class Type” in which it is defined.
To test Copy Constructors add a new class under the project naming it as CopyConDemo.cs and write the
following code:
class CopyConDemo {
int Custid; string Name; double Balance;
public CopyConDemo(int Custid) {
this.Custid = Custid; this.Name = "Scott"; this.Balance = 5000.00;
}
public CopyConDemo(CopyConDemo cd) //Copy Constructor {
this.Custid = cd.Custid; this.Name = cd.Name; this.Balance = cd.Balance;
}
public void Display() {
Console.WriteLine("Custid: {0}; Name: {1}; Balance: {2}", Custid, Name, Balance);
}
static void Main() {
CopyConDemo cd1 = new CopyConDemo(101);
CopyConDemo cd2 = new CopyConDemo(cd1); //Calling Copy Constructor
cd1.Display(); cd2.Display(); Console.WriteLine();
cd1.Balance = 10000.00; cd1.Display(); cd2.Display(); Console.WriteLine();
cd2.Balance = 20000.00; cd1.Display(); cd2.Display(); Console.ReadLine();
}
}
In the above case “cd2” is a new instance of the class which is created by copying the values from “cd1” and
here any changes that are performed on members of “cd1” will not reflect to members of “cd2” and vice versa
because they have their own individual memory which is not accessbile to others.
1. Default Constructor
2. Parameterized Constructor
3. Copy Constructor
4. Static Constructor
5. Private Constructor
76
Default Constructor: a constructor defined with out any parameters is known as a default constructor, which will
initialize fields of a class with default values. If a class is not defined with any explicit constructor then the class will
contain an implicit default constructor.
Parameterized Constructor: if a constructor is defined with atleast 1 parameter then we call it as parameterized
constructor and these constructors must be explicitly defined but never implicitly defined. Parameterized
constructors are used for initializing fields of a class with given set of values which we can pass while creating the
instance of that class.
Copy Constructor: it’s a constructor which takes the same type as its “Parameter” and initializes the fields of class
by copying values from an existing instance of the same class. A Copy constructors will not create a reference to the
class i.e. it will create a new instance for the class by allocating memory for all the members of that class and very
importantly any changes made on the source will not reflect to the new instance and vice-versa.
Static Constructor: if a constructor is defined explicitly by using static modifier we call it as a static constructor and
this constructor is the first block of code which executes under the class and they are responsible for initializing static
fields and more over these constructors can't be overloaded because they can't be parameterized. This constructor
is called implicitly before the first instance is created or any static members are referenced.
Private Constructor: If a constructor is explicitly declared by used private modifier we call it as a private constructor.
If a class contains only private constructors and no public constructors, other classes cannot create instances of that
class. The use of private constructor is to serve singleton classes where a singleton class is one which limits the
number of instances created to one.
Sealed Class: if a class is explicitly declared by using sealed modifier we call it as a sealed class and these classes can't
be inherited by other classes, for example:
In the above case Class1 is a sealed class so it can’t be inherited by any other class, for example:
class Class2 : Class1 => Invalid
Note: even if a sealed class can't be inherited it is still possible to consume the members of a sealed class by creating
its instance, for example String is a sealed class in our FCL, so we can't inherit from that class but we can still consume
it in all our classes by creating the instance of String class.
Sealed Method: If a parent class method can't be overriden under a child class, then we call that method as Sealed
Method. By default every method of a class is sealed because we can never override any method of parent class
under the child class unless the method is declared as virtual. If a method is declared as virtual under a class then
any child class of it in a linear hierarchy can override that method, for example:
Class1
public virtual void Show()
Class2 : Class1
public override void Show() //Valid
77
Class3 : Class2
public override void Show() //Valid
Note: in the above case even if Class2 is not overriding the method, then also Class3 can override the method.
When a child class is overriding any of its parent classes’ virtual methods, it can seal those methods by using
sealed modifier on them, so that furthur overriding of those methods can't be performed by its child classes, for
example:
Class1
public virtual void Show()
Class2 : Class1
public sealed override void Show() //Valid
Class3 : Class2
public override void Show() //Invalid
Note: in the above case Class2 has sealed the method while overriding the method so Class3 can’t override the
method.
Abstract Class and Abstract Method
Abstract Method: a method without any method body is known as abstract method i.e. an abstract method contains
only declaration without any implementation. To declare a method as abstract it is must to use “abstract” modifier
on that method explicitly.
Abstract Class: a class under which we declare abstract members is known as abstract class and must be declared
by using “abstract” modifier.
Note: each and every abstract member of an abstract class must be implemented by the child of abstract class
without fail (mandatory).
The concept of abstract method's is near similar to method overriding i.e. in case of overriding, if at all a
parent class contains any methods declared as virtual then child classes can re-implement those methods by using
override modifier whereas in case of abstract methods if at all a parent class contains any methods declared as
abstract then every child class must implement all those methods by using the same override modifier only.
78
An abstract class can contain both abstract and non-abstract (concrete) members also, and if at all any child
class of the abstract class wants to consume any non-abstract members of its parent, must first implement all the
abstract members of its parent.
Abstract Class:
• Non-Abstract/Concrete Members
• Abstract Members
Note: we can't create the instance of an abstract class, so abstract classes are never useful to themselves, i.e an
abstract class is always a parent providing services to their child classes.
To test an abstract class and abstract methods add a new class under the project naming it as
“AbsParent.cs” and write the following code in it:
abstract class AbsParent {
public void Add(int x, int y) {
Console.WriteLine(x + y);
}
public void Sub(int x, int y) {
Console.WriteLine(x - y);
}
public abstract void Mul(int x, int y);
public abstract void Div(int x, int y);
}
Now add another class “AbsChild.cs” to implement the above abstract classes - abstract methods and write
the following code in it:
79
}
static void Main() {
AbsChild c = new AbsChild();
c.Add(100, 23); c.Sub(78, 19); c.Mul(15, 18); c.Div(580, 24);
Console.ReadLine();
}
}
Note: even if the instance of an abstract class can't be created it is still possible to create the reference of an abstract
class by using it's child classes instance, and with that reference we can call each and every concrete method of
abstract class as well as its abstract methods which are implemented by child class, and to test this re-write code
under Main method of class “AbsChild” as following:
For example if we are designing a Mathematical Application then we follow the below process of implementation:
Step 3: Designing the database by following the rules we learnt in entity implementations.
Step 4: Develop an application and define classes representing each and every entity.
Note: while defining classes representing entities, as learnt in inheritance first we need to define a parent class with
all the common attributes as following:
80
In the above case, “Figure” is a Parent class containing all the common attributes between the 4 entities.
Now we want a method that returns Area of each and every figure, and even if the method is common for all the
classes, still we can't define it in the parent class Figure, because the formula to calculate area varies from figure to
figure. So to resolve the problem, without defining the method in parent class we need to declare it in the parent
class Figure as abstract and restrict each and every child class to implement logic for that method as per their
requirement as following:
In the above case because GetArea() method is declared as abstract, it is mandatory for all the child classes
to implement that method under them, but logic can be varying from each other whereas signature of the method
can't change and now all the child classes have to do the following:
1. Define a constructor to initialize the attributes that are required for that entity.
2. Implement GetArea() method and write logic for calculating the Area of that corresponding figure.
To test the above add a new “Code File” under project naming it as “TestFigures.cs” and write the following code:
using System;
namespace OOPSProject {
public abstract class Figure {
public double Width, Height, Radius;
public const float Pi = 3.14f;
public abstract double GetArea();
}
public class Cone : Figure {
public Cone(double Radius, double Height) {
this.Radius = Radius; this.Height = Height;
}
public override double GetArea() {
return Pi * Radius * (Radius + Math.Sqrt((Height * Height) + (Radius * Radius)));
}
}
public class Circle : Figure {
public Circle(double Radius) {
this.Radius = Radius;
}
public override double GetArea() {
return Pi * Radius * Radius;
}
}
public class Triangle : Figure {
public Triangle(double Base, double Height) {
81
this.Width = Base; this.Height = Height;
}
public override double GetArea() {
return 0.5 * Width * Height;
}
}
public class Rectangle : Figure {
public Rectangle(double Length, double Breadth) {
this.Width = Length; this.Height = Breadth;
}
public override double GetArea() {
return Width * Height;
}
}
class TestFigures {
static void Main() {
Cone cone = new Cone(12.54, 67.34);
Console.WriteLine("Area of Cone: " + cone.GetArea());
Circle circ = new Circle(45.29);
Console.WriteLine("Area of Circle: " + circ.GetArea());
Triangle trin = new Triangle(67.32, 56.98);
Console.WriteLine("Area of Triangle: " + trin.GetArea());
Rectangle rect = new Rectangle(29.45, 32.78);
Console.WriteLine("Area of Rectangle: " + rect.GetArea());
Console.ReadLine();
}
}
}
Interface
Interface is also a user-defined type like a class but can contain only “Abstract Members” in it and all those
abstract members should be implemented by a child class of the interface.
Just like a class can have another class as its parent, it can also have an interface as its parent but the main
difference is if a class is a parent we call it as inheriting whereas if an interface is a parent we call it as implementing.
If a class is inheriting from another class we call it as Implementation Inheritance whereas if a class is
implementing an interface we call it as Interface Inheritance. Implementation Inheritance provides re-usability
82
because by inheriting from a class we can consume members of a parent in child class whereas Interface Inheritance
doesn't provide any re-usability because in this case we need to implement abstract members of a parent in child
class, but not consume.
Note: we have already discussed in the 5th rule of inheritance that Java and .Net Languages doesn't support multiple
inheritance thru class, because of ambiguity problem i.e. a class can have 1 and only 1 immediate parent class to it;
but both in Java and .Net languages multiple inheritance is supported thru the interfaces i.e. a class can have more
than 1 interface as its immediate parent.
Adding an Interface under Project: Just like we have “Class Item Template” in “Add New Item” window to define a
class we are also provided with an “Interface Item Template” to define an Interface. To test working with interfaces
add 2 interfaces under the project naming them as IMath1.cs, IMath2.cs and write the following code:
interface IMath1 { interface IMath2 {
void Add(int a, int b); void Mul(int a, int b);
void Sub(int a, int b); void Div(int a, int b);
} }
To implement methods of both the above interfaces add a class under project naming it as ClsMath.cs and
write the following code:
83
Console.WriteLine(a / b);
}
static void Main() {
ClsMath obj = new ClsMath();
obj.Add(100, 34); obj.Sub(576, 287); obj.Mul(12, 38); obj.Div(456, 2);
Console.ReadLine();
}
}
Points to Ponder:
1. The implementation class can inherit from another class and also implement “n” no. of interfaces, but class
name must be first in the list followed by interface names.
E.g.: class ClsMath : Program, IMath1, IMath2
2. While declaring abstract members in an interface we don't require using “abstract” modifier on them and
in the same way while implementing those abstract members we don't require to use “override” modifier
also.
Just like we can't create instance of an abstract class, we can't create instance of an interface also; but here
also we can create a reference of interface by using it's child class instance and with that reference we can call all
the members of parent interface which are implemented in child class and to test this re-write code under Main
method of class “ClsMath” as following:
Whereas in Java and .Net Languages multiple inheritance is supported thru interfaces i.e. a class can have
any no. of interfaces as its parent, but still we don't come across any ambiguity problem because child class of an
interface is not consuming parent's members but implements them.
84
If we come across any situation as above we can implement the interface methods under class by using 2
different approaches:
1. Implement the method of both interfaces only for 1 time under the class and both interfaces will assume
the implemented method is of its only and in this case we can call the method directly by using class
instance.
2. We can also implement the method of both interfaces separately for each interface under the class by pre-
fixing interface name before method name and we call this as explicit implementation, but in this case we
need to call the method by using reference of interface that is created with the help of a child class instance.
To test the above add 2 new interfaces under the project naming them as Interface1.cs, Interface2.cs and
write the following code:
interface Interface1 { interface Interface2 {
void Test(); void Test();
void Show(); void Show();
} }
Now add a new class under the project naming it as ImplClass.cs for implementing both the above interfaces
and write the following code:
Structure
Structure is also a user-defined type like a class, which can contain members what a class can contain like
constructor, static constructor, constants, fields, methods, properties, indexers, operators and events.
85
Memory is allocated for its instances on Managed Heap Memory is allocated for its instances on Stack, so
so we get the advantage of Automatic Memory Automatic Memory Management is not available but
Management thru Garbage Collector. faster in access.
Recommended for representing entities with larger Recommended for representing entities with smaller
volumes of data. volumes of data.
All pre-defined reference types in our FCL like string All pre-defined value types in our FCL like int
(System.String) and object (System.Object) are defined (System.Int32), float (System.Single), bool
as classes. (System.Boolean), char (System.Char) and Guid
(System.Guid) are defined as structures.
"new" keyword is mandatory for creating the instance "new" keyword is optional for creating the instance and
and in this process we need to call any constructor that if "new" is not used it will call default constructor which
is available in the class. is defined implicitly, whereas it is still possible to use
"new" and call other constructors also.
Contains an implicit default constructor if no Contains an implicit default constructor every time.
constructor is defined explicitly.
We can declare fields and those fields can be initialized We can declare fields but those fields can't be initialized
at the time of declaration. at the time of declaration.
Fields can also be initialized thru a constructor as well Fields can only be initialized thru a constructor as well
as referring thru instance also we can initialize them. as referring thru instance also we can initialize them.
Constructor is mandatory for creating the instance Default constructor is mandatory for creating the
which can either be default or parameterized also. instance without using new keyword and apart from
that we can also define parameterized constructors.
Developers can define any constructor like default or Developers can define parameterized constructors only
parameterized also, or else implicit default constructor because there is always an implicit default constructor,
gets defined. and this default constructor is mandatory if at all we
want to create instance without using "new" keyword.
If defined with “0” constructors after compilation there If defined with “0” constructors after compilation here
will be “1” constructor and if defined with “n” also there will be “1” constructor whereas if defined
constructors after compilation there will be “n” with “n” constructors after compilation there will be “n
constructors only. + 1” constructors.
Supports both, implementation as well as interface Supports only interface inheritance but not
inheritances also i.e. a class can inherit from another implementation inheritance i.e. a structure can
class as well as implement an interface also. implement an interface but can never inherit from
another structure.
Syntax to define a structure:
[<modifiers>] struct <Name> {
-Define Members
}
Adding a Structure under Project: we are not provided with any Structure Item template in the add new item
window, like we have class and interface item templates, so we need to use code file item template to define a
structure under the project. Add a Code File under project, naming it as “MyStruct.cs” and write the following in it:
using System;
namespace OOPSProject {
86
struct MyStruct {
int x;
public MyStruct(int x) {
this.x = x;
}
public void Display() {
Console.WriteLine("Method in a structure: " + x);
}
static void Main() {
MyStruct m1 = new MyStruct(); m1.Display();
MyStruct m2; m2.x = 10; m2.Display();
MyStruct m3 = new MyStruct(20); m3.Display();
Console.ReadLine();
}
}
}
Consuming a Structure: we can consume a Structure and its members from another structure or a class also; but
only by creating its instance because structure doesn't support inheritance. To test this, add a new class under the
project naming it as “TestStruct.cs” and write the following code:
class TestStruct {
static void Main() {
MyStruct obj = new MyStruct(50);
obj.Display();
Console.ReadLine();
}
}
Extension Methods
It's a new feature added in CSharp 3.0 which allows adding new methods to existing types (class/structure)
without creating a new derived type or re-compiling or modifying the original type. Extension methods are a special
kind of static methods, but they are called or consumed as if they are non-static methods. There is no difference
between calling an extension method and methods that are directly defined in the type.
Rules for defining Extension Methods:
1. Extension methods should be defined in a static class only.
2. The first parameter of an extension method is the type name to which the method has to be bound with,
pre-fixed by “this” keyword and we call this parameter as binding parameter which will not be taken into
consideration while calling the method.
3. We can also define any additional parameters (formal parameters) to an extension method from 2nd place
of parameter list and all those parameters will be taken into consideration while calling the method.
Note: If an extension method is defined with “n” no. of parameters, while calling the method there will be
“n - 1” parameters only, because it excludes the binding parameter.
4. We can't bind an extension method with multiple types because it can have only 1 binding parameter.
5. Extension methods can't access private members in the original type, but can access other members.
6. If we create an extension method which has the same signature method inside the original type we are
extending, then the extension method will not be called.
87
To test Extension Methods add a new class naming it as ExtensionMethods.cs and write the following code:
static class ExtensionMethods {
public static void Test5(this Program p, int i) { //Adding extension method to user-defined class Program
Console.WriteLine("Extension Method into Program class: " + i);
}
public static void Show(this MyStruct m) { //Adding extension method to user-defined structure MyStruct
Console.WriteLine("Extension method into MyStruct structure.");
}
public static ulong Factorial(this UInt32 i) { //Adding extension method to pre-defined structure UInt32
if (i == 0 || i == 1) { return 1; }
if (i == 2) { return 2; }
else { return i * Factorial(i - 1); }
}
public static string ToPascal(this String OldStr) { //Adding extension method to pre-defined class (Sealed Class)
if (OldStr.Trim().Length > 0) {
string NewStr = null;
OldStr = OldStr.ToLower();
string[] sarr = OldStr.Split(' ');
foreach (string str in sarr) {
char[] carr = str.ToCharArray(); carr[0] = char.ToUpper(carr[0]);
if (NewStr == null) { NewStr = new String(carr); }
else { NewStr += " " + new String(carr); }
}
return NewStr;
}
return OldStr;
}
Now add a new class TestExtensionMethods.cs and write the following:
class TestExtensionMethods {
static void Main() {
Program p = new Program(); p.Test5(100);
MyStruct obj = new MyStruct(); obj.Show();
uint fact = 15; ulong result = fact.Factorial(); Console.WriteLine($"Factorial of {i} is: {result}");
string str = "hElLo hOw aRe yoU"; str = str.ToPascal(); Console.WritLine(str); Console.ReadLine();
}
}
A Solution also requires a name, which can be specified by us while opening a new project or else will take
name of the first project that is created under solution, if not specified. In our case solution name is OOPSProject
because our project name is OOPSProject. A solution can have projects of different .NET languages as well as can be
88
of different project templates also like Windows Application, Console Application, Class Library etc. but a project
cannot contain items of different .NET languages i.e. they must be specific to one language only.
To add a new project under our OOPSProject solution, right click on solution node in Solution Explorer and
select add “New Project” which opens the new project window, under it select language as Visual C#, template as
Console Application, name the project as “SecondProject” and click ok which adds the new project under the
OOPSProject solution.
By default the new project also comes with a class Program but under SecondProject namespace, now write
following code in its main method and execute:
Console.WriteLine(“Second Project under the solution.”);
Console.ReadLine();
To run the above class, first we need to set a property “StartUp Project”, because there are multiple
project's under the solution and VS by default runs first project of the Solution only i.e. OOPSProject under the
solution. To set the “StartUp Project” property and run classes under SecondProject open Solution Explorer, right
click on SecondProject, select “Set as StartUp Project” and then run the project.
Note: if the new project is added with new classes we need to again set “StartUp Object” property under Second
Projects property window, because each project has its own property window.
Saving Solution and Projects:
The application what we have created right now is saved physically on hard disk in the same hierarchy as
seen under Solution Explorer i.e. first a folder is created representing the solution and under that a separate folder
is created representing each project and under that items corresponding to that project gets saved as following:
Assembly files are what we carry on to client systems when the application has to be deployed there, so
they are referred as units of deployment. All FCL were installed on our machines in the form of assemblies only. The
assembly file of a project will be present under bin\Debug\netcoreapp3.1 folder of that project's folder.
ildasm: Intermediate Language Dis-Assembler. We use it to dis-assemble an Assembly file and view the contents of
it. To check it out, open Visual Studio Command Prompt, go to the location where the assembly files of the project
are present and use it as following:
89
ildasm <name of the assembly file>
E.g.: Open Visual Studio Command Prompt, go to the below location and try the following:
<drive>:\<our_folder>\OOPSProject\OOPSProject\bin\Debug\netcoreapp3.1> ildasm OOPSProject.exe
<drive>:\<our_folder>\OOPSProject\SecondProject\bin\Debug\netcoreapp3.1> ildasm SecondProject.exe
Q. Can we consume classes of a project from other classes of same project?
Ans: Yes, we can consume them directly because all those classes were under same project and will be considered
as a family.
To test this go to OOPSProject Solution, right click on the SecondProject we have newly added, select add
reference and add the reference of OOPSProject assembly from its physical location. Now add a new class under the
SecondProject naming it as Class1.cs and write the following code in it:
using OOPSProject;
class Class1 {
static void Main() {
Rectangle r = new Rectangle(12.34, 56.78); Console.WriteLine(r.GetArea());
Circle c = new Circle(34.56); Console.WriteLine(c.GetArea());
Cone cc = new Cone(12.67, 34.89); Console.WriteLine(cc.GetArea());
Triangle t = new Triangle(45.23, 98.42); Console.WriteLine(t.GetArea());
Console.ReadLine();
}
}
Assemblies and Namespaces:
An assembly is an output file which gets generated after compilation of a project and it is physical. The
name of an assembly file will be same as project name and can't be changed at all.
Project: Source Code Assembly: Compiled Code (IL Code)
A namespace is a logic container of types which are used for grouping types. By default every project has a
namespace and its name is same as the project name, but we can change namespace names as per our requirements
and more over a project can contain multiple namespaces in it also.
For Example: TestProject (Console Applicaion) when compiled generates an assembly with the name as
TestProject.exe, under it namespaces can be as following:
namespace NSP1 {
Class1
90
Class2
}
namespace NSP2 {
Class3
Class4
}
Whenever we want to consume a type which is defined under a project from other projects first we need
to add reference of the assembly corresponding to that project and then only we can consume types of that assembly
in the new project. While consuming types in the assembly for which reference is added we need to prefix type
names with their namespace or import the namespace and consume the types.
Access Specifier’s
These are a special kind of modifiers using which we can define the scope of a type and its members i.e.
who can access them and who cannot. C# supports 5 access specifiers in it, those are:
1. Private 2. Internal 3. Protected 4. Protected Internal 5. Public 6. Private Protected (C# 7.3)
Note: members that are defined in a type with any scope or specifier are always accessible with in the type,
restrictions comes into picture only when we try to access them outside of the type.
Private: members declared as private under a class or structure can be accessed only with-in the type in which they
are defined and more over their default scope is private only. Interfaces can't contain any private members and their
default scope is public. Types can’t be declared as private, so this applies only to members.
Protected: members declared as protected under a class can be accessed only from their child class i.e. non-child
classes can't consume them. Types can’t be declared as protected, so this applies only to members.
Internal: members and types that are declared as internal can be consumed only with in the project, both from a
child or non-child. The default scope for a type in C# is internal only.
Protected Internal: members declared as protected internal will have dual scope i.e. within the project they behave
as internal providing access to whole project and out-side the project they will change to protected and provide
access to their child classes. Types can’t be declared as protected internal, so this applies only to members.
Public: a type or member of a type if declared as public is global in scope which can be accessed from anywhere.
Private Protected (Introducted in C# 7.3 Version): members declared as private protected in a class are accessible
only from the child classes that are defined in the same project. Types can’t be declared as private protected, so this
applies only to members.
To test access specifiers open a new CSharp Project of type “Console App.”, name the project as
“AccessDemo1” and re-name the solution as “MySolution”. By default the project comes with a class Program; write
the following code in it making it as public.
public class Program { //Case 1 (Accessing members of a class from same class)
private void Test1_Private() {
Console.WriteLine("Private Method");
}
internal void Test2_Internal() {
Console.WriteLine("Internal Method");
91
}
protected void Test3_Proteced() {
Console.WriteLine("Protected Method");
}
protected internal void Test4_ProtecedInternal() {
Console.WriteLine("Protected Internal Method");
}
public void Test5_Public() {
Console.WriteLine("Public Method");
}
public void Test6_PrivateProtected() {
Console.WriteLine("Public Method");
}
static void Main(string[] args) {
Program p = new Program();
p.Test1_Private(); p.Test2_Internal(); p.Test3_Proteced();
p.Test4_ProtecedInternal(); p.Test5_Public(); p.Test6_PrivateProtected();
}
}
Now add a new class Two.cs under the project and write the following:
class Two : Program { //Case 2 (Accessing members of a class from a child class in the same project)
static void Main() {
Two obj = new Two();
obj.Test2_Internal(); obj.Test3_Protected(); obj.Test4_ProtectedInternal();
obj.Test5_Public(); obj.Test6_PrivateProtected(); Console.ReadLine();
}
}
Now add another new class Three.cs in the project and write the following:
class Three { //Case 3 (Accessing members of a class from a non-child class in the same project)
static void Main() {
Program p = new Program();
p.Test2_Internal(); p.Test4_ProtectedInternal(); p.Test5_Public(); Console.ReadLine();
}
}
Now Add a new “Console App” project under “MySolution”, name it as “AccessDemo2”, rename the default
file Program.cs as Four.cs so that class name also changes to Four, add a reference to AccessDemo1 assembly from
its physical location to the new project and write the following code in the class Four:
class Four : AccessDemo1.Program { //Case 4 (Accessing members of a class from a child class in another project)
static void Main() {
Four obj = new Four();
obj.Test3_Protected(); obj.Test4_ProtectedInternal(); obj.Test5_Public(); Console.ReadLine();
}
}
Now add a new class under AccessDemo2 project, naming it as Five.cs and write the following:
using AccessDemo1
92
class Five { //Case 5(Accessing members of a class from a non-child class in another project)
static void Main() {
Program p = new Program();
p.Test5_Public(); Console.ReadLine();
}
}
Private Internal Protected Private Protected Public
Cases Protected Internal
Case 1: Same Class - Same Project Yes Yes Yes Yes Yes Yes
Case 2: Child Class - Same Project No Yes Yes Yes Yes Yes
Case 3: Non-Child Class - Same Project No Yes No No Yes Yes
Case 4: Child Class - Another Project No No Yes No Yes Yes
Case 5: Non-Child Class - Another Project No No No No No Yes
Language Interoperability
As discussed earlier the code written in 1 .NET Language can be consumed from any other .NET Languages and
we call this as Language Interoperability.
• VB is not a case-sensitive language.
• VB does not have any curly braces; the end of a block is represented with a matching End Stmt.
• VB does not have any semi colon terminators each statement must be in a new line.
• The extension of source files will be “.vb”.
To test this, add a new “Console App” project under “MySolution” choosing the language as Visual Basic and
name the project as “AccessDemo3”. By default the project comes with a file “Module1.vb”, so open solution
explorer, delete that file under project and add a new class naming it as “TestCS.vb”. Now add the reference of
“AccessDemo1” assembly to the current project choosing it from its physical location and write the following code
under the Class TestCS:
Imports AccessDemo1
Public Class TestCS : Inherits Program
Shared Sub Main()
Dim obj As New TestCS() ‘Creating a instance of a class in VB.Net Language
obj.Test3_Protected()
obj.Test4_ProtectedInternal()
obj.Test5_Public()
Console.ReadLine()
End Sub
End Class
Note: to run the class set both Startup Project and Startup Object properties also.
Consuming VB.Net Code in CSharp: Now to test consuming VB.Net code in CSharp, add a new project under
“MySolution”, choosing the language as VB, project type as "Class Library" and name the project as “AccessDemo4”.
A Class Library is a collection of types that can be consumed but not executed. After compilation the extension of
project’s assembly will be “.dll”.
93
By default the project comes with a class Class1 within the file Class1.vb, write the below code in it:
Public Class Class1
Public Function SayHello(name As String) As String
Return "Hello " & name
End Function
Public Sub AddNums(x As Integer, y As Integer)
Console.WriteLine(x + y)
End Sub
Public Sub Math(a As Integer, b As Integer, ByRef c As Integer, ByRef d As Integer)
c=a+b
d=a*b
End Sub
End Class
Now to compile the project open solution explorer, right click on “AccessDemo4” project and select “Build”
option which compiles and generates an assembly “AccessDemo4.dll”.
Now add a new class under “AccessDemo2” project with the name “TestVB.cs”, add reference of
“AccessDemo4.dll” assembly from its physical location and write the following code under the class TestVB:
using AccessDemo4;
class TestVB {
static void Main() {
Class1 obj = new Class1(); obj.AddNums(100, 50);
int x = 0, y = 0; obj.Math(100, 25, ref x, ref y); Console.WriteLine(x + " " + y);
string str =obj.SayHello("Raju"); Console.WriteLine(str); Console.ReadLine();
}
}
Note: to run the class set both Startup Project and Startup Object properties also.
Q. How to restrict a class not to be accessible for any other class to consume?
Ans: This can be done by declaring all the class constructors as private.
Q. How to restrict a class not to be accessible for any other class to consume by creating its instance?
Ans: This can be done by declaring all the class constructors as protected.
Destructor or Finalizer
Destructors are used to destruct objects (instances) of classes. A destructor is also a special method just like
a constructor, whereas constructors are called when instance of a class is created and destructors are called when
instance of a class is destroyed. Both of them will have the same name i.e. the name of class in which they are
defined, but to differentiate between each other we prefix destructor with a tilde (~) operator. For Example:
class Test {
Test() {
//Constructor
94
}
~Test() {
//Destructor
}
Remarks:
• Destructors cannot be defined in structs. They are only used with classes.
• A destructor does not take modifiers or have parameters.
• A class can only have one destructor and cannot be inherited or overloaded.
• Destructors cannot be called. They are invoked automatically.
The programmer has no control over when the destructor is called because this is determined by the garbage
collector; garbage collector calls the destructor in any of the following cases:
1. Called in the end of a programs execution and destroys all instances that are associated with the program.
2. In the middle of a programs execution also the garbage collector checks for instances that are no longer
being used by the application. If it considers an instance is eligible for destruction, it calls the destructor
and reclaims the memory used to store the instance.
3. It is possible to force garbage collector by calling GC.Collect() method to check for un-used instances and
reclaim the memory used to store those instances.
Note: we can force the garbage collector to do clean up by calling the GC.Collect method, but in most cases this
should be avoided because it may create performance issues, i.e. when the garbage collector comes into picture for
reclaiming memory of un-used instances, it will suspend the execution of programs.
To test a destructor add a new class DestDemo.cs and write the following:
class DestDemo {
public DestDemo() {
Console.WriteLine("Instance is created.");
}
~DestDemo() {
Console.WriteLine("Instance is destroyed.");
}
static void Main()
{
DestDemo d1 = new DestDemo();
DestDemo d2 = new DestDemo();
DestDemo d3 = new DestDemo();
//d1 = null; d3 = null; GC.Collect(); (Write all the 3 statements in the same line with comments).
Console.ReadLine();
}
}
Execute the above program by using Ctrl + F5 and watch the output of program, first it will call Constructor
for 3 times because 3 instances are created and then waits at ReadLine() statement to execute; now press enter key
to finish the execution of ReadLine(), immediately destructor gets called for 3 times because it is the end of programs
execution, so all 3 instances associated with the program are destroyed. This proves that destructor is called in the
end of a programs execution.
95
Now un-comment the commented code in Main method of above program and re-execute the program
again by using Ctrl + F5 to watch the difference in output, in this case 2 instances are destroyed before execution of
ReadLine() because we have marked them as un-used by assigning “null” and called Garbage Collector explicitly and
the third instance is destroyed in end of programs execution.
Properties
A property is a member that provides a flexible mechanism to read, write, or compute the value of a private
field. Properties can be used as if they are public fields, but they are actually special methods called accessors.
Suppose a class is associated with any value and if we want to expose that value outside of the class, access to that
value can be given in 4 different ways:
I. By storing the value under a public field, access can be given to that value outside of the class, for Example:
public class Circle {
public double Radius = 12.34;
}
Now by creating the instance of above class we can get or set a value to the field as following:
class TestCircle {
static void Main() {
Circle c = new Circle();
double Radius = c.Radius; //Getting the old value of Radius
96
c.Radius = 56.78; //Setting a new value for Radius
}
}
Note: in this approach it will provide Read/Write access to the value i.e. anyone can get the old value of the field as
well as anyone can set with a new value for the field.
II. By storing the value under a private field also we can provide access to the value outside of the class by
defining a property on that field. The advantage in this approach is it can provide access to the value in 3
different ways:
I. Only get access (Read Only Property)
II. Only set access (Write Only Property)
III. Both get and set access (Read/Write Property)
•
A property is one or two code blocks, representing a get accessor and/or a set accessor.
•
The code block for the get accessor is executed when the property is read and the body of the get accessor
resembles that of a method. It must return a value of the property type. The get accessor resembles a value
returning method without any parameters.
• The code block for the set accessor is executed when the property is assigned with a new value. The set
accessor resembles a non value returning method with parameter, i.e. it uses an implicit parameter called
“value”, whose type is the same type of the property.
• A property without a set accessor is considered as read-only. A property without a get accessor is
considered as write-only. A property that has both accessors is considered as read-write.
Remarks:
• Properties can be marked as public, private, protected, internal, or protected internal. These access
modifiers define how users of the class can access the property. The get and set accessors for the same
property may have different access modifiers. For example, the get may be public to allow read-only access
from outside the class, and the set may be private or protected.
• A property may be declared as a static property by using the static keyword. This makes the property
available to callers at any time, even if no instance of the class exists.
• A property may be marked as a virtual property by using the virtual keyword, which enables derived classes
to override the property behavior by using the override keyword. A property overriding a virtual property
can also be sealed, specifying that for derived classes it is no longer virtual.
• A property can be declared as abstract by using the abstract keyword, which means that there is no
implementation in the class, and derived classes must write their own implementation.
To test properties first add a new code file Cities.cs and write the following code:
namespace OOPSProject {
public enum Cities { Bengaluru, Chennai, Delhi, Hyderabad, Kolkata, Mumbai }
}
Now add a new class Customer.cs and write the following code:
97
public class Customer {
int _Custid; bool _Status; string _Name, _State; double _Balance; Cities _City;
public Customer(int Custid) {
_Custid = Custid; _Status = false; _Name = "John"; _Balance = 5000.00; _City = 0; _State = "Karnataka";
Country = "India";
}
public int Custid { //Read Only Property
get { return _Custid; }
}
public bool Status { //Read/Write Property
get { return _Status; } set { _Status = value; }
}
public string Name { //Read/Write Property with condition in set
get { return _Name; }
set { if(_Status) { _Name = value; } }
}
public double Balance { //Read/Write Property with condition in get & set
get { if(_Status) { return _Balance; } return 0; }
set { if (_Status) { if(value >= 500) { _Balance = value; } } }
}
public Cities City { //Read/Write Enumerated Property
get { return _City; }
set { if (_Status) { _City = value; } }
}
public string State { //Read/Write property with different scope to each accessor (2.0)
get { return _State; }
protected set { if (_Status) { _State = value; } }
}
public string Country { //Auto-Implemented or Automatic Property (3.0)
get; private set;
}
public string Continent { get; } = "Asia"; //Auto-Property Initializer (6.0)
}
Note: The contextual keyword value is used in the set accessor in ordinary property declarations. It is similar to an
input parameter of a method. The word value references the value that client code is attempting to assign to the
property.
Enumerated Property:
It is a property that provides with a set of constants to choose from, for example BackgroundColor property
of the Console class that provides with a list of constant colors to choose from, under an enum ConsoleColor. E.g.:
Console.BackgroundColor = ConsoleColor.Blue;
An enum is a distinct type that consists of a set of named constants called the enumerator list. Usually it is
best to define an enum directly within a namespace so that all classes in the namespace can access it with equal
convenience. However, an enum can also be nested within a class or structure.
98
[<modifiers>] enum <Name> { <list of constant values> }
E.g.: public enum Days { Monday, Tuesday, Wednesday, Thursday, Friday }
By default, the first value is represented with an index 0, and the value of each successive enumerator is
increased by 1. For example, in the above enumeration, Monday is 0, Tuesday is 1, Wednesday is 2, and so forth.
To define an Enumerated Property adopt the following process:
Step 1: define an enum with the list of constants we want to provide for the property to choose.
E.g.: public enum Days { Monday, Tuesday, Wednesday, Thursday, Friday };
Step 2: declare a field of type enum on which we want to define a property.
E.g.: Days _Day = 0; or Days _Day = Days.Monday; or Days _Day = (Days)0;
Step 3: now define a property on the enum field for providing access to its values.
public Days Day {
get { return _Day; }
set { _Day = value; }
}
Auto-Implemented properties:
In C# 3.0 and later, auto-implemented properties make property-declaration more concise when no
additional logic is required in the property accessors. E.g.: Country property in our Customer class, but up to CSharp
5.0 it is important to remember that auto-implemented properties must contain both get and set blocks either with
the same access modifier or different also whereas from CSharp 6.0 it’s not mandatory because of a new feature
called “Auto Property Initializer”, which allows to initialize a property at declaration time. In our Customer class the
Country property we have defined can be implemented as following also:
E.g.: public string Country { get; } = "India";
To consume the properties we have defined above add a new class TestCustomer.cs and write the following:
class TestCustomer {
static void Main() {
Customer obj = new Customer(101);
Console.WriteLine("Custid: " + obj.Custid);
//obj.Custid = 102; //Can't be assigned to as property is read only
99
Console.WriteLine("Current City: " + obj.City);
obj.City = Cities.Hyderabad; Console.WriteLine("Modified City: " + obj.City);
using System;
namespace OOPSProject {
public class Student {
int? _Id, _Class; string _Name; float? _Marks, _Fees;
public int? Id { get { return _Id; } set { _Id = value; } }
public int? Class { get { return _Class; } set { _Class = value; } }
public string Name { get { return _Name; } set { _Name = value; } }
public float? Marks { get { return _Marks; } set { _Marks = value; } }
public float? Fees { get { return _Fees; } set { _Fees = value; } }
public override string ToString() {
return "Id: " + _Id + "\nName: " + _Name + "\nClass: " + _Class + "\nMarks: " + _Marks + "\nFees: " + _Fees;
}
}
class TestStudent {
static void Main() {
Student s1 = new Student { Id = 101, Name = "Raju", Class = 10, Marks = 550.00f, Fees = 5000.00f };
Student s2 = new Student { Id = 102, Name = "Venkat", Class = 10 };
Student s3 = new Student { Id = 103, Name = "Suresh", Marks = 575.00f };
Console.WriteLine(s1); Console.WriteLine(s2); Console.WriteLine(s3); Console.ReadLine();
}
}
}
Indexers
Indexers allow instances of a class or struct to be indexed just like arrays. Indexers resemble properties
except that their accessors take parameters. Indexers are syntactic conveniences that enable you to create a class
or struct that client applications can access just as an array. Defining an indexer allows you to create classes that act
100
like "virtual arrays." Instances of that class or structure can be accessed using the [] array access operator. Defining
an indexer in C# is similar to defining operator [] in C++, but is considerably more flexible. For classes or structure
that encapsulate array or collection - like functionality, defining an indexer allows the users of that class or structure
to use the array syntax to access the class or structure. An indexer doesn't have a specific name like a property it is
defined by using “this” keyword.
Indexers Overview:
• “this” keyword is used to define the indexers.
• The out and ref keyword are not allowed on parameters.
• A get accessor returns a value. A set accessor assigns a value.
• The value keyword is only used to define the value being assigned by the set indexer.
• Indexers do not have to be indexed by integer value; it is up to you how to define the look-up mechanism.
• Indexers can be overloaded.
• Indexers can't be defined as static.
• Indexers can have more than one formal parameter, for example, accessing a two-dimensional array.
To test indexers add a CodeFile under the project naming it as TestEmployee.cs and write the below code in it:
using System;
namespace OOPSProject {
public class Employee {
int _Eno; bool _Status; double _Salary; string _Name, _Job;
public Employee(int Eno) {
_Eno = Eno; _Status = true; _Name = "Scott"; _Job = "Manager"; _Salary = 25000.00;
}
public object this[int Index] {
get {
if (Index == 1) { return _Eno; }
else if (Index == 2) { return _Name; }
else if (Index == 3) { return _Job; }
else if (Index == 4) { return _Salary; }
else if (Index == 5) { return _Status; }
else { return null; }
}
set {
if (Index == 2) { _Name = (string)value; }
else if (Index == 3) { _Job = (string)value; }
else if (Index == 4) { _Salary = (double)value; }
else if (Index == 5) { _Status = (bool)value; }
}
}
101
public object this[char Index] {
get {
if (Char.ToUpper(Index) == 'A') { return _Eno; }
else if (Char.ToUpper(Index) == 'B') { return _Name; }
else if (Char.ToUpper(Index) == 'C') { return _Job; }
else if (Char.ToUpper(Index) == 'D') { return _Salary; }
else if (Char.ToUpper(Index) == 'E') { return _Status; }
else { return null; }
}
set {
if (Char.ToLower(Index) == 'b') { _Name = (string)value; }
else if (Char.ToLower(Index) == 'c') { _Job = (string)value; }
else if (Char.ToLower(Index) == 'd') { _Salary = (double)value; }
else if (Char.ToLower(Index) == 'e') { _Status = (bool)value; }
}
}
}
class TestEmployee {
static void Main() {
Employee Emp = new Employee(1001);
Console.WriteLine("Employee No: " + Emp[1]); Console.WriteLine("Employee Name: " + Emp[2]);
Console.WriteLine("Employee Job: " + Emp[3]); Console.WriteLine("Employee Salary: " + Emp[4]);
Console.WriteLine("Employee Status: " + Emp[5] + "\n");
Deconstructor
These are newly introduced in C# 7.0 which can also be used to provide access to the values or expose the
values associated with a class to the outside environment, apart from public fields, properties and indexers.
Deconstructor is a special method with the name “Deconstruct” that is defined under the class to expose (Read Only)
the attributes of a class and this will be defined with a code that is reverse to a constructor.
To understand deconstructors, add a code file in our project naming it as “TestTeacher.cs” and write the
below code in it:
using System;
namespace OOPSProject {
public class Teacher {
int Id;
string Name, Subject, Designation;
102
double Salary;
public Teacher(int Id, string Name, string Subject, string Designation, Double Salary) {
this.Id = Id;
this.Name = Name;
this.Subject = Subject;
this.Designation = Designation;
this.Salary = Salary;
}
public void Deconstruct(out int Id, out string Name, out string Subject, out string Designation, out double Salary)
{
Id = this.Id;
Name = this.Name;
Subject = this.Subject;
Designation = this.Designation;
Salary = this.Salary;
}
}
class TestTeacher {
static void Main() {
Teacher obj = new Teacher(1005, "Suresh", "English", "Lecturer", 25000.00);
(int Id, string Name, string Subject, string Designation, double Salary) = obj;
Console.WriteLine("Teacher Id: " +Id);
Console.WriteLine("Teacher Name: " + Name);
Console.WriteLine("Teacher Subject: " + Subject);
Console.WriteLine("Teacher Designation: " + Designation);
Console.WriteLine("Teacher Salary: " + Salary);
Console.ReadLine();
}
}
}
In the above case “Deconstruct” (name cannot be changed) is a special method which will expose the
attributes of teacher class. We can capture the values exposed by “Deconstructors” by using tuples, through the
instance of class we have created.
E.g: (int Id, string Name, string Subject, string Designation, double Salary) = obj;
The above statement can be implemented as following also:
E.g: (var Id, var Name, var Subject, var Designation, var Salary) = obj;
The above statement can be implemented as following also:
E.g: var (Id, Name, Subject, Designation, Salary) = obj;
Note: Deconstructors will provide read-only access to the attributes of a class.
We can also overload Deconstructors to access particular values from the list of attributes and to test that
add the following deconstructor in the Teacher class.
public void Deconstruct(out int Id, out string Name, out double Salary) {
Id = _Id; Name = _Name; Salary = _Salary;
103
}
Now we can capture only those 3 values and to test that write the following code in the “Main()” method
of “TestTeacher” class just above the “ReadLine()” method.
Without OverLoading the Deconstructors also we can access required attribute values by just putting "_" at
the place whose values we don't want to access, and to test this Add the following code in the “Main()” method of
“TestTeacher” class just above the “ReadLine()” method.
Errors which occur in a program due to syntactical mistakes at the time of program compilation are known
as compile time errors and these are not considered to be dangerous.
Errors which occur in a program while the execution of a program is taking place are known as runtime
errors, which can occur due to various reasons like wrong implementation of logic, wrong input supplied to the
program, missing of required resources etc. Runtime errors are dangerous because when they occur under the
program, the program terminates abnormally at the same line where the error got occurred without executing the
next lines of code. To test this, add a new class naming it as ExceptionDemo.cs and write the following code:
class ExceptionDemo {
static void Main() {
Console.Write("Enter 1st number: " ); int x = int.Parse(Console.ReadLine());
Console.Write("Enter 2nd number: " ); int y = int.Parse(Console.ReadLine());
int z = x / y; Console.WriteLine("The result of division is: " + z); Console.WriteLine("End of the Program.");
}
104
}
Execute the above program by using Ctrl + F5, and here there are chances of getting few runtime errors
under the program, to check them enter the value for y as ‘0’ or enter character input for x or y values, and in both
cases when an error got occurred program gets terminated abnormal on the same line where error got occurred.
Exception: In C#, errors in the program at run time are caused through the program by using a mechanism called
Exceptions. Exceptions are classes derived from class Exception of System namespace. Exceptions can be thrown by
the .NET Framework CLR (Common Language Runtime) when basic operations fail or by code in a program. Throwing
an exception involves creating an instance of an Exception-derived class, and then throwing that instance by using
the throw keyword. There are so many Exception classes under the Framework Class Library where each class is
defined representing a different type of error that occurs under the program, for example: FormatException,
NullReferenceException, IndexOutOfRangeException, ArithmeticException etc.
Exceptions are basically 2 types like SystemExceptions and ApplicationExceptions. System Exceptions are
pre-defined exceptions that are fatal errors which occur on some pre-defined error conditions like DivideByZero,
FormatException, NullReferenceException etc. ApplicationExceptions are non-fatal errors i.e. these are error that
are caused by the programs explicitly. Whatever the exception it is every class is a sub class of class Exception only
and the hierarchy of these exception classes will be as following:
➢ Exception
➢ SystemException
➢ FormatException
➢ NullReferenceException
➢ IndexOutOfRangeException
➢ ArithmeticException
➢ DivideByZeroException
➢ OverflowException
➢ ApplicationException
Exception Handling: It is a process of stopping the abnormal termination of a program whenever a runtime error
occurs under the program; if exceptions are handled under the program we will be having the following benefits:
1. As abnormal termination is stopped, statements that are not related with the error can be still executed.
2. We can also take any corrective actions which can resolve the problems that may occur due to the errors.
3. We can display user friendly error messages to end users in place of pre-defined error messages.
How to handle an Exception: to handle an exception we need to enclose the code of the program under some
special blocks known as try and catch blocks which should be used as following:
try {
-Statement’s where there is a chance of getting runtime errors.
-Statement’s which should not execute when the error occurs.
}
catch(<Exception Class Name> [<Variable>]) {
-Statement’s which should execute only when the error occurs.
}
[---<multiple catch blocks if required>---]
105
To test handling exceptions add a new class TryCatchDemo.cs and write the following code:
class TryCatchDemo {
static void Main() {
try {
Console.Write("Enter 1st number: " ); int x = int.Parse(Console.ReadLine();
Console.Write("Enter 2nd number: " ); int y = int.Parse(Console.ReadLine());
int z = x / y; Console.WriteLine(“The result of division is: “ + z);
}
catch(DivideByZeroException) { Console.WriteLine("Divisor value should not be zero."); }
catch(FormatException) { Console.WriteLine("Input values must be numeric."); }
catch(Exception ex) { Console.WriteLine(ex.Message); }
Console.WriteLine("End of the program.");
}
}
If we enclose the code under try and catch blocks the execution of program will take place as following:
• If all the statements under try block are successfully executed (i.e. no error in the program), from the last
statement of try the control directly jumps to the first statement which is present after all the catch blocks.
• If any statement under try causes an error from that line, without executing any other lines of code in try,
control directly jumps to catch blocks searching for a catch block to handle the error:
• If a catch block is available that can handle the exception then exceptions are caught by that catch
block, executes the code inside of that catch block and from there again jumps to the first
statement which is present after all the catch blocks.
• If a catch block is not available to handle that exception which got occurred, abnormal termination
takes place again on that line.
Note: Message is a property under the Exception class which gets the error message associated with the exception
that got occurred under the program, this property was defined as virtual under the class Exception and overridden
under all the child classes of class Exception as per their requirement, that is the reason why when we call
“ex.Message” under the last catch block, even if “ex” is the reference of parent class, it will get the error message
that is associated with the child exception class but not of itself because we have already learnt in overriding that
“parent’s reference which is created by using child classes instance will call child classes overridden members” i.e.
nothing but dynamic polymorphism.
Finally Block: this is another block of code that can be paired with try along with catch or without catch also and the
specialty of this block is code written under this block gets executed at any cost i.e. when an exception got occurred
under the program or an exception did not occur under the program. All statements under try gets executed only
when there is no exception under the program and statements under catch block will be executed only when there
is exception under the program whereas code under finally block gets executed in both the cases. To test finally
block add a new class FinallyDemo.cs and write the following code:
class FinallyDemo {
static void Main() {
try {
Console.Write("Enter 1st number: " ); int x = int.Parse(Console.ReadLine());
Console.Write("Enter 2nd number: " ); int y = int.Parse(Console.ReadLine());
106
if (y == 1) { return; } int z = x / y; Console.WriteLine(“The result of division is: “ + z);
}
catch(Exception ex) { Console.WriteLine(ex.Message); }
finally { Console.WriteLine("Finally block executed."); }
Console.WriteLine("End of the program.");
} //End of the method
}
Execute the above program for 2 times, first time by giving input which doesn’t cause any error and second
time by giving the input which causes an error and check the output where in both the cases finally block is executed.
In both the cases not only finally block along with it “End of the program.” statement also gets executed,
now test the program for the third time by giving the divisor value i.e. value to y as 1, so that the if condition in the
try block gets satisfied and return statement gets executed. As we are aware that return is a jump statement which
jumps out of the method in execution, but in this case it will jump out only after executing the finally block of the
method because once the control enters into try we cannot stop the execution of finally block.
Note: try, catch and finally blocks can be used in 3 different combinations like:
I. try and catch: in this case exceptions that occur in the program are caught by the catch block so abnormal
termination will not take place.
II. try, catch and finally: in this case behavior will be same as above but along with it finally block keeps
executing in any situation.
III. try and finally: in this case exceptions that occur in the program are not caught because there is no catch
block so abnormal termination will take place but still the code under finally block gets executed.
Application Exceptions: these are non-fatal application errors i.e. these are errors that are caused by the programs
explicitly. Application exceptions are generally raised by programmers under their programs basing on their own
error conditions, for example in a division program we don’t want the divisor value to be an odd number. If a
programmer wants to raise an exception explicitly under his program he needs to do 2 things under the program.
1. Create the instance of any exception class.
2. Throw that instance by using throw statement. E.g.: throw <instance of exception class>
While creating an Exception class instance to throw explicitly we are provided with different options in choosing
which exception class instance has to be created to throw, like:
1. We can create instance of a pre-defined class i.e. ApplicationException by passing the error message that
has to be displayed when the error got occurred as a parameter to the class constructor and then throw
that instance.
E.g. ApplicationException ex = new ApplicationException (“<error message>”);
throw ex;
or
throw new ApplicationException (“<error message>”);
2. We can also define our own exception class, create instance of that class and throw it when required. If we
want to define a new exception class we need to follow the below process:
I. Define a new class inheriting from any pre-defined Exception class (but ApplicationException is
preferred choice as we are dealing with application exceptions) so that the new class also is an
exception.
II. Override the Message property inherited from parent by providing the required error message.
107
To test this first add a new class under the project naming it DivideByOddNoException.cs and write the following:
public class DivideByOddNoException : ApplicationException {
public override string Message {
get { return "Attempted to divide by odd number."; }
}
}
Now add a new class ThrowDemo.cs and write the following code:
class ThrowDemo {
static void Main() {
Console.Write("Enter 1st number: " ); int x = int.Parse(Console.ReadLine());
Console.Write("Enter 2nd number: " ); int y = int.Parse(Console.ReadLine());
if(y % 2 > 0) {
throw new ApplicationException(“Divisor value should not be an odd number.”);
//throw new DivideByOddNoException();
}
int z = x / y; Console.WriteLine(“The result of division is: “ + z);
Console.WriteLine("End of the program.");
}
}
Test the above program for the first time by giving the divisor value as an odd number and now
ApplicationException will raise and displays the error message “Divisor value should not be an odd number.”. Now
comment the first throw statement and uncomment the second throw statement so that when the divisor value is
an odd number DivideByOddNoException will raise and displays the error message “Attempted to divide by odd
number.”.
Delegates
Delegate is a type which holds the method(s) reference in an object. It is also referred to as a type safe
function pointer. Delegates are roughly similar to function pointers in C++; however, delegates are type-safe and
secure. A delegate instance can encapsulate a static or a non-static method also and call that method for execution.
Effective use of a delegate improves the performance of applications.
Defining a Delegate:
[<modifiers>] delegate void|<type> Name(<Parameter List>)
108
Note: while defining a delegate you should follow the same signature of the method i.e. parameters of delegate
should be same as the parameters of method and return types of delegate should be same as the return types of
method we want to call by using the delegate.
Instantiate the delegate: In this process we create the instance of delegate and bind the method with delegate.
AddDel ad = new AddDel(AddNums); or AddDel ad = AddNums;
SayDel sd = new SayDel(SayHello); or SayDel sd = SayHello;
Calling the delegate: Call the delegate by passing required parameter values, so that the method which is bound
with delegate gets executed.
ad(100, 50); string str = sd(“Raju”);
Where to define a delegate: Delegates can be defined either with in a class or with in a namespace also just like we
define other types.
Add a code file under the project naming it as Delegates.cs and write the following code:
namespace OOPSProject {
public delegate void MathDelegate(int x, int y);
public delegate string WishDelegate(string name);
public delegate void CalculatorDelegate(int a, int b, int c);
}
Add a new class DelDemo1.cs under the project and write the following code:
class DelDemo1 {
public void AddNums(int x, int y, int z) {
Console.WriteLine(x + y + z);
}
public static string SayHello(string name) {
return "Hello " + name + “, good morning.”;
}
static void Main() {
DelDemo1 obj = new DelDemo1();
CalculatorDelegate cd = obj.AddNums; (or) CalculatorDelegate cd = new CalculatorDelegate(obj.AddNums);
cd(100, 50, 25); cd(123, 456, 789); cd(396, 224, 156);
109
}
}
Multicast Delegate: It is a delegate which holds the reference of more than one method. Multicast delegates must
contain only methods that return void. If we want to call multiple methods using a single delegate all the methods
should have the same Parameter types. To test this, add a new class DelDemo2.cs under the project and write the
following code:
class DelDemo2 {
public void Add(int x, int y) {
Console.WriteLine("Add: " + (x + y));
}
public void Sub(int x, int y) {
Console.WriteLine("Sub: " + (x - y));
}
public void Mul(int x, int y) {
Console.WriteLine("Mul: " + (x * y));
}
public void Div(int x, int y) {
Console.WriteLine("Div: " + (x / y));
}
static void Main() {
DelDemo2 obj = new DelDemo2();
MathDelegate md = obj.Add; md += obj.Sub; md += obj.Mul; md += obj.Div;
md(100, 50); Console.WriteLine(); md(575, 25); Console.WriteLine(); md -= obj.Mul; md(678, 28);
Console.ReadLine();
}
}
Anonymous Methods (Introduced in CSharp 2.0):
In versions of C# before 2.0, the only way to instantiate a delegate was to use named methods. C# 2.0
introduced anonymous methods which provide a technique to pass a code block as a delegate parameter.
Anonymous methods are basically methods without a name. An anonymous method is inline unnamed method in
the code. It is created using the delegate keyword and doesn’t require modifiers, name and return type. Hence we
can say an anonymous method has only body without name, return type and optional parameters. An anonymous
method behaves like a regular method and allows us to write inline code in place of explicitly named methods. To
test this, add a new class DelDemo3.cs and write the following code:
class DelDemo3 {
static void Main() {
WishDelegate wd = delegate(string name) {
return "Hello Mr./Ms./Mrs. " + name + " have a nice day.";
};
CalculatorDelegate cd = delegate(int a, int b, int c) {
Console.WriteLine(a * b * c);
};
Console.WriteLine(wd("Raju")); Console.WriteLine(wd("Naresh")); Console.WriteLine(wd("Praveen"));
cd(10, 20, 30); cd(40, 50, 60); cd(70, 80, 90);
110
Console.ReadLine();
}
}
Lambda Expression (Introduced in CSharp 3.0):
While Anonymous Methods were a new feature in 2.0, Lambda Expressions are simply an improvement to
syntax when using Anonymous method. Lambda Operator “=>” was introduced so that there is no longer a need to
use the delegate keyword, or provide the type of the parameter. The type can usually be inferred by compiler from
usage. To test this, add a new class DelDemo4.cs and write the following code:
class DelDemo4 {
static void Main() {
WishDelegate wd = name => {
return "Hello Mr./Ms./Mrs. " + name + " have a nice day.";
};
Console.WriteLine(wd("Raju")); Console.WriteLine(wd("Naresh")); Console.WriteLine(wd("Praveen"));
111
Method C# 6
Read-only property C# 6
Property C# 7.0
Constructor C# 7.0
Finalizer C# 7.0
Indexer C# 7.0
For example, below is a class defined with out using expression bodied members:
public class Circle {
double _Radius; const float _Pi = 3.14f;
public Circle(double Radius) {
_Radius = Radius;
}
~Circle() {
Console.WriteLine("Instance is destroyed.");
}
public float Pi {
get { return _Pi; }
}
public double Radius {
get { return _Radius; } set { _Radius = value; }
}
public double GetArea() {
return _Pi * _Radius * _Radius;
}
public double GetPerimeter() {
return 2 * _Pi * _Radius;
}
}
Above class cen be defined as following by using expression bodied members:
public class Circle {
double _Radius;
const float _Pi = 3.14f;
public Circle(double Radius) => _Radius = Radius; //C# 7.0
~Circle() => Console.WriteLine("Instance is destroyed."); //C# 7.0
public float Pi => _Pi; //C# 6.0
public double Radius //C# 7.0
{
get => _Radius;
set => _Radius = value;
}
public double GetArea() => _Pi * _Radius * _Radius; //C# 6.0
public double GetPerimeter() => 2 * _Pi * _Radius; //C# 6.0
}
Anonymous Types (Introduced in C# 3.0):
112
Anonymous type, as the name suggests, is a type that doesn't have any name. C# allows you to create an
instance with the new keyword without defining a class. The implicitly typed variable - “var” or “dynamic” is used to
hold the reference of anonymous types.
var EmpType = new { Id = 1001, Name = "Raju", Job = "Manager", Salary = 25000.00, Status = true };
In the above example, “EmpType” is an instance of the anonymous type which is created by using the new
keyword and object initializer syntax. It includes 5 properties of different data types. An anonymous type is a
temporary data type that is inferred based on the data that you include in an object initializer. Properties of
anonymous types will be read-only properties so you cannot change their values.
Notice that the compiler applies the appropriate type to each property based on the value assigned. For
example Id is of integer type, Name and Job are of string type, Salary is of double type and Status is of boolean type.
Internally, the compiler automatically generates the new type for anonymous types. You can check that by calling
GetType method on an anonymous type instance which will return the following value:
<>f__AnonymousType0`5[System.Int32,System.String,System.String,System.Double,System.Boolean]
Remember that Anonymous Types are derived from the Object class and it is a sealed class and all the
properties are created as read only properties. An anonymous type will always be local to the method where it is
defined. Usually, you cannot pass an anonymous type to another method; however, you can pass it to a method that
accepts a parameter of dynamic type. Anonymous types can be nested i.e. an anonymous type can have another
anonymous type as a property.
Points to Remember:
• Anonymous type can be defined using the new keyword and object initializer syntax.
• The implicitly typed variable - “var” or “dynamic” keyword, is used to hold an anonymous type.
• Anonymous type is a reference type and all the properties are read-only.
• The scope of an anonymous type is local to the method where it is defined.
To test anonymous types add a new Code File under the project naming it as “TestAnonymousTypes.cs”
and write the following code in it:
using System;
namespace OOPSProject {
class Printer {
public static void Print(dynamic d) {
Console.WriteLine("Employee No: " + d.Eno);
Console.WriteLine("Employee Name: " + d.Name);
Console.WriteLine("Employee Job: " + d.Job);
Console.WriteLine("Employee Salary: " + d.Salary);
Console.WriteLine("Employee Status: " + d.Status);
Console.WriteLine("Department No: " + d.Dept.Did);
Console.WriteLine("Department Name: " + d.Dept.Dname);
Console.WriteLine("Department Location: " + d.Dept.Location);
}
}
class TestAnonymousTypes {
static void Main() {
113
var EmpType = new { Eno = 1005, Name = "Scott", Job = "Manager", Salary = 25000.00, Status = true,
Dept = new { Did = 30, Dname = "Sales", Location = "Hyderabad" } };
Printer.Print(EmpType); Console.ReadLine();
}
}
}
Partial Types/Partial Classes (Introduced in CSharp 2.0):
It is possible to split the definition of a class or struct or an interface over two or more source files. Each
source file contains a section of the type definition, and all parts are combined when the application is compiled.
There are several situations when splitting a class definition is desirable like:
• When working on large projects, spreading a type over separate files enables multiple programmers to work
on it at the same time.
• Visual Studio uses these partial classes for auto generation of source code in the development of Windows
Forms Applications, Web Applications, and Web Services and so on.
Points to Remember:
• The partial keyword indicates that other parts of the class, struct, or interface can be defined in the
namespace.
• All the parts must use the partial keyword.
• All the parts must be available at compile time to form the final type.
• All the parts must have the same accessibility, such as public or internal.
• If any part is declared abstract, then the whole type is considered abstract.
• If any part is declared sealed, then the whole type is considered sealed.
• If any part declares a base type, then the whole type inherits that class.
• Parts can specify different base interfaces, and the final type implements all the interfaces listed by all the
partial declarations.
• Any class, struct, or interface members declared in a partial definition are available to all the other
parts.
• The final type is the combination of all the parts at compile time.
• The partial modifier is not available on delegate or enumeration declarations.
To test partial classes add 2 new code files under the project Part1.cs and Part2.cs and write the following code:
using System;
namespace OOPSProject {
partial class Parts {
public void Method1() {
Console.WriteLine("Method 1");
}
114
public void Method2() {
Console.WriteLine("Method 2");
}
}
}
using System;
namespace OOPSProject {
partial class Parts {
public void Method3() {
Console.WriteLine("Method 3");
}
public void Method4() {
Console.WriteLine("Method 4");
}
}
}
Now to test the above partial class, add a new class TestParts.cs under the project and write the following code:
class TestParts {
static void Main() {
Parts p = new Parts();
p.Method1(); p.Method2(); p.Method3(); p.Method4(); Console.ReadLine();
}
}
Partial Methods (Introduced in C# 3.0):
A partial class or struct may contain a partial method. One part of the type contains the signature of the
method and an optional implementation may be defined in the same part or another part. If the implementation is
not supplied, then the method and all calls to the method are removed at compile time.
Partial methods enable the implementer of one part of a type to define a method and the implementer of
the other part of the type can decide whether to implement the method or not. If the method is not implemented,
then the compiler removes the method signature and all calls to the method. The calls to the method, including any
results that would occur from evaluation of arguments in the calls, have no effect at run time. Therefore, any code
in the partial type can freely use a partial method, even if the implementation is not supplied, no compile-time or
run-time errors will result if the method is called but not implemented.
Partial methods are especially useful as a way to customize generated code. They allow for a method name
and signature to be reserved, so that generated code can call the method but the developer can decide whether to
implement the method or not.
A partial method consists of two parts: the declaration, and the implementation. These may be in separate
parts of a partial type, or in the same part. If there is no implementation for the declaration, then the compiler
removes both the defining declaration and all calls to the method.
115
• Partial methods are indicated by the partial modifier and can be declared within partial classes only.
• Partial methods must be private and must return void.
• Partial methods do not always have an implementation and can also be declared as static.
• Partial methods can have arguments including ref but not out.
• You cannot make a delegate to a partial method.
To test partial methods add 2 new code files Test1.cs and Test2.cs under the project and write the following code:
using System;
namespace OOPSProject {
partial class Test {
partial void Method1(); //Partial method declaration
public void Method2() {
Console.WriteLine("Method 2.");
Method1(); //Calling Partial Method
}
}
}
using System;
namespace OOPSProject {
partial class Test {
partial void Method1() { //Partial method implementation
Console.WriteLine("Method 1.");
}
static void Main() {
Test obj = new Test();
obj.Method2; Console.ReadLine();
}
}
}
Windows Programming
In development of any application we need a user interface (UI) to communicate with end users and User
Interfaces are of 2 types:
1. CUI (Character User Interface)
2. GUI (Graphical User Interface)
Initially we have only CUI, E.g.: Dos, Unix OS etc., where these applications suffers from few criticisms like:
1. They are not user friendly, because we need to learn the commands first to use them.
2. They do not allow navigating from one place to other.
To solve the above problems, in early 90's GUI applications are introduced by Microsoft with its Windows OS,
which has a beautiful feature known as “Look and Feel”. To develop GUI's Microsoft has provided a language also in
90's only i.e. VB (Visual Basic), later when .NET was introduced the support for GUI has been given in all languages
of .NET.
116
Developing Graphical User Interfaces:
To develop GUI’s we need some special components known as controls and those controls are readily
available in .NET Language's as classes under the namespace System.Windows.Forms. All the control classes that are
present under this namespace were grouped into different categories like:
• Common Controls
• Container Controls
• Menus and Tool Bar Controls
• Data Controls
• Components
• Printing Controls
• Dialog Controls
• Reporting Controls
Note: the parent class for all the control classes is the class “Control”, which is defined with the properties, methods
and events that are common for each control like Button, TextBox, Form, Panel, etc.
How to develop a GUI Application?
To develop a GUI Application the base control that has to be created first is Form. To create the Form, first
define a class inheriting from the pre-defined class “Form” so that the new class also becomes a Form.
E.g.: public class Form1 : Form
To run the Form class we have defined call the static method “Run” of “Application” class by passing the
instance of Form we have created as a parameter.
E.g: Form1 f = new Form1(); Application.Run(f); or Application.Run(new Form1());
Note: we can develop a windows application either by using a notepad following the above process as well as under
Visual Studio also using “Windows Forms App” project template.
Developing Windows Application using Notepad: Open notepad, write the following code in it, save, compile and
then execute.
117
To develop a windows application under VS open the new project window, select “Windows Forms App”
project template and specify a name to the project, e.g.: WindowsProject.
By default the project comes with 2 classes in it:
• Form1
• Program
Form1 is the class which is defined inheriting from predefined class Form.
E.g.: public partial class Form1 : Form
Here the class Form1 is partial which means it is defined on multiple files, those are:
• Form1.cs
• Form1.Designer.cs
Note: we will not find the Form1.Designer.cs file open by default to open it go to solution explorer expand the node
Form1.cs and under it we find Form1.Designer.cs file, double click on it to open.
Design View is the place where we design the application; this is accessible both to programmers and end
user's whereas Code View is the place where we write code for the execution of application, this is accessible only
to programmers.
Note: because of the design view what VS provides we call it as WYSIWYG IDE (What You See Is What You Get).
Program is a static class and in this class we find a Main method under which object of class Form1 is created
for execution, as following:
Application.Run(new Form1());
Note: Program class is the main entry point of the project from where the execution starts.
Adding new Forms in the project:
A project can contain any no. of forms in it, to add a new form under our project i.e. “WindowsProject”,
open Solution Explorer => right click on project and select Add => "Windows Form", which adds a new form Form2.cs.
To run the new form, go to Program class and change the code under Application.Run method as Form2.
E.g.: Application.Run(new Form2());
Properties: as we are aware that every control has properties, methods and events, to access the properties of a
control VS provides “Property Window” that lists all the properties of a control, to open Property Window select the
control and press F4. We can change any property value in the list of properties, under property window like Width,
118
Height, BackColor, Font, ForeColor etc., for which we can see the impact immediately after changing the property
value. To test this go to properties of Form2 we have added right now and change any property value you want.
Whenever we set a value to any property of a control under property window, VS on behalf of us writes all
the necessary code by assigning values to the properties we have modified. We can view that code under
InitializeComponent() method of the class which is called in constructor of the class. To view code under
InitializeComponent method, go to Code View and right click on the method called in constructor and select “Go to
definition”, this takes us to Form2.Designer.cs file and here also we find the same class Form2 because it is partial.
Setting properties to control manually by writing the code in Notepad:
using System; using System.Drawing; using System.Windows.Forms;
public class Form2 : Form {
public Form2() {
InitializeComponent();
}
private void InitializeComponent() {
this.Text = "Form2"; this.BackColor = Color.Pink; this.Size = new Size(400, 400);
}
static void Main() {
Application.Run(new Form2());
}
}
Event: it is a time period which tells when an action has to be performed i.e. when exactly we want to execute a
method. Every control will have no. of events under it where each event occurs on a particular time period. We can
access the events of a control also under property window only. To view them in the property window choose events
Tab on top of the property window. If we want to write any code that should execute when an event occurs double
click on the desired event corresponding to a control, which takes you to code view and provides a method for
writing the code.
Now in our project add a new form Form3.cs, go to its Events, double click on Load Event and write the
following code under Form3_Load method that is generated in Code View:
MessageBox.Show("Welcome to windows applications.");
Again go to design view, double click on the Click Event and write following code under Form3_Click method:
MessageBox.Show("You have clicked on the form.");
What happens when we double click on an event of a control in the property window?
When we double click on an event in property window internally a method gets generated for writing the
code and that method has a special name "Event Procedure", which is a block of code that is bound with an event
of control and gets executed whenever the event occurs. The code written under event procedure will be executed
by the event whenever the event occurs by taking the help of a delegate internally, as following:
119
In the above case whenever the Event occur it will call the delegate which then executes the event
procedure that is bound with the event; because a delegate is responsible for execution of the event procedure first
the control, event, delegate and event procedure should be bound with each other as following:
Events and Delegates are pre-defined under FCL (Events are defined in control classes and delegates are
defined under namespaces), what is defined here is only an event procedure. After defining the event procedure in
form class VS links the Control, Event, Delegate and Event Procedure with each other as we have seen above and
this can be found under the method InitializeComponent of “Designer.cs” file.
Note: 1 delegate can be used by multiple events to execute event procedures, but all events will not use the same
delegates, where different events may use different delegates to execute event procedures.
How to define an Event Procedure manually?
To define Event Procedures manually we need to follow a standard format as following:
Syntax: [<modifiers>] void <Name>(Object sender, EventArgs e) {
<Stmts>;
}
• Event Procedures are non-value returning methods.
• An event procedure can have any name but Visual Studio adopts a convention while naming them i.e.:
<control name>_<event>. E.g.: Form1_Load, button1_Click, textBox1_KeyPress.
• Every event procedure should take 2 mandatory parameters:
i. Object sender
ii. EventArgs e
Note: When we define event procedures manually we can give any name to them as per our choice.
Defining Event Procedures in Notepad:
using System; using System.Drawing; using System.Windows.Forms;
public class Form3 : Form {
public Form3() {
InitializeComponent();
}
private void InitializeComponent() {
this.Text = "Form3"; this.BackColor = Color.Pink; this.Size = new Size(400, 400);
this.Load += new EventHandler(TestProc1); this.Click += new EventHandler(TestProc2);
}
private void TestProc1(object sender,EventArgs e) {
MessageBox.Show("Load Event Occured.");
}
private void TestProc2(object sender,EventArgs e) {
MessageBox.Show("Click Event Occured.");
}
static void Main() {
120
Application.Run(new Form3());
}
}
Placing controls on a form:
By default we are provided with no. of controls where each control is a class. These controls are available
in ToolBox window on LHS of the studio, which displays all controls, organized under different Tabs (groups). To
place a control on the form either double click, on desired control or select the control and place it in the desired
location on form.
Note: use Layout toolbar to align controls properly.
How does a form gets created ?
When a form is added to the project internally following things takes place:
i. Defines a class inheriting from the pre-defined class “Form” so that the new class is also a Form.
E.g.: public partial class Form1 : Form
ii. Sets some initialization properties like Name, Text etc., under InitializeComponent method.
E.g.: this.Name = "Form1"; this.Text = "Form1";
The code that is present under a windows application is divided into 2 categories:
• Designer Code
• Business Logic
Code which is responsible for construction of the Form is known as designer code and code which is responsible for
execution of the Form is known as business logic. Designer code is generated by VS under InitializeComponent
method of “Designer.cs” file and business logic is written by programmers in the form of Event Procedures.
Before .NET 2.0 designer code and business logic were defined in a class present under a single file as following:
Form1.cs
public class Form1 : Form {
-Designer Code
-Business Logic
}
From .NET 2.0 with introduction of partial classes’ designer code and business logic were separated into 2 different
files but of the same class only as following:
Form1.cs
public partial class Form1 : Form {
-Business Logic
121
}
Form1.Designer.cs
partial class Form1 {
-Designer Code
}
Creating a Form and placing a Button on it using notepad:
using System; using System.Drawing; using System.Windows.Forms;
public class Form4 : Form {
Button button1;
public Form4() {
InitializeComponent();
}
private void InitializeComponent() {
button1 = new Button(); button1.Text = "Click Me"; button1.BackColor = Color.Magenta;
button1.Size = new Size(100, 50); button1.Location = new Point(100, 100);
button1.Click += new EventHandler(TestProc); this.Controls.Add(button1);
this.Text = "Form4"; this.BackColor = Color.Pink; this.Size = new Size(400, 400);
}
private void TestProc(object sender, EventArgs e) {
MessageBox.Show("Button is clicked.");
}
static void Main() {
Application.Run(new Form4());
}
}
Default Events: as we are aware every control has no. of events to it, but 1 event will be default for a Control. To
write code under that default event of Control directly double click on the control which takes to an event procedure
associated with that default event.
Control Default Event
Form Load
Button Click
TextBox TextChanged
CheckBox and RadioButton CheckedChanged
ListView, ListBox, ComboBox and CheckedListBox SelectedIndexChanged
Working with Events and Event Procedures:
The concept of events and event procedures has been derived from classical VB Language, but there an
event procedure can be bound with only single event of a single control, where as in .NET it can be bound with
multiple events of a single control as well as with multiple controls also.
Binding an Event Procedure with multiple Events of a Control:
Add a new form to the project Form4 and double click on it which defines an event procedure Form4_Load,
now bind the same event procedure with click event of form also, to do this go to events of form, select click event
and click on the drop down beside, which displays the list of event procedures available, select “Form4_Load” event
procedure that is defined previously which binds the event procedure with click event also, now under the event
procedure write the following code and execute:
MessageBox.Show("Event Procedure bound with multiple Events of a Control.");
122
Binding an Event Procedure with multiple Controls:
Add a new form in the project i.e. Form5 and design it as below. Now double click on button1 which generates
a click event procedure for button1, bind that event procedure with button2, textBox1, textBox2 and Form5 also
and write the following code under the event procedure: MessageBox.Show("Control is clicked by the user..");
Binding an Event Procedure with multiple Controls & identifying the “Type” of Control which is raising the Event:
Add a new form in the project i.e. Form6 and design it same as Form5. Now double click on button1 which
generates a click event procedure for button1, bind that event procedure with button2, textBox1, textBox2 and
Form6 also and write the following code under the event procedure:
if (sender.GetType().Name == "Button") or if (sender is Button)
MessageBox.Show("Button is clicked by the user.");
else if (sender.GetType().Name == "TextBox") or if (sender is TextBox)
MessageBox.Show("TextBox is clicked by the user.");
else
MessageBox.Show("Form6 is clicked by the user.");
When an event procedure is bound with multiple controls, any of the control can raise the event in runtime
and execute the event procedure, but the instance of control which is raising the event will be coming into the event
procedure and captured under the parameter "sender" of event procedure as following:
As sender is of type object it's capable of storing instance of any class in it, so after the instance of the
control class is captured under sender by calling GetType() method on it we can identify the type of control to which
that instance belongs as we have performed above.
Binding an Event Procedure with multiple Controls and identifying the “Exact Control” which is raising the Event:
Add a new form in the project i.e. Form7 and design it same as Form5. Now double click on button1 which
generates a click event procedure for button1, bind that event procedure with button2, textBox1, textBox2 and
Form7 also and write the following code under the event procedure:
if (sender is Button) {
Button b = sender as Button;
if (b.Name == "button1")
123
MessageBox.Show("Button1 is clicked");
else
MessageBox.Show("Button2 is clicked");
}
else if (sender is TextBox) {
TextBox tb = (TextBox)sender;
if (tb.Name == "textBox1")
MessageBox.Show("TextBox1 is clicked");
else
MessageBox.Show("TextBox2 is clicked");
}
else
MessageBox.Show("Form7 is clicked");
When an event procedure is bound with multiple controls and if we want to identify the exact control which
is raising the event, we need to identify the “Name (Instance Name)” of control. But even if “sender” represents the
control which is raising the event, using sender we cannot find the control name because we are already aware that
instance of a class can be stored in its parent's variable and make it as a reference but with that reference we cannot
access the child class’s members (Rule No. 3 of Inheritance). So if we want to find the name of control that is raising
the event we need to convert sender back into appropriate control type (Button or TextBox) from which it is created
by performing an explicit conversion and then find out the name of control instance. We can convert sender into
control type in any of the following ways:
Note: as RadioButton control allows only single selection when we want to use them under multiple options or
questions we need to group related RadioButton's, so that 1 can be selected from each group, to group them we
need to place RadioButtons on separate container controls like Panel, GroupBox, TabControl etc.
Both these 2 controls provides a common boolean property Checked which returns true if the control is
selected or else returns false, using which we can identify which option has been chosen by the users. Now add a
new form in the project and write the below code by designing the form as following:
124
MessageBox.Show("Radio Button1 is selected");
else if (radioButton2.Checked)
MessageBox.Show("Radio Button2 is selected");
else if (radioButton3.Checked)
MessageBox.Show("Radio Button3 is selected");
GroupBox
GroupBox
• Change the name of every control on the form for example ‘Name’ TextBox as ‘txtName’, ‘Total Fees’
TextBox as ‘txtFees’, ‘Reset Form’ Button as ‘btnReset’ and ‘Close Form’ button as ‘btnClose’, ‘Courses’
GroupBox as ‘gbCourses’, ‘Track’ GroupBox as ‘gbTrack’, ‘C (750)’ CheckBox as ‘cbC’, ‘Normal’ RadioButton
as ‘rbNormal’ and so on.
• Change the ReadOnly property of ‘Total Fees’ TextBox as true so that it becomes non editable, enter ‘0’ as
a value in Text property and also set the TextAlign property as right.
• Set the Tag property for each CheckBox and RadioButton with their corresponding fees values and it should
be ‘0’ for Normal RadioButton. Tag property is used for associating user defined data to any control just like
Text property, but Text value is visible to end user and Tag value is not visible to end user.
• Double click on any one CheckBox so that CheckedChanged Event procedure gets generated; bind that
event procedure with all the remaining CheckBox’s.
• Double click on any one RadioButton so that CheckedChanged Event procedure gets generated; bind that
event procedure with all the remaining RadioButton’s.
• Now go to Code View and write the following code.
125
if (cb.Checked) {
Count += 1; Amt += Convert.ToInt32(cb.Tag);
}
else {
Count -= 1; Amt -= Convert.ToInt32(cb.Tag);
}
txtFees.Text = Amt.ToString();
Code under CheckedChanged Event Procedure of all RadioButton’s:
int Amt = int.Parse(txtFees.Text);
RadioButton rb = sender as RadioButton;
if (rb.Checked)
Amt += Convert.ToInt32(rb.Tag) * Count;
else
Amt -= Convert.ToInt32(rb.Tag) * Count;
txtFees.Text = Amt.ToString();
Code under Click Event Procedure of Reset Form Button:
foreach (Control ctrl in gbCourses.Controls) {
CheckBox cb = ctrl as CheckBox; cb.Checked = false;
}
foreach (Control ctrl in this.Controls) {
if (ctrl is TextBox) {
TextBox tb = ctrl as TextBox; tb.Clear();
}
}
txtFees.Text = "0"; txtName.Focus();
Code under Click Event Procedure of Close Form Button: this.Close();
Button, Label and TextBox Controls:
Note: by default the WordWrap property of the control is set as true disabling horizontal scroll bar so set it as false
to get horizontal scroll bar.
To use the control as a password field either set the PasswordChar property of control with a character we
want to use as Password Character like * or # or $ or @ etc., or else set the UseSystemPasswordChar property as
true which indicates the text in the control should appear as the default password character.
126
MaskedTextBox:
This control looks same as a TextBox but can be used for taking the input in specific formats from the users.
To set a format for input, select the Mask property in property window and click on the button beside it, which opens
a window, in it we can either choose a mask from list of available masks or select custom and specify our own mask
format using zeros in the mask textbox below as following:
Railway PNR: 000-0000000 Credit Card No: 0000-0000-0000-0000 Aadhar No: 0000 0000 0000
Even if we set the Mask as date it will not validate for a valid date and if we want to do that we need to
explicitly write code for the controls TypeValidationCompleted event.
To work with all the above controls add a new form in the project and design it as following:
To perform the first 4 validations first set the MaxLength property of both password textbox's to 16 so that
they will accept only 16 chars, then define a Validating event procedure for user name textbox and bind that event
procedure with both the password textbox's also. Now write the following code under the event procedure:
127
MessageBox.Show("Cannot leave the field empty.", "Mandatory Field Error", MessageBoxButtons.OK,
MessageBoxIcon.Error); e.Cancel = true; return;
}
if (tb.Name != "txtName") {
if (tb.Text.Trim().Length < 8) {
MessageBox.Show("Password should be between 8 to 16 chars", "Password Error", MessageBoxButtons.OK,
MessageBoxIcon.Error); e.Cancel = true; return;
}
}
if(tb.Name == "txtCPwd") {
if(txtPwd.Text != txtCPwd.Text) {
DialogResult dr = MessageBox.Show("Confirm password is not matching with password. \n\nDo you remember
the password?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if(dr == DialogResult.Yes) { txtCPwd.Clear(); txtCPwd.Focus(); }
else { txtPwd.Clear(); txtCPwd.Clear(); txtPwd.Focus(); }
return;
}
txtPwd.Enabled = txtCPwd.Enabled = false;
}
• Validating event occurs when the focus is leaving the control and validates the content entered in the
control.
• Some events are associated with properties with them e.g.: Validating, KeyPress etc., if we want to consume
the properties of an event under its event procedure we can make use of the parameter "e" of the event
procedure which refers to properties of current executing event.
• In the above code “Cancel” is a property of validating event, which when set as true restricts the focus not
to leave the control.
To perform date validation, generate Load event procedure for Form control and set ValidatingType property
for MaskedTextBox to DateTime so that it accepts only Date value as following:
mtbDOB.ValidatingType = typeof(DateTime);
Now generate TypeValidationCompleted event handler for Date MaskedTextBox and write the following
code in it by importing System.Globalization namespace:
128
else {
MessageBox.Show("Date of birth field is mandatory.", "Warning", MessageBoxButtons.OK,
MessageBoxIcon.Warning); e.Cancel = true;
}
If we want the Phone No TextBox to accept only numeric values and back spaces first set the MaxLength
property as 10, then define a KeyPress event procedure for Phone No TextBox and write following code in it:
If we want Mobile No TextBox to start with digits 6, 7, 8 and 9 and should have minimum of 10 digits, generate
a Validating event procedure for Mobile No. TextBox and write the following code in it by importing
System.Text.RegularExpressions namespace:
if (txtMobile.Text.Length > 0) {
Regex mobilePattern = new Regex(@"^[6-9]\d{9}$");
if (!mobilePattern.IsMatch(txtMobile.Text)) {
MessageBox.Show("Entered number is not a valid mobile number.", "Phone Error", MessageBoxButtons.OK,
MessageBoxIcon.Error); e.Cancel = true;
}
}
If we want Email TextBox to accept a valid Email Id generate a Validating event procedure to Email Id
TextBox and write the following code in it:
if (txtEmail.Text.Length > 0) {
Regex emailPattern = new Regex(@"^\w+[\w-\.]*\@\w+((-\w+)|(\w*))\.[a-z]{2,3}$");
if (!emailPattern.IsMatch(txtEmail.Text)) {
MessageBox.Show("Entered string is not in a valid email format.", "Email Error", MessageBoxButtons.OK,
MessageBoxIcon.Error); e.Cancel = true;
}
}
To close the form even from mandatory fields go to properties of Close Button and set its 'CausesValidation'
property as false so that code under that button gets executed before the execution of any other controls validating
event. Now write the following code under Close Button Click Event:
129
}
this.Close();
}
Note: When we set CausesValidation = false for TextBox it will restrict validating event of the control not to occur so
that focus leaves the textbox and form gets closed.
Code under Save Button Click Event Procedure:
MessageBox.Show("Data saved to database");
Code under Clear Button Click Event Procedure:
foreach(Control ctrl in this.Controls) {
if(ctrl isTextBoxBase) {
TextBoxBase tb = ctrl as TextBoxBase; tb.Clear();
}
}
txtPwd.Enabled = txtCPwd.Enabled = true; txtName.Focus();
MessageBox: This control is used for displaying messages with in a windows application by calling its static method
“Show”, which returns a value of type DialogResult (enum), using it we can find out which button has been clicked
on the MessageBox like Ok or Yes or No or Cancel etc. Show is an overloaded method that is defined with different
overloads as following:
• Show(string msg) => Dialog Result
• Show(string msg, string caption) => Dialog Result
• Show(string msg, string caption, MessageBoxButtons buttons) => Dialog Result
• Show(string msg, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) => Dialog Result
MessageBoxButtons is an enum which provides a list of options to choose what buttons should be displayed
on the MessageBox for the user to select.
MessageBoxIcon is an enum which provides a list of options to choose what icon should be displayed on
the MessageBox describing about the message like Error or Warning or Question or Information etc., icons.
ComboBox, ListBox, CheckedListBox: These controls are also used for providing users with a list of values to choose
from. ComboBox allows only single selection but it is editable which gives a chance to either select from the list of
values available or enter a new value, it’s a combination of 2 controls DropDownList + TextBox. ListBox by default
allows single selection only but can be changed to multi-selection by setting the SelectionMode property either to
MultiSimple [Mouse Click] or MultiExtended [Ctrl + Mouse Click]. CheckedListBox is same as ListBox but displays a
CheckBox beside every item for selection and by default it allows multi-selection.
Adding values to the controls: we can add values to the 3 controls in different ways like:
1. In the properties of the control we find a property Items, select it and click on the button beside it which
opens a window, enter the values we want to add, but each in a new line.
2. By using Items.Add method of the control we can add values, but only one at a time.
<List Control>.Items.Add(object value)
3. By using Items.AddRange method of the control an array of values can be added at a time.
<List Control>.Items.AddRange(object[] values)
4. By using DataSource property of the control a DataTable can be bound to it so that all the records of table
gets bound to the control, but as it can display only a single column we need to specify the column to be
displayed using the DisplayMember property.
<List Control>.DataSource = <DataTable>; <List Control>.DisplayMember = <ColumnName>;
130
Accessing all values from the controls: for accessing all the values from List Controls they provide a property known
as Items which returns an object[] of all items.
<List Control>.Items => Object[]
Accessing selected values from the controls: for accessing the selected values from List Controls we need to make
use of the following properties:
ComboBox: Text => string SelectedItem => object SelectedIndex => int
ListBox: SelectedItem => object SelectedIndex => int SelectedItems => object[] SelectedIndices => int[]
CheckedListBox: CheckedItems => object[] CheckedIndices => int[]
Add a new form in the project and design it as below, then go to Items property of the ComboBox control,
click on the button beside it and enter a list of countries in the window opened.
131
Code under first ComboBox SelectedIndexChanged Event Procedure:
comboBox2.Text = “”; comboBox2.Items.Clear();
switch (comboBox1.SelectedIndex) {
case 0: comboBox2.Items.AddRange(new string[] { "January", "February", "March" } ); break;
case 1: comboBox2.Items.AddRange(new string[] { "April", "May", "June" } ); break;
case 2: comboBox2.Items.AddRange(new string[] { "July", "August", "September" } ); break;
case 3: comboBox2.Items.AddRange(new string[] { "October", "November", "December" } ); break;
}
PictureBox: We use this control for displaying images in our Application and to load an image in to the control we
can use any of the following properties:
ImageLocation = <path of the image>
Image = Image.FromFile(string ImgPath)
Image = Image.FromStream(Stream stream)
Use BorderStyle property to control what type of border we want for the PictureBox, with any of the
following values:
None [d] FixedSingle Fixed3D
Use SizeMode property of the control to set image placement and control sizing under the PictureBox which
can be set with any of the following values:
Normal [d] StretchImage AutoSize CenterImage
Dialog Controls:
These are special controls which provide an interface for displaying a list of values too choose from or for
entering of new values, we have 5 dialog controls like ColorDialog, FolderBrowserDialog, FontDialog, OpenFileDialog
and SaveFileDialog. Dialog controls are not shown directly on the form even after adding them, we can see them at
bottom of the studio in design time, to make them visible in runtime we need to explicitly call the method
ShowDialog() on the controls instance, which returns a value of type DialogResult (enum), using it we can find out
which button has been clicked on the DialogControl like Ok button or Cancel button.
Dialog Controls never performs any actions they are only responsible for returning the values to application
developers which has been chosen by end users or entered by the end users and then the developers are responsible
for capturing those values to perform the necessary actions. To capture the values that are chosen or entered by
end users we are provided with following properties:
ColorDialog: Color FolderBrowserDialog: SelectedPath
FontDialog: Font OpenFileDialog & SaveFileDialog: FileName
Design a new form as below, add the ColorDialog, FontDialog, OpenFileDialog and SaveFileDialog controls
to the form and write the below code:
132
Picture Box
Members of Timer:
1. Tick: it is an event which occurs whenever the specified interval time elapses.
2. Interval: it is a property using which we specify the frequency of execution in milliseconds.
3. Start(): it is a method to start the execution of timer.
4. Stop(): it is a method to stop the execution of timer.
Developing an Image Viewer Application:
Add a new form in the project. Place a panel control on the form and set its dock property as right. Place a
picture box control on the form and set its dock property as fill and size mode property as “StretchImage”. Now
design the panel present on the right hand side of the form as following:
133
List Box
Panel
Name the buttons as btnLoad, btnPrev, btnNext, btnStart and btnStop. Set the Enabled property of btnPrev,
btnNext and btnStart button as false, set the Visible property of btnStop as false, then go to properties of form and
under its CancelButton property select the button as btnStop which gets mapped to Esc key of keyboard. Add the
FolderBrowserDialog and Timer controls to the form, set the Interval property of Timer as 2991 and write the
following code in Code View:
using System.IO;
Declarations: object[] OldVals;
Code under Load Images Button Click Event Procedure:
DialogResult dr = folderBrowserDialog1.ShowDialog();
if (dr == DialogResult.OK) {
string dirPath = folderBrowserDialog1.SelectedPath; string[] files = Directory.GetFiles(dirPath, "*.jpg");
if(files.Length > 0) { listBox1.Items.Clear(); listBox1.Tag = dirPath;
foreach (string file in files) {
int pos = file.LastIndexOf("\\"); string fname = file.Substring(pos + 1); listBox1.Items.Add(fname);
}
btnPrev.Enabled = false; btnNext.Enabled = true; btnStart.Enabled = true;
}
else { MessageBox.Show("Selected folder doesn't contain any Jpeg Images.", "Image Viewer",
MessageBoxButtons.OK, MessageBoxIcon.Information); }
}
Code under ListBox SelectIndexChanged Event Procedure:
pictureBox1.ImageLocation = listBox1.Tag + "\\" + listBox1.SelectedItem;
Code under Prev Button Click Event Procedure: listBox1.SelectedIndex -= 1;
Code under Next Button Click Event Procedure: listBox1.SelectedIndex += 1;
Code under Timer Tick Event Procedure:
if (listBox1.SelectedIndex < listBox1.Items.Count - 1) { listBox1.SelectedIndex += 1; }
else { btnStop.PerformClick(); } //PerformClick will invoke click event procedure of the button.
Code under Start Slide Show Button Click Event Procedure:
OldVals = new object[3]; OldVals[0] = FormBorderStyle; OldVals[1] = WindowState; OldVals[2] = panel1.Width;
this.WindowState = FormWindowState.Maximized; this.FormBorderStyle = FormBorderStyle.None;
panel1.Width = 0; btnStop.Visible = true; timer1.Start();
Code under Stop Slide Show Button Click Event Procedure:
timer1.Stop(); btnStop.Visible = false; panel1.Width = (int)OldVals[2];
this.WindowState = (FormWindowState)OldVals[1]; this.FormBorderStyle = (FormBorderStyle)OldVals[0];
AcceptButton and CancelButton Properties of the Form Control:
Under Form we have 2 properties AcceptButton and CancelButton which allows us to map buttons on a
form with keyboard keys. AcceptButton property if bound with a button that button gets clicked whenever we press
Enter key of key board. CancelButton property if bound with a button that button gets clicked whenever we press
134
Esc key of key board. To set them go to properties of form select either AcceptButton or CancelButton properties
which will display the list of buttons on form select a button from them.
Adding Menu's to a Form:
To add menu's to Form first we need to place a MenuStrip control on the Form which is present under
Menu's and Toolbar’s Tab of Toolbox, which sits on top of the Form as the dock property is set as Top.
To add a Menu on MenuStrip click on LHS corner of it which shows a textbox asking to “Type Here”, enter
Text in it which adds a Menu, and repeat the same process for adding of multiple Menu's.
To add a MenuItem under a menu, click on the Menu which shows a textbox below asking to “Type Here”,
enter Text in it which adds a MenuItem, and repeat the same process for adding of multiple MenuItems.
Note: both Menu and MenuItem are objects of the class ToolStripMenuItem.
If we want Menu's to be responding for "Alt Keys" of keyboard prefix with "&" before the character that
should respond for Alt.
E.g.: &File &Edit F&ormat
To define a shortcut for MenuItems so that they respond to keyboard actions, go to properties of
MenuItem, select “Shortcut Keys” Property, click on drop down beside it, which displays a window, in that window
choose a modifier Ctrl or Alt or Shift and then choose a Key from ComboBox below.
To group related MenuItems under a Menu we can add Separators between MenuItems, to do it right click
on a MenuItem and select Insert => separator which adds a separator on top of the MenuItem.
Note: same as we inserted a separator we can also insert a MenuItem if required, in the middle.
If we want to display any Image beside MenuItem right click on it and select “Set Image” which opens a
window, select Local Resource and click on Import button which opens a DialogBox, using it select an image from
your Hard disk.
Sometimes we find check mark beside MenuItem to identify a property is on or off, e.g.: Word Wrap under
Notepad. To provide check marks beside a MenuItem right click on it and select "Checked", but to check or uncheck
the item in run time we need to write code explicitly under click event of MenuItem as following:
if (<control>.Checked == true) { <control>.Checked = false; }
else { <control>.Checked = true; }
Multi Document Interface:
When designing an application it can contain any no. of forms in it, right now to run the desired form we
are explicitly specifying the form class name under Program class. But, when we provide the application to client we
only give him the assemblies of the project (i.e. IL Code) so client doesn't have a chance to edit the class Program
and specify the form class name he wants to run.
To overcome the above problem we are provided with an approach known Multi-Document Interface,
where in this approach an application will be having only one form as a startup Form which is referred as MDI Parent
or MDI Container, so that clients don't require changing the form name under program class at any time.
Rest of the forms in application will be under the control of MDI Parent Form and referred as MDI Children.
To make a form as MDI Parent set the property IsMdiContainer as true. To launch a form as child of parent create
object of child form class, set its MdiParent property with parent forms reference and then call Show() method.
135
Note: an application can have only one MDI Parent and all other forms must be children of parent which should
come and sit under parent. E.g.: Visual Studio by default launches as an MDI Container and the all other forms like
“New Project” and “Add New Item” windows will come and sit under it as its children.
Layout: When we have more than one child form opened at a time under parent, child forms will be arranged inside
the parent by using a layout for arrangement, which can be any of the following 4 options like:
I. Cascade (d): child forms are arranged 1 on the top of the other.
II. TileVertical: child forms are arranged 1 beside the other.
III. TileHorizontal: child forms are arranged 1 below the other.
IV. ArrangeIcons: all child forms icons are arranged with in the bottom of parent.
To test this add a new Form in the Project naming it as MdiParent.cs, set it’s IsMdiContainer property as
true to make it as a MDI Parent and also set the WindowState property as Maximized so that form gets launched to
the full size of the screen. Add a MenuStrip control to the Form and place 2 menus on it: Forms and Layout. Under
Forms Menu add a MenuItem for each child form to launch e.g.: Form1, Form2, Form3 etc. Under Layout Menu add
the following MenuItems: Arrange Icons, Cascade, Horizontal and Vertical and write the following code:
Code under Each Form's MenuItems: Form1 f = new Form1(); f.MdiParent = this; f.Show();
Code under Arrange Icons MenuItem: this.LayoutMdi(MdiLayout.ArrangeIcons);
Code under Cascade MenuItem: this.LayoutMdi(MdiLayout.Cascade);
Code under Vertical MenuItem: this.LayoutMdi(MdiLayout.TileVertical);
Code under Horizontal MenuItem: this.LayoutMdi(MdiLayout.TileHorizontal);
RichTextBox: This control works same as a TextBox but provides advanced text entry and editing features such as
character and paragraph formatting.
136
To create a control in first process we need to add UserControl Item Template in the project which provides
a class that is inheriting from UserControl class, whereas in the second case we need to add Class Item Template and
then inherit from the control class we want to extend.
Note: to develop UserControls we are provided with Windows Forms Control Library project template. But the
controls whatever we develop are finally consumed from Windows Forms App. only.
People working on controls are classified as:
I. Component Developers
II. Application Developers
The person who develops controls is known as component developers and those who consume the controls for
developing applications are known as application developers. While developing controls the developer should first
design the control, write all the behavior to control and then define any required properties, methods and events
for the control.
Properties: these are defined to expose values that are associated with a control to application developer.
E.g.: Text property of TextBox, Checked property of CheckBox, ElapsedTime property of StopClock etc.
Methods: these are defined to perform actions so that application developers can call them when required.
E.g.: Clear(), Focus() methods of TextBox, Close() method of Form, Start(), Stop() method of StopClock etc.
Events: While developing a control the developer of control may not know what actions has to be performed at
some specific time periods. For example:
1. The developer of button control is not aware what should happen when end user clicks on the button.
2. What should happen when button is clicked by end user, will be decided by the application developer.
3. Even if decided by application developer it is the responsibility of component developer to execute the code
that is implemented by application developer whenever end user clicks on the button.
4. To perform the above, component developer first defines an event under his control and then asks the
application developer to write logic/code under an event procedure, binding it with the event he has
defined, so that whenever the event occurs or raises event procedure gets executed.
5. To execute the event procedure we are already aware that events will take the help of delegates.
Note: As we are aware that events take help of delegates to execute event procedures, so while defining events we
must specify which delegate will be used by event to execute the event procedure. So first a delegate has to be
defined and then the event has to be defined. For example:
public delegate void EventHandler(object sender, EventArgs e);
public event EventHandler Click;
All delegates that are pre-defined in FCL which we are consuming right now under existing Controls =>
Events are declared with 2 parameters:
1. Object sender
2. EventArgs e
This is the reason why all our event procedures are also taking the same two parameters because we are
already aware that IO parameters of delegate should be same as the IO parameters of method it has to call.
137
Note: While defining events under user controls we can either make use of any pre-defined delegate or we can also
define our own delegates.
Go to properties of MaskedTextBox, set the Mask property by clicking on the button beside the property,
select “Time (US)”, set the text property as “0000” and also set the ReadOnly property as True, then add a Timer
control and set the interval property as “997”. Now go to code view and write the following code in the class:
138
if(sec < 59) { sec += 1; }
else {
sec = 0;
if(min < 59) { min += 1; }
else { min = 0; timer1.Stop(); }
}
if (sec < 10) { secstr = "0" + sec; }
else { secstr = sec.ToString(); }
if (min < 10) { minstr = "0" + min; }
else { minstr = min.ToString(); }
mtbTime.Text = minstr + secstr;
Under Start Button: timer1.Start();
Under Stop Button: timer1.Stop();
Under Reset Button: timer1.Stop(); mtbTime.Text = "0000"; min = sec = 0;
Under Close Button: if(CloseClick != null) { CloseClick(); } //Step 3: Raising CloseClick event that is defined above.
[Category(“Data”)]
[Description("Gets the elapsed time of current instance of StopClock.")]
public string ElapsedTime { //Defining a property to expose the elapsed time of StopClock.
get { return mtbTime.Text; }
}
public void Start() { //Defining a method to start the StopClock implicitly.
btnStart.Enabled = btnStop.Enabled = false; timer1.Start();
}
public void Stop() { //Defining a method to stop the StopClock implicitly.
timer1.Stop();
}
Consuming the control: Now open solution explorer, right click on the project, select build which compiles the
project and generates an assembly “UserControlsProject.dll” at following location:
<drive>:\<folder>\ControlsProject\ControlsProject\bin\Debug\netcoreapp3.1\UserControlsProject.dll
The control what we have developed can be consumed only by placing it on a Windows Form, so go to our
“WindowsProject” open the ToolBox, right click on it, select “Add Tab” which adds a new tab in the ToolBox enter a
name to it as “CSharp Controls”. Now right click on the “CSharp Controls” tab and select the option “Choose Items”
which opens a dialog box, click on the browse button in it and select the assembly “UserControlsProject.dll” from its
physical location which adds the control under the new tab.
Now add a new form in the project, place the StopClock control on it and run to test the control, once after
testing, to consume the property, methods and event we have defined in control do the following:
Consuming the property: place a new button on the Form, set its text as “Show Elapsed Time” and write the
following code under its click event:
MessageBox.Show(stopClock1.ElapsedTime);
Consuming the method: now to test the method we have defined write the following code under Form Load which
will start the StopClock implicitly when we run the form:
stopClock1.Start();
139
Consuming the event: now to test the CloseClick Event we have defined go to events of the StopClock control and
double click on the CloseClick event which defines an event procedure “stopClock1_CloseClick()”, write the following
code in it which gets executed when we click on the Close button of StopClock:
stopClock1.Stop(); MesageBox.Show(“Form is closing”); this.Close();
Developing an Inherited or Extended Control: In case of inherited controls we copy the design of an existing control
to a new control and then add/change its existing behavior in the new control.
Now let us create a “CustomTextBox” control inheriting from the existing TextBox control and add new
properties to it. To do this open our “UserControlsProject” under which we have developed the “StopClock”, add a
new “Code File” in the project naming it as “CustomTextBox.cs” and write the following code:
[Category("Behavior")]
[Description("Controls the type of values CustomTextBox has to accept.")]
public Options SetOption { get; set; } = 0;
public CustomTextBox() {
this.KeyPress += CustomTextBox_KeyPress;
}
case Options.Char:
if (char.IsLetter(e.KeyChar) == false && Convert.ToInt32(e.KeyChar) != 32) {
MessageBox.Show("Enter alphabet values only.", "Character Error", MessageBoxButtons.OK,
140
MessageBoxIcon.Error); e.Handled = true;
}
break;
case Options.CharOrDigit:
if (char.IsLetterOrDigit(e.KeyChar) == false) {
MessageBox.Show("Enter alpha-numeric values only.", "Alpha-Number Error", MessageBoxButtons.OK,
MessageBoxIcon.Error); e.Handled = true;
}
break;
case Options.Password:
if (char.IsLetterOrDigit(e.KeyChar) == false && Convert.ToInt32(e.KeyChar) != 35 &&
Convert.ToInt32(e.KeyChar) != 45 && Convert.ToInt32(e.KeyChar) != 64 &&
Convert.ToInt32(e.KeyChar) != 95) {
MessageBox.Show("Password can contain alphabets, numeric and special characters like -, _, # and @
only.", "Password Error", MessageBoxButtons.OK, MessageBoxIcon.Error); e.Handled = true;
}
break;
}
}
}
}
Recompile the project again so that the new control “CustomTextBox” gets added to
“UserControlsProject.dll”. To consume this control go back to our WindowsProject, right click on “CSharp Controls”
tab we added earlier, select “Choose Items”, click Browse, select the “UserControlsProject.dll” from its physical
location again, which adds the “CustomTextBox” control to our tab, i.e. under “StopClock” control. Now we can place
the control on any Form and consume it by setting the required “SetOption” value of the control. In case of “Digit”
option by setting “AcceptDecimal” property value as “true” that “TextBox” will accept “Decimal” points in it because
default value of “AcceptDecimal” is false.
Now add a new form in the project, add 6 CustomTextBox’s on the form and test them by setting the
SetOption values as following and for the last CustomTextBox also set AcceptDecimal as true:
ADO.Net
141
Pretty much every application deals with data in some manner, whether that data comes from memory,
databases, XML files, text files, or something else. The location where we store the data can be called as a Data
Source or Data Store where a Data Source can be a file, database, address books or indexing server etc.
Programming Languages cannot communicate with Data Sources directly because each Data Source adopts
a different Protocol (set of rules) for communication, so to overcome this problem long back Microsoft has
introduced intermediate technologies like Odbc and Oledb which works like bridge between the Applications and
Data Sources to communicate with each other.
ODBC (Open Database Connectivity) is a standard C programming language middleware API for accessing
database management systems (DBMS). ODBC accomplishes DBMS independence by using an ODBC driver as a
translation layer between the application and the DBMS. The application uses ODBC functions through an ODBC
driver manager with which it is linked, and the driver passes the query to the DBMS. An ODBC driver will be providing
a standard set of functions for the application to use, and implementing DBMS-specific functionality. An application
that can use ODBC is referred to as "ODBC-Compliant". Any ODBC-Compliant application can access any DBMS for
which a driver is installed. Drivers exist for all major DBMS’s as well as for many other data sources like Microsoft
Excel, and even for Text or CSV files. ODBC was originally developed by Microsoft in 1992.
1. It's a collection of drivers, where these drivers sit between the App's and Data Source's to communicate
with each other and more over we require a separate driver for each and every data source.
2. Odbc drivers comes along with your Windows O.S. and we can find them at the following location:
Control Panel => Administrative Tools => Odbc Data Sources
3. To consume these Odbc Drivers first we need to configure them with the data source by creating a "DSN"
(Data Source Name).
4. Odbc drivers are open source i.e. there is an availability of these Odbc Drivers for all the leading O.S's in the
market.
OLE DB (Object Linking and Embedding, Database, sometimes written as OLEDB or OLE-DB), an API
designed by Microsoft, allows accessing data from a variety of data sources in a uniform manner. The API provides
a set of interfaces implemented using the Component Object Model (COM) and SQL. Microsoft originally intended
OLE DB as a higher-level replacement for, and successor to, ODBC, extending its feature set to support a wider variety
of non-relational databases, such as object databases and spreadsheets that do not necessarily implement SQL. OLE
DB is conceptually divided into consumers and providers. The consumers are the applications that need access to
the data, and the providers are the software components that implement the interface and thereby provide the data
to the consumer. An OLE DB provider is a software component enabling an OLE DB consumer to interact with a data
source. OLE DB providers are alike to ODBC drivers. OLE DB providers can be created to access such simple data
stores as a text file and spreadsheet, through to such complex databases as Oracle, Microsoft SQL Server, and many
others. It can also provide access to hierarchical data stores. These OLE DB Providers are introduced by Microsoft
around the year 1996.
1. It’s a collection of providers where these providers sit between the App's and Data Source to communicate
with each other and we require a separate provider for each data source.
142
2. Oledb Providers are designed for communication with relational & non-relational data source also i.e. it
provides support for communication with any Data Source.
3. Oledb Providers sits on server machine so they are already configured with data source and when we
connect with any data source they will help in the process of communication.
4. Oledb Providers are developed by using COM and Sql Languages, so they are also un-managed.
5. Microsoft introduced OLEDB as a replacement for ODBC for its Windows Systems.
6. Oledb is a pure Microsoft technology which works only on Windows Platform.
Note: If any language wants to consume Odbc Drivers or Oledb Providers they must use some built-in libraries of the
language in which we are developing the application without writing complex coding.
ADO.Net:
143
It is a set of types that expose data access services to the .NET programmer. ADO.NET provides functionality
to developers writing managed code similar to the functionality provided to native COM developers by ADO.
ADO.NET provides consistent access to data sources such as Microsoft SQL Server, as well as data sources exposed
through OLE DB and XML. Data-sharing consumer applications can use ADO.NET to connect to these data sources
and retrieve, manipulate, and update data. It is an integral part of the .NET Framework, providing access to relational
data, XML, and application data. ADO.NET supports a variety of development needs, including the creation of front-
end database clients and middle-tier business objects used by applications or Internet browsers.
ADO.Net provides libraries for Data Source communication under the following namespaces:
• System.Data
• System.Data.Odbc
• System.Data.Oledb
• System.Data.SqlClient
• System.Data.OracleClient
Note: System.Data, System.Data.Odbc, System.Data.Oledb and System.Data.SqlClient namespaces are under the
assembly System.Data.dll whereas System.Data.OracleClient is under System.Data.OracleClient.dll assembly.
System.Data: types of this namespace are used for holding and managing of data on client machines. This
namespace contains following set of classes in it: DataSet, DataTable, DataRow, DataColumn, DataView,
DataRelation, etc.
System.Data.Odbc: types of this namespace can communicate with any Relational Data Source using Un-Managed
Odbc Drivers.
System.Data.Oledb: types of this namespace can communicate with any Data Source using Oledb Providers (Un-
Managed COM Providers).
System.Data.SqlClient: types of this namespace can purely communicate with Sql Server database only using
SqlClient Provider (Managed .Net Framework Provider).
System.Data.OracleClient: types of this namespace can purely communicate with Oracle database only using
OracleClient Provider (Managed .Net Framework Provider).
All the above 4 namespaces contains same set of types as following: Connection, Command, DataReader,
DataAdapter, Parameter and CommandBuilder etc, but here each class is referred by prefixing with Odbc, Oledb,
Sql and Oracle keywords before the class name to discriminate between each other as following:
144
Performing Operations on a DataSource: the operations we perform on a Data Source will be Select, Insert, Update
and Delete, and each and every operation we perform on a Data Source involves in 3 steps, like:
• Establishing a connection with the data source.
• Sending a request to Data Source by using SQL.
• Capturing the results given by the data source.
Note: ConnectionString is a collection of attributes that are required for connecting with a DataSource, those are:
• DSN
• Provider
• Data Source
• User Id and Password
• Integrated Security
• Database or Initial Catalog
• Extended Properties
DSN: this is the only attribute that is required if we want to connect with a data source by using Odbc Drivers and
by using this attribute we need to specify the DSN Name.
Provider: this attribute is required when we want to connect to the data source by using Oledb Providers. So by
using this attribute we need to specify the provider name based on the data source we want to connect with.
Oracle: Msdaora or ORAOLEDB.ORACLE Sql Server: SqlOledb
MS-Access or MS-Excel: Microsoft.Jet.Oledb.4.0 MS-Indexing Server: Msidxs
Data Source: this attribute is required to specify the server name if the data source is a database or else if the data
source is a file we need to specify path of the file and this attribute is required in case of any provider communication.
User Id and Password: This attribute is required to specify the credentials for connection with a database and this
attribute is required in case of any provider communication.
Oracle: Scott/tiger Sql Server: Sa/123
Integrated Security: this attribute is used while connecting with Sql Server Database only to specify that we want
to connect with the Server by using Windows Authentication and in this case we should not use User Id and Password
attributes and this attribute is required in case of any provider communication.
Database or Initial Catalog: these attributes are used while connecting with Sql Server Database only to specify the
name of DB we want to connect with and this attribute is required in case of any provider communication.
Extended Properties: this attribute is required only while connecting with MS-Excel using Oledb Provider.
145
List of attributes which are required in case of Odbc Drivers, Oledb and .Net Framework Providers:
Attribute ODBC Driver OLEDB Provider .NET Framework
Provider
DSN Yes No No
Provider No Yes No
Data Source No Yes Yes
User Id and Password No Yes Yes
Integrated Security* No Yes Yes
Database or Initial Catalog* No Yes Yes
Extended Properties** No Yes -
*Only for Sql Server **Only for Microsoft Excel
Connection String for SqlServer to connect by using different options:
OdbcConnection con = new OdbcConnection("Dsn=<Dsn Name>");
OledbConnection con = new OledbConnection("Provider=SqlOledb;Data Source=<Server Name>;
Database=<DB Name>;User Id=<User Name>;Password=<Pwd>");
SqlConnection con = new SqlConnection("Data Source=<Server Name>;Database=<DB Name>;
User Id=<User Name>;Password=<Pwd>");
Note: in case of Windows Authentication in place of User Id and Password attributes we need to use Integrated
Security = SSPI (Security Support Provider Interface).
146
Testing the process of establishing a connection: open a new project of type Windows Forms Application, name it
as DBOperations and design the form as following:
Again click on Add button, select a driver for Oracle and click Finish button, which opens a window, in it
enter the following details, Data Source Name: OraDsn, Description: Connects with Oracle Database, TNS Service
Name: <Server Name>, User ID: Scott/tiger, click on Ok button which adds the DSN under ODBC Data Source
Administrator window.
Again click on Add button, select a driver for Excel and click Finish button, which opens a window, in it enter
the following details, Data Source Name: ExcelDsn, Description: Connects with Microsoft Excel, Click on the “Select
Workbook” button and select the Excel file (.xls) from it’s physical location and clik on Ok button which adds the DSN
under ODBC Data Source Administrator window.
Now open the Solution Explorer, right click on References node under project and select “Add Reference”
which opens ‘’Reference Manager” dialog box, in that on the LHS under Assemblies option select Framework, now
on the RHS select “System.Data.OracleClient.dll” assembly, click Ok and then write the following code:
147
Under “Connect with Sql Server using .NET Framework Provider” Button Click:
SqlConnection con = new SqlConnection("Data Source=Server;User Id=Sa;Password=123;Database=Master");
con.Open(); MessageBox.Show("Connection with Sql Server using Framework Provider is: " + con.State);
con.Close(); MessageBox.Show("Connection with Sql Server using Framework Provider is: " + con.State);
Under “Connect with MS Excel using Odbc Driver” Button Click:
OdbcConnection con = new OdbcConnection("Dsn=ExcelDsn");
con.Open(); MessageBox.Show("Connection with Excel using Odbc Driver is: " + con.State);
con.Close(); MessageBox.Show("Connection with Excel using Odbc Driver is: " + con.State);
Under “Connect with MS Excel using Oledb Provider” Button Click:
OleDbConnection con = new OleDbConnection(
"Provider=Microsoft.Jet.Oledb.4.0;Data Source=C:\\ExcelDocs\\School.xls;Extended Properties=Excel 8.0");
con.Open(); MessageBox.Show("Connection with Excel using Oledb Provider is: " + con.State);
con.Close(); MessageBox.Show("Connection with Excel using Oledb Provider is: " + con.State);
Under “Connect with Oracle using Odbc Driver” Button Click:
OdbcConnection con = new OdbcConnection("Dsn=OraDsn");
con.Open(); MessageBox.Show("Connection with Oracle using Odbc Driver is: " + con.State);
con.Close(); MessageBox.Show("Connection with Oracle using Odbc Driver is: " + con.State);
Under “Connect with Oracle DB using Oledb Provider” Button Click:
OleDbConnection con = new OleDbConnection("Provider=OraOledb.Oracle;User Id=Scott;Password=tiger");
con.Open(); MessageBox.Show("Connection with Oracle using Oledb Provider is: " + con.State);
con.Close(); MessageBox.Show("Connection with Oracle using Oledb Provider is: " + con.State);
Under “Connect with Oracle using .NET Framework Provider” Button Click:
OracleConnection con = new OracleConnection("Data Source=Server;User Id=Scott;Password=tiger");
con.Open(); MessageBox.Show("Connection with Oracle using .NET Framework Provider is: " + con.State);
con.Close(); MessageBox.Show("Connection with Oracle using .NET Framework Provider is: " + con.State);
Sending request to Data Source by using SQL:
In this process we send a request to Data Source by specifying the type of action we want to perform by
using an Sql Statement like Select, Insert, Update, and Delete or by calling a Stored Procedure present under the
Data Source. To send and execute Sql Statements or call Stored Procedures in Data Source we use Command class.
The object of class Command can be created in any of the following ways:
Command cmd = new Command(); cmd.Connection = <con>; cmd.CommandText = "<Sql Stmt or SP Name>";
or
Command cmd = new Command("<Sql Stmt or SP Name>", <con>);
148
Methods of Command class:
• ExecuteReader() => DataReader
• ExecuteScalar() => object
• ExecuteNonQuery() => int
Note: after creating object of Command class we need to call any of the execute methods to execute the stmt’s.
Use ExecuteReader() method when we want to execute a Select Statement that returns data as rows and
columns. The method returns an object of class DataReader which holds data that is retrieved from data source in
the form of rows and columns.
Use ExecuteScalar() method when we want to execute a Select Statement that returns a single value result.
The method returns result of the query in the form of an object.
Use ExecuteNonQuery() method when we want to execute any SQL statement other than select, like Insert
or Update or Delete etc. The method returns an integer that tells the no. of rows affected by the statement.
Note: The above process of calling a suitable method to capture results is our third step i.e. capturing the results.
Accessing data from a DataReader: DataReader is a class which can hold data in the form of rows and columns, to
access data from DataReader it provides the following members:
1. GetName(int ColumnIndex) => string
Returns name of the column for given index position.
2. Read() => bool
Moves record pointer from current location to next row and returns a Boolean value which tells whether the row to
where it moved contains any data or not, which will be true if data is present or false if data is not present.
3. GetValue(int ColumnIndex) => object
4. Indexer[int ColumnIndex] => object
5. Indexer[string ColumnName] => object
All the 3 are used for retrieving column values from the row to which pointer was pointing by specifying the Column
Index or Column Name.
6. FieldCount => int
This property returns the no. of columns fetched into the DataReader
7. NextResult() => bool
Moves record pointer from current table to next table and returns a Boolean value which tells whether the location
to which it moved contains a table or not, which will be true if present or false if not present.
Add a new Windows Form under the project and design it as following:
using System.Data.OracleClient;
Declarations: OracleConnection con; OracleCommand cmd; OracleDataReader dr;
149
Under Form Load:
con = new OracleConnection(“User Id=Scott;Password=tiger;Data Source=Server”);
cmd = new OracleCommand(“Select Deptno, Dname, Loc From Dept”, con); con.Open(); dr = cmd.ExecuteReader();
label1.Text = dr.GetName(0); label2.Text = dr.GetName(1); label3.Text = dr.GetName(2); ShowData();
private void ShowData() {
if(dr.Read()) {
textBox1.Text = dr.GetValue(0).ToString();
textBox2.Text = dr[1].ToString();
textBox3.Text = dr[“Loc”].ToString();
}
else { MessageBox.Show(“You are alredy at last record of the table.”, “Warning”); }
}
Under Next Button: ShowData();
Under Close Button: if(con.State != ConnectionState.Closed) { con.Close(); } this.Close();
Working with Sql Server
Sql Server is a collection of databases, where a database is again collection of various objects like tables,
views, procedures etc.; users of Sql Server can be owner of 1 or more databases at a time, so while connecting with
Sql Server from a .NET application within the connection string we need to specify name of the database we want
to connect either by using Database or Initial Catalog attributes.
Sql Server provides 2 different modes of authentication for connecting with the DB Server those are:
1. Windows Authentication
2. Sql Server Authentication
When a user connects through windows authentication, Sql Server validates the account name and
password using the windows principal token in the operating system; this means that the user identity is confirmed
by windows, Sql Server does not ask for the password and does not perform the identity validation. When using Sql
Server authentication, logins are created in Sql Server that is not based on windows user accounts, both the user
name and password are created by using Sql Server and stored in Sql Server database. Users connecting with Sql
Server authentication must provide their credentials every time they connect with DB Server.
Note: if we want to connect using windows authentication mode, within the connection string in the place of User
Id and Password attributes use “Integrated Security=SSPI” attributes.
150
Now select Eno Column, right click on it and select the option “Set Primary Key” and make it as an identity
or key column of the table. Select Status column, go to its properties in the bottom and set “Default value or Binding”
property as 1, which takes the default value for status column as true. Click on the save button at top of the studio
which will prompt for table name enter name as “Employee” and click Ok which adds the table under tables node.
Now right click on the table created and select “Edit” which opens a window, enter some data in to the table ignoring
Photo and Status columns. Close the studio.
Now ddd a new form in the project and design it as following and write the below code:
using System.Data.SqlClient;
Declarations: SqlConnection con; SqlCommand cmd; SqlDataReader dr; string SqlStr;
Under Form Load:
con = new SqlConnection("User Id=Sa;Password=123;Database=CSDB;Data Source=Server");
cmd = new SqlCommand(); cmd.Connection = con; con.Open(); LoadData();
private void LoadData() {
cmd.CommandText = "Select Eno, Ename, Job, Salary, Status From Employee Order By Eno";
dr = cmd.ExecuteReader(); ShowData();
}
private void ShowData() {
if (dr.Read()) {
textBox1.Text = dr[0].ToString();
textBox2.Text = dr[1].ToString();
textBox3.Text = dr[2].ToString();
textBox4.Text = dr[3].ToString();
checkBox1.Checked = Convert.ToBoolean(dr[4]);
}
else { MessageBox.Show("No data exists."); }
}
Under Next Button: ShowData();
Under New Button:
textBox1.Text = textBox2.Text = textBox3.Text = textBox4.Text = ""; checkBox1.Checked = false;
dr.Close(); cmd.CommandText = "Select IsNull(Max(Eno), 1000) + 1 From Employee";
textBox1.Text = cmd.ExecuteScalar().ToString(); btnInsert.Enabled = true; textBox2.Focus();
private void ExecuteDML() {
DialogResult d = MessageBox.Show(“Are you sure of executing the below Sql Statement?\n\n" + SqlStr,
"Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
151
if (d == DialogResult.Yes) {
cmd.CommandText = SqlStr;
int count = cmd.ExecuteNonQuery();
if (count > 0) { MessageBox.Show("Statement executed successfully"); }
else { MessageBox.Show("Statement failed execution"); }
LoadData();
}
}
Under Close Button: if (con.State != ConnectionState.Closed) { con.Close(); } this.Close();
Under Insert Button:
SqlStr = "Insert Into Employee (Eno, Ename, Job, Salary, Status) Values(" + textBox1.Text + ", '" + textBox2.Text + "',
'" + textBox3.Text + "', " + textBox4.Text + ", " + Convert.ToInt32(checkBox1.Checked) + ")";
or
SqlStr = String.Format("Insert Into Employee (Eno, Ename, Job, Salary, Status) Values({0}, '{1}', '{2}', {3}, {4})",
textBox1.Text, textBox2.Text, textBox3.Text, textBox4.Text, Convert.ToInt32(checkBox1.Checked));
or
SqlStr = $"Insert Into Employee (Eno, Ename, Job, Salary, Status) Values ({textBox1.Text}, '{textBox2.Text}',
'{textBox3.Text}', {textBox4.Text} , {Convert.ToInt32(checkBox1.Checked)})";
or
SqlStr="Insert Into Employee (Eno, Ename, Job, Salary, Status) Values (@Eno, @Ename, @Job, @Salary, @Status)";
cmd. Parameters.Clear(); cmd.Parameters.AddWithValue("@Eno", textBox1.Text);
cmd.Parameters.AddWithValue("@Ename", textBox2.Text);
cmd.Parameters.AddWithValue("@Job", textBox3.Text);
cmd.Parameters.AddWithValue("@Salary", textBox4.Text);
cmd.Parameters.AddWithValue("@Status", checkBox1.Checked);
dr.Close(); ExecuteDML();
152
Under Delete Button:
SqlStr = "Delete From Employee Where Eno=" + textBox1.Text;
or
SqlStr = String.Format("Delete From Employee Where Eno={0}", textBox1.Text);
or
SqlStr = $"Delete From Employee Where Eno={textBox1.Text}";
or
SqlStr = "Delete From Employee Where Eno=@Eno";
cmd. Parameters.Clear(); cmd.Parameters.AddWithValue("@Eno", textBox1.Text);
dr.Close(); ExecuteDML();
Accessing data from Excel document into .Net Application
MS Excel is a file system which stores data in the form of rows and columns same as a database table. An
Excel document is referred as Work Book that contains Work Sheets in it, work books are considered as databases
and work sheets are considered as tables. First row of work sheet can store column names.
using System.Data.Odbc;
Declarations: OdbcConnection con; OdbcCommand cmd; OdbcDataReader dr; string SqlStr;
Under Form Load:
con = new OdbcConnection("Dsn=ExcelDsn;ReadOnly=0");
cmd = new OdbcCommand(); cmd.Connection = con; con.Open(); LoadData();
label1.Text=dr.GetName(0); label2.Text=dr.GetName(1);
label3.Text=dr.GetName(2); label4.Text=dr.GetName(3);
153
private void LoadData() {
cmd.CommandText = "Select * From [Student$]"; dr = cmd.ExecuteReader(); ShowData();
}
private void ShowData() {
if (dr.Read()) {
textBox1.Text = dr[0].ToString(); textBox2.Text = dr[1].ToString();
textBox3.Text = dr[2].ToString(); textBox4.Text = dr[3].ToString();
}
else
MessageBox.Show("No data exists.”);
}
Under Next Button: ShowData();
Under Clear Button:
textBox1.Text=textBox2.Text=textBox3.Text=textBox4.Text = ""; textBox1.Focus();
private void ExecuteDML() {
dr.Close(); cmd.CommandText = SqlStr;
if (cmd.ExecuteNonQuery() > 0) { MessageBox.Show("Insert Or Update operation was successful."); }
else {MessageBox.Show("Insert or Update operation failed."); }
LoadData();
}
Under Insert Button: SqlStr = String.Format(“Insert Into [Student$] Values ({0}, ‘{1}’ {2}, {3})”, textBox1.Text,
textBox2.Text, textBox3.Text, textBox4.Text); dr.Close(); ExecuteDML();
Under Update Button: SqlStr = String.Format(“Update [Student$] Set Sname='{0}’, Class={1}, Fees={2} where
Sno={3}”, textBox2.Text, textBox3.Text, textBox4.Text, textBox1.Text); ExecuteDML();
Under Close Button: if (con.State != ConnectionState.Closed) { con.Close(); } this.Close();
Connecting with Excel using OLEDB Provider: to connect with Excel documents using OLEDB Provider, Connection
String should be as following:
"Provider=Microsoft.Jet.Oledb.4.0;Data Source=<path of the excel file>;Extended Properties=Excel 8.0"
Note: OdbcConnection class opens connection with Excel Document in read only mode so if we want to perform any
manipulations to data in the document we need to open it in read/write mode by setting the attribute “ReadOnly=0”
under the connection string, whereas OledbConnection will open the document in read/write mode only so no need
of using readonly attribute there.
DataReader: it's a class designed for holding the data on client machines in the form of Rows and Columns.
Features of DataReader:
1. Faster access to data from the Data Source because it is “Connection Oriented”.
2. Can hold multiple tables in it at a time and to load multiple tables into a DataReader pass multiple Select
Statements as “CommandText” to command separated by a semi-colon.
E.g.: Command cmd = new Command("Select * From Student;Select * From Teacher", con);
DataReader dr = cmd.ExecuteReader();
Note: use NextResult() method on data reader object to navigate from current table to next table.
dr.NextResult() => bool
154
Drawbacks of DataReader:
1. As it is connection oriented requires a continuous connection with data source while we are accessing the
data, so there are chances of performance degradation if there are more no. of clients accessing data at the
same time.
2. It gives forward only access to the data i.e. allows going either to next record or table but not to previous
record or table.
3. It is a read only object which will not allow any changes to data that is present in it.
Dis-Connected Architecture: ADO.Net supports 2 different models for accessing data from Data Sources:
In the first case we require a continuous connection with Data Source for accessing data from it and in this
case we use DataReader class for holding data on client machines, where as in the 2nd case we don’t require a
continuous connection with Data Source for accessing the data from it i.e. we require a connection only for loading
data from Data Source and in this case we use DataSet class for holding data on client machines.
Working with DataSet
DataSet: It's a class present under System.Data namespace designed for holding and managing of the data on client
machines apart from DataReader. DataSet class provides the following features:
1. DataSet is also capable of holding multiple tables like a DataReader whereas in case of DataSet those tables
can be loaded from different data sources.
2. It is designed in disconnected architecture which requires a connection just for loading data but not for
holding and accessing the data.
3. It provides scrollable navigation to data which allows us to move in any direction i.e. either top to bottom
or bottom to top.
4. It is updatable i.e. changes can be made to data present in it and those changes can be sent back to DB for
update.
5. It provides options for searching and sorting of data that is present under it.
6. It provides options for establishing relations between the tables that are present under it.
Loading Data into DataSet's: The class which is responsible for loading data into DataReader from a DataSource is
Command, in the same way DataAdapter class is required for communication between DataSource and DataSet.
DataSource <= Command => DataReader
DataSource <=> DataAdapter <=> DataSet
155
Note: Select Command Text means it can be a Select Stmt or a Stored Procedure which contains a Select Stmt.
Instance of DataAdapter class can be created in any of the following ways:
Connection con = new Connection(“<Connection String>”);
Command cmd = new Command(“<Select Stmt or SP Name>”, con);
DataAdapter da = new DataAdapter();
da.SelectCommand = cmd;
Or
Connection con = new Connection(“<Connection String>”);
Command cmd = new Command(“<Select Stmt or SP Name>”, con);
DataAdapter da = new DataAdapter(cmd);
Or
Connection con = new Connection(“<Connection String>”);
DataAdapter da = new DataAdapter(“<Select Stmt or SPName>”, con);
or
DataAdapter da = new DataAdapter(“<Select Stmt or SPName>”, “<Connection String>”);
Properties of DataAdapter:
1. SelectCommand
2. InsertCommand
3. UpdateCommand
4. DeleteCommand
Methods of DataAdapter:
1. Fill(DataSet ds, string tableName)
2. Update(DataSet ds, string tableName)
Fill method is used for loading data from DataSource into the DataSet and Update method is used for
updating any changes made in the DataSet back to DataSource:
When we call Fill method on DataAdapter class, then following actions will take place internally:
• DataAdapter will open a connection with the Data Source.
• Executes the SelectCommand present in it on the DataSource and loads data from table to DataSet.
• Closes the connection.
156
Once the execution of Fill method is completed data gets loaded into the DataSet as below:
As we are discussing DataSet is updatable, we can make changes to the data that is loaded into it like adding,
and modifying and deleting of records and after making changes to data in DataSet if we want to send those changes
back to DataSource we need to call Update method on DataAdapter, which performs the following:
• DataAdapter will re-open the connection with Data Source.
• Changes that are made to data in DataSet will be sent back to corresponding table, where in this process it
will make use of Insert, Update and Delete commands of DataAdapter.
• Closes the connection.
Once Update method execution is completed data gets re-loaded into DataSet as below with all unchanged rows:
Accessing data from DataSet: DataReader’s provides pointer based access to the data, so we can get data only in a
sequential order whereas DataSet provides index based access to the data, so we can get data from any location
randomly. DataSet is a collection of tables where each table is represented as a class DataTable and identified by its
index position or name. Every DataTable is again collection of Rows and collection of Columns where each row is
represented as a class DataRow and identified by its index position and each column is represented as a class
DataColumn and identified by its index position or name.
• Accessing a DataTable from DataSet: <dataset>.Tables[index] or <dataset>.Tables[name]
E.g.: ds.Tables[0] or ds.Tables["Employee"]
157
Add a new form in the project, design it as below, and then add reference of Microsoft.VisualBasic assembly
from Framework tab of add reference window and write the code:
158
Adding a DataRow to DataTable of DataSet:
To add a DataRow to the DataTable of DataSet adopt the following process:
1. Create a new row by calling the NewRow method on DataTable which creates a new row with the same
structure of the DataTable.
2. Assign values to the new row by treating it as a single dimensional array.
3. Call the Rows.Add method on DataTable and add the row to DataRowCollection.
Under Insert Button:
DataRow dr = ds.Tables[0].NewRow();
dr[0] = textBox1.Text; dr[1] = textBox2.Text; dr[2] = textBox3.Text; dr[3] = textBox4.Text;
ds.Tables[0].Rows.Add(dr); rno = ds.Tables[0].Rows.Count - 1; btnInsert.Enabled = false;
MessageBox.Show("DataRow added to DataTable of DataSet.");
Updating a DataRow in DataTable of DataSet:
To update an existing DataRow in DataTable of DataSet we need to re-assign the modified values back to
the DataRow in data table, so that the old values get modified with new values.
Under Update Button:
ds.Tables[0].Rows[rno]["Ename"] = textBox2.Text; ds.Tables[0].Rows[rno]["Job"] = textBox3.Text;
ds.Tables[0].Rows[rno][ "Salary"] = textBox4.Text; MessageBox.Show("DataRow updated in DataTable of
DataSet.");
Deleting a DataRow from DataTable of DataSet:
To delete an existing DataRow in DataTable of DataSet call Delete() method pointing to the row that has
to be deleted on DataTable.
Under Delete Button:
ds.Tables[0].Rows[rno].Delete(); MessageBox.Show("DataRow deleted from DataTable of DataSet.");
Saving changes made in DataTable of DataSet back to DataBase:
If we want to save changes made in DataTable of DataSet back to Database we need to call Update method
on DataAdapter by passing the DataSet which contains modified values as a parameter. If Update method of
DataAdapter has to work it should contain 3 commands under it i.e. Insert, Update and Delete and these 3
commands have to be written by the programmers explicitly or can be generated implicitly with the help of
CommandBuilder class. CommandBuilder class constructor if given with DataAdapter that contains a
SelectCommand in it will generate the remaining 3 commands.
Note: CommandBuilder can generate update and delete commands for a given select command only if the database
table contains Primary Key Constraint on it.
Under Save To DB Button:
SqlCommandBuilder cb = new SqlCommandBuilder(da);
da.Update(ds, "Employee"); MessageBox.Show("Data saved to DB Server");
Under Close Button: this.Close();
Searching for a DataRow in DataTable of DataSet:
To search for a DataRow in DataTable of DataSet call Find method on DataRowCollection this searches for
the DataRow on Primary Key Column(s) of table and returns a Row.
Find(Object key) => DataRow Find(Object[] keys) => DataRow
Use the first method if the primary key constraint is present on a single column or else use the second
method if it is a composite primary key.
Note: if the Find method has to work we need to first load the Primary Key information of database table into DataSet
by setting the property value as “AddWithKey” for MissingSchemaAction of DataAdapter.
159
Under Search Button:
int Eno;
string value = InputBox("Enter Employee No. to search", "Employee Search", "", 150, 150);
if (int.TryParse(value, out Eno)) {
DataRow dr = ds.Tables[0].Rows.Find(Eno);
if (dr != null) {
rno = ds.Tables[0].Rows.IndexOf(dr);
textBox1.Text = dr[0].ToString(); textBox2.Text = dr[1].ToString();
textBox3.Text = dr[2].ToString(); textBox4.Text = dr[3].ToString();
}
else { MessageBox.Show("No Employee exists with given Employee No."); }
}
else { MessageBox.Show("Employee No must be an integer value."); }
Configuration Files
While developing applications if there are any values in application which requires changes in future, should
not be hard coded i.e. should not be maintained as static values within the application, because if any changes are
required for those values in future client will not be able to make changes because they will not have the source
code of application for modification. To overcome this problem we need to identify those values and put them under
a special file known as Configuration File, it’s an XML file which stores values in it in the form of Key/Value pairs. The
values that are present under configuration file can be read from applications in runtime. We store values like
Company Name, Address, Phone No, Fax No, connection strings etc., in these files. When an application is installed
on the client machines along with it the configuration file also will be installed there and because the configuration
file is a text file clients can edit those files and make modification to the values under them at any time and those
values will be taken into the application for execution.
Storing values under configuration file: By default the file comes with a tag <configuration></configuration> and all
the values must be present under this tag only in the form of different sections as following:
<appSettings>
<add key="CompanyName" value="Naresh I Technologies"/>
<add key="Address" value="Ameerpet, Hyderabad - 38"/>
<add key="Phone" value="23746666"/>
<add key="Email" value="[email protected]"/>
</appSettings>
Reading configuration file values from applications: to read configuration file values from applications we are
provided with a class ConfigurationManager within the namespace System.Configuration present under the
assembly System.Configuration.dll. To consume the class we need to first add reference of the assembly using the
“Add Reference” window and we find the assemblies under Framework Tab, after adding the reference of assembly
import the namespace and read the values as following:
ConfigurationManager.AppSettings.Get(string key) => string (returns the value for given key as string)
To test the above process add a configuration file in the project and store values in it as shown above within
the <configuration>/<configurations> tags. Now add a new form in the project place a button on it setting the Text
as “Read Configuration Values” and write the following code in code view:
160
using System.Configuration;
Under Read Configuration Values Button:
string cname = ConfigurationManager.AppSettings.Get("CompanyName");
string addr = ConfigurationManager.AppSettings.Get("Address");
string phone = ConfigurationManager.AppSettings.Get("Phone");
string email = ConfigurationManager.AppSettings.Get("Email");
MessageBox.Show(cname + “\n” + addr + “\n” + phone + “\n” + email);
Storing Connection Strings under configuration files:
We can store the connection string values also in configuration files so that we don’t require to specify the
connection strings in all forms and whenever we want to change it we can make the change directly under the
configuration file. To store connection strings in the configuration file we are provided with a tag
<connectionStrings> same as <appSettings> tag, so we can maintain the connection string values in the configuration
file under <configuration> tag as following:
<connectionStrings>
<add name="OConStr" connectionString="User Id=Scott;Password=tiger;Data Source=Server"
providerName=" ORAOLEDB.ORACLE"/>
<add name="SConStr" connectionString ="User Id=Sa;Password=123;Database=CSDB;
Data Source=Server" providerName="SqlOledb"/>
</connectionStrings>
Note: we can read the connection string values from our application as following:
string oconstr = ConfigurationManager.ConnectionStrings["OConStr"].ConnectionString;
string oprovider = ConfigurationManager.ConnectionStrings["OConStr"].ProviderName;
string sconstr = ConfigurationManager.ConnectionStrings["SConStr"].ConnectionString;
string sprovider = ConfigurationManager.ConnectionStrings["SConStr"].ProviderName;
DataGridView: this control is used for displaying the data in the form of a table i.e. rows and columns. To display
data in the control first we need to bind the DataTable of DataSet to the DataGridView control by using its
DataSource property as following:
dataGridView1.DataSource = <DataTable>
DataGridView control has a specialty i.e. changes performed to data in it gets reflected directly to the data
of DataTable to which it was bound, so that we can update dataset back to database directly.
To test this process add a new Form in the project and place a DataGridView control on it setting its dock
property as top. Now place 2 buttons on the form setting the text as “Save” and “Close” and write the code:
161
using System.Configuration; using System.Data.SqlClient;
Declarations:
DataSet ds; SqlDataAdapter da;
Under Form Load:
string ConStr = ConfigurationManager.ConnectionStrings["SConStr"].ConnectionString;
da = new SqlDataAdapter("Select Eno, Ename, Job, Salary From Employee Order By Eno", ConStr);
ds = new DataSet(); da.Fill(ds, "Employee"); dataGridView1.DataSource = ds.Tables[0];
Under Save Button:
SqlCommandBuilder cb = new SqlCommandBuilder(da); da.Update(ds, "Employee");
MessageBox.Show("Data saved to DB Server.", “Confirmation”);
Under Close Button: this.Close();
Loading multiple tables into DataSet:
A DataSet can hold any no. of tables in it and if we want to load multiple tables into a Dataset we have 2 different
approaches.
1. Using a single DataAdapter we can load any no. of tables into the DataSet by changing the SelectCommand
of DataAdapter each time after calling Fill and loading a table into DataSet. In this approach if we want to
make any changes to the data of dataset and send it back to database we can do it on last table of the
dataset only because an adapter can hold all the 4 commands only for a single table i.e. for the last
SelectCommand we have given only the required insert, update and delete commands will be generated by
CommandBuilder class.
2. Using a separate DataAdapter for each table being loaded into DataSet we can load multiple tables. In this
approach it will be possible to update all the tables data back to the database because each table is using
an individual DataAdapter that will hold the required insert, update and delete commands for a table and
more over, here each table can also be loaded from a different data source i.e. 1 table from Oracle 1 table
from Sql Server etc.
DataView: Just like we have Views in SQL, we have DataView class in ADO.Net. A DataView class object represents
a customized view of DataTable object. Operations like Sorting; Searching can be performed on a DataView object.
In scenarios like retrieval of a subset of data from a DataTable, we can make use of DataView to get this data. Note
that the DefaultView property of a DataTable returns the default data view for the DataTable.
Using a DataView for filtering and sorting the data under DataTable:
Step1: Create a reference of class DataView by calling DefaultView property on the DataTable which will return a
DataView reference with same structure of the table on which the property is called.
DataView dv = ds.Tables[“Emp”].DefaultView;
Step2: Specify a condition for filter by making use of the RowFilter property or specify a column for sorting the data
using Sort property of DataView class.
dv.RowFilter = “Job = ‘Manager’”; dv.RowFilter = “Salary > 2500”;
dv.RowFilter = “Job = ‘Manager’ And Salary > 2500”; dv.RowFilter = “Job = ‘Manager’ Or Salary > 2500”;
dv.Sort = “Salary”; or dv.Sort = “Salary Desc”; or dv.Sort = “Salary, Comm”; or dv.Sort = “Salary, Comm Desc”;
Loading multiple tables into a DataSet using single DataAdapter and filtering the data using DataView:
Add a new form in the project, place a ComboBox control at top center, place a DataGridView control below
and write the following code:
162
using System.Configuration; using System.Data.SqlClient;
Declarations:
SqlDataAdapter da; DataSet ds; bool Flag = false;
Under Form Load:
string ConStr = ConfigurationManager.ConnectionStrings["SConStr"].ConnectionString;
ds = new DataSet(); da = new SqlDataAdapter(“Select * from Dept”, ConStr); da.Fill(ds, “Dept”);
da.SelectCommand.CommandText = “Select * From Emp”; da.Fill(ds, “Emp”);
comboBox1.DataSource = ds.Tables[“Dept”]; comboBox1.DisplayMember = “Dname”;
comboBox1.ValueMember = “Deptno”; comboBox1.SelectedIndex = -1;
comboBox1.Text = “-Select Department-”; dataGridView1.DataSource = ds.Tables[“Emp”]; Flag = true;
Under ComboBox SelectedIndexChanged:
If (Flag) {
DataView dv = ds.Tables[“Emp”].DefaultView;
dv.RowFilter = “Deptno=” + comboBox1.SelectedValue;
dv.Sort = “Salary” or dv.Sort = “Salary Desc”; or dv.Sort = “Salary, Comm”; or dv.Sort = “Salary, Comm Desc”;
}
Note: Just like we can bind a DataTable to DataGridView control in the same way it can also be bound to ComboBox
and ListBox controls using DataSource property, but these controls even if bound with the table they can display
only a single column. So using DisplayMember property of the controls we need to specify which column has to be
displayed. We can also bind another column of the table using ValueMember property of the controls but that
column values will not be visible to end user whereas we can access them in code using SelectedValue property of
control for the selected DisplayMember.
Loading multiple tables into a DataSet from different DataSources using different DataAdapter’s:
Add a new form in the project; place a SplitContainer on it which comes with 2 panels in it. Now place a
button on each panel and set the dock property of button as top. Set the caption of the first button as “Save Data to
Sql Server” and caption of second button as “Save Data to Oracle”. Then add a DataGridView control on each panel
and set their dock property as Fill. Then write the following code:
163
DataRelation:
DataRelation is used to relate two DataTable objects to each other through DataColumn objects. For example,
in a Dept/Emp relationship, “Dept” table is a parent and “Emp” table is a child of the relationship. This is similar to a
primary key/foreign key relationship. Relationships are created between matching columns in the parent and child
tables. That is, the DataType value for both columns must be identical.
To use foreign key constraint we need a parent table that contains master data and a child table that contains
detailed data corresponding to master data. Parent table should contain a Reference Key Column with a Primary Key
or Unique Key Constraints imposed on it, and child table should contain a Foreign Key Column with Foreign Key
Constraint imposed on it which refers into the values of Reference Key Column. If relationships are established
between tables following rules comes into picture:
1. Cannot store a value in the foreign key column of child table, provided the given value is not present in
reference key column of parent table.
2. Cannot delete a row from parent table provided the given reference key value of the record being deleted
has child records in the child table without addressing what to do with the corresponding child records.
3. Cannot update reference key value of the parent table provided the given reference key value being
updated has child records in the child table without addressing what to do with the corresponding child
records.
If we want to establish same type of relations between tables of a DataSet also, we can do it with the help
of DataRelation class.
DataRelation (string relname, DataColumn RKColumn, DataColumn FKColumn)
After creating object of DataRelation it has to be added explicitly to the DataSet under which tables were
present using Relations.Add method of DataSet.
<dataset>.Relations.Add(DataRelation dr)
For deleting or updating reference key values in parent table if the reference key value has an child records
in the child table some rules comes into picture for delete and update known as DeleteRules and UpdateRules, those
are:
1. None: Cannot delete or update reference key value of parent table when corresponding child records exists in
child table, this rule is applied by default under DB's.
2. Cascade: In this case we can delete or update reference key values of parent table, but the corresponding child
records in child table will also be deleted or updated, this rule is applied by default in case of DataSet's.
3. Set Null: In this case also we can delete or update reference key values of parent table but the corresponding child
records foreign key value changes to null.
4. Set Default: This is same as Set Null, but in this case the corresponding child records foreign key value changes
to default value of the column.
Note: we need to apply required rule for delete or update on DataRelation using following statements:
<datarelation>.ChildKeyConstraint.DeleteRule = Rule.<rule>;
<datarelation>.ChildKeyConstraint.UpdateRule = Rule.<rule>;
To use SetDefault rule first we need to set a default value for foreign column using the following statement:
<datatable>.Columns[name].DefaultValue=<value>
164
Loading multiple tables into a DataSet and establishing relation between the tables:
Add a new form in the project, place a SplitContainer on it and change the Orientation property of the
control as Horizontal so that the panels under the SplitContainer will be horizontally aligned (default is vertical).
Place a DataGridView control on each panel and set their Dock property as Fill. Now write the following code:
165
Stored Procedures
Whenever we want to interact with a database from an application we use SQL statements. When we use
SQL statements within the application we have a problem i.e. when the application runs SQL Statements will be sent
to Database for execution and there those SQL statements will be parsed (compile) and then executed. The process
of parsing takes place each time we run the application and because of this performance of our application
decreases. To overcome the above problems write SQL Statements directly under Database only with in an object
known as Stored Procedure and then call them for execution. As a SP is a pre-compiled block of code that is ready
for execution, will directly execute the statements without parsing them each time.
• If required we can also define parameters but only optional. If we want to pass parameters to a Sql Server
SP prefix the special character “@” before parameter name.
public void Test(int x) //CSharp
Create Procedure Test(@x int) //Sql Server
• A SP can also return values, to return a value we use out clause in Oracle and Output clause in Sql Server.
public void Test(int x, ref|out int y) //CSharp
Create Procedure Test(@x int, @y int out|output) //Sql Server
Creating a Stored Procedure:
We can create a SP in Sql Server either by using Sql Server Management Studio or Visual Studio.Net also. To
create a SP from Visual Studio first we need to configure our Database under Server Explorer, to do this go to view
menu, select Server Explorer which gets launched on LHS of the studio. To configure it right click on the node “Data
Connections”, select “Add Connection” which opens a window asking to choose a Data Source select “MS Sql Server”,
click ok, which opens “Add Connection” window and under it provide the following details:
166
Click on the OK button which adds the DB under Server Explorer, expand it, and right click on the node Stored
Procedures, select “Add New Stored Procedure” which opens a window and write the following code:
Create Procedure Employee_Select
As
Select Eno, Ename, Job, Salary, Photo, Status From Employee Order by Eno;
Now right click on the document window & select “Execute” which will create the procedure on Database Server.
Calling a SP from .Net application:
To call a SP from .Net Application we use Command class and the process of calling will be as following:
1. Create instance of class Command by specifying SP Name as CommandText.
3. If the SP has any parameters we need to add those parameters to the Command i.e. if the parameter is
input we need to add input parameters by calling “AddWithValue” method and in-case if the parameter is
output we need to add them by calling “Add” method of Command class.
4. If the procedure is a Query Procedure then call ExecuteReader() method of Command class which executes
the procedure and loads data into DataReader, whereas if we want to load data into DataSet then create
DataAdapter class instance passing Command as a parameter and call Fill Method. If the SP contains any
Non-Query operations then call ExecuteNonQuery method of Command to execute the SP.
Calling above Stored Procedure using DataAdapter: Add a new form in the project, place a DataGridView control
on it, set Dock property as “Fill” and write following code:
167
Under Form Load:
string ConStr = ConfigurationManager.ConnectionStrings["SConStr"].ConnectionString;
con = new SqlConnection(ConStr); cmd = new SqlCommand("Employee_Select", con);
cmd.CommandType = CommandType.StoredProcedure; con.Open();
dr = cmd.ExecuteReader(); ShowData();
private void ShowData() {
if (dr.Read()) {
textBox1.Text = dr[0].ToString(); textBox2.Text = dr[1].ToString();
textBox3.Text = dr[2].ToString(); textBox4.Text = dr[3].ToString();
checkBox1.Checked = (bool)dr["Status"];
}
else
MessageBox.Show("Last record of the table");
}
Under Next Button: ShowData();
Parameters of Stored Procedures:
Stored Procedure’s can be defined with parameters either to send values for execution or receiving values after
execution. While calling a Stored Procedure which has parameters from our .Net application, for each parameter of
the Procedure we need to add a matching parameter under Command i.e. for input parameter matching input
parameter has to be added and for output parameter a matching output parameter has to be added. Every
parameter that is added under Command has 5 attributes to it like Name, Value, DbType, Size and Direction which
can be Input (d) or Output.
• Name refers to name of the parameter that is defined in SP.
• Value refers to value being assigned in case of input or value we are expecting in case of output.
• DbType refers to data type of the parameter in terms of the Database where the SP exists.
• Size refers to size of data.
• Direction specifies whether the parameter is Input or Output.
If a SP has Input or Output parameters we need to specify the following attributes while adding the parameters:
Input Output
Name Yes Yes
Value Yes No
DbType Yes [*] Yes
Size No Yes [**]
Direction No Yes
168
Performing Select and DML Operations using Stored Procedures: To perform select, insert, update and delete
operations using SP’s first define the following procedures in Database.
Alter PROCEDURE Employee_Select (@Eno Int = Null, @Status Bit = Null)
As
Begin
If @Eno Is Null And @Status Is Null --Retrieves all records
Select Eno, Ename, Job, Salary, Photo, Status From Employee;
Else If @Eno Is Null And @Status Is Not Null --Retrieves all records based on status
Select Eno, Ename, Job, Salary, Photo, Status From Employee Where Status=@Status;
Else If @Eno Is Not Null And @Status Is Not Null --Retrieves single record based on Eno and Status
Select Eno, Ename, Job, Salary, Photo, Status From Employee Where Eno=@Eno And Status=@Status;
End
Create Procedure Employee_Insert(@Ename Varchar(50), @Job Varchar(50), @Salary Money, @Photo Image, @Eno
Int Output)
As
Begin
Begin Transaction
Select @Eno = IsNull(Max(Eno), 1000) + 1 From Employee;
Insert Into Employee (Eno, Ename, Job, Salary, Photo) Values (@Eno, @Ename, @Job, @Salary, @Photo);
Commit Transaction;
End;
Create Procedure Employee_Update(@Eno Int, @Ename Varchar(50), @Job Varchar(50), @Salary Money, @Photo
Image)
As
Update Employee Set Ename=@Ename, Job=@Job, Salary=@Salary, Photo=@Photo Where Eno=@Eno;
CREATE PROCEDURE Employee_Delete(@Eno Int)
As
Update Employee Set Status=0 Where Eno=@Eno;
Add a new form, design it as following by adding an OpenFileDialog control and then write below code:
169
textBox1.Text = textBox2.Text = textBox3.Text = textBox4.Text = "";
pictureBox1.Image = null; ImgData = null; ImgPath = ""; textBox2.Focus();
Under Select Button:
string value = Interaction.InputBox("Enter an Employee No.");
if (int.TryParse(value, out int Eno)) {
cmd.Parameters.Clear(); cmd.CommandText = "Employee_Select";
cmd.Parameters.AddWithValue("@Eno", Eno); cmd.Parameters.AddWithValue("@Status", true);
da = new SqlDataAdapter(cmd); ds = new DataSet(); da.Fill(ds, "Employee");
if (ds.Tables[0].Rows.Count > 0) {
ImgData = null; ImgPath =""; pictureBox1.Image = null;
textBox1.Text = ds.Tables[0].Rows[0][0].ToString(); textBox2.Text = ds.Tables[0].Rows[0][1].ToString();
textBox3.Text = ds.Tables[0].Rows[0][2].ToString(); textBox4.Text = ds.Tables[0].Rows[0][3].ToString();
if (ds.Tables[0].Rows[0][4] != DBNull.Value) {
ImgData = (byte[])ds.Tables[0].Rows[0][4];
MemoryStream ms = new MemoryStream(ImgData);
pictureBox1.Image = Image.FromStream(ms);
}
else
MessageBox.Show("Employee doesn't exist, please check the given employee number.", "Warning",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
MessageBox.Show("Enter an integer value as Employee No.", “Warning”, MessageBoxButtons.OK,
MessageBoxIcon.Warning);
Under Insert Button:
try {
cmd.Parameters.Clear(); cmd.CommandText = "Employee_Insert";
cmd.Parameters.AddWithValue("@Ename", textBox2.Text);
cmd.Parameters.AddWithValue("@Job", textBox3.Text);
cmd.Parameters.AddWithValue("@Salary", textBox4.Text);
if(ImgPath.Trim().Length > 0) {
ImgData = File.ReadAllBytes(ImgPath); cmd.Parameters.AddWithValue("@Photo", ImgData);
}
else {
cmd.Parameters.AddWithValue("@Photo", DBNull.Value);
cmd.Parameters["@Photo"].SqlDbType = SqlDbType.Image;
}
cmd.Parameters.Add("@Eno", SqlDbType.Int).Direction = ParameterDirection.Output;
con.Open(); cmd.ExecuteNonQuery();
textBox1.Text = cmd.Parameters["@Eno"].Value.ToString(); ImgPath = ""; ImgData = null;
}
catch (Exception ex)
{ MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error); }
finally { con.Close(); }
Under Update Button:
try {
170
cmd.Parameters.Clear(); cmd.CommandText = "Employee_Update";
cmd.Parameters.AddWithValue("@Eno", textBox1.Text);
cmd.Parameters.AddWithValue("@Ename", textBox2.Text);
cmd.Parameters.AddWithValue("@Job", textBox3.Text);
cmd.Parameters.AddWithValue("@Salary", textBox4.Text);
if (ImgPath.Trim().Length == 0 && ImgData == null) {
cmd.Parameters.AddWithValue("@Photo", DBNull.Value);
cmd.Parameters["@Photo"].SqlDbType = SqlDbType.Image;
}
else if (ImgPath.Trim().Length > 0) {
ImgData = File.ReadAllBytes(ImgPath);
cmd.Parameters.AddWithValue("@Photo", ImgData);
}
else { cmd.Parameters.AddWithValue("@Photo", ImgData); }
con.Open(); cmd.ExecuteNonQuery();
MessageBox.Show("Record updated under the database table.", "Information Message", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{ MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error); }
finally { con.Close();}
Under Delete Button:
try {
cmd.Parameters.Clear(); cmd.CommandText = "Employee_Delete";
cmd.Parameters.AddWithValue("@Eno", textBox1.Text);
con.Open(); cmd.ExecuteNonQuery();
btnClear.PerformClick();
MessageBox.Show("Record deleted under the database table.", "Sql Message", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{ MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK, MessageBoxIcon.Error); }
finally { con.Close(); }
Under Close Button: this.Close();
Under Load Image Button:
openFileDialog1.Filter = "Jpeg Images|*.jpg|Bitmap Images|*.bmp|All Files|*.*";
DialogResult dr = openFileDialog1.ShowDialog();
if (dr == DialogResult.OK) {
ImgPath = openFileDialog1.FileName; pictureBox1.ImageLocation = ImgPath;
}
Under Image Delete Button:
pictureBox1.Image = null; ImgData = null; ImgPath = "";
Accessing All, Active and In-Active records by using Select Stored Procedure:
Create a new form for accessing all, active and in-active records from the table using select SP. To do this
add a new Form in the project, place a ComboBox control on the top center, goto it’s items property and add the
171
values: All, Active & In-Active; now place a DataGridView Control, goto its properties and Set AllowUserToAddRows
and AllowUserToDeleteRows property as false, ReadOnly Property as true and then write the following code:
DataGridView
172
{ dataGridView1.Rows[i].Height = 200; }
}
Note: Create the following tables on your CSDB Database to try the examples show in the document.
Create Table Dept (Deptno Int Constraint Deptno_Pk Primary Key, Dname Varchar(50), Loc Varchar(50))
Insert into Emp Values(1001, 'Scott', 'President', NULL, '01/01/88', 5000, NULL, 10)
Insert into Emp Values(1002, 'Clark', 'Manager', 1001, '01/01/88', 4000, NULL, 10)
Insert into Emp Values(1003, 'Smith', 'Manager', 1001, '01/01/90', 3500, 500, 20)
Insert into Emp Values(1004, 'Vijay', 'Manager', 1001, '01/01/92', 4000, NULL, 30)
Insert into Emp Values(1005, 'Ajay', 'Salesman', 1003, '02/04/89', 3000, 300, 20)
Insert into Emp Values(1006, 'John Smith', 'Salesman', 1003, '02/08/88', 3300, 600, 20)
Insert into Emp Values(1007, 'Venkat', 'Salesman', 1003, '04/15/88', 3300, 0, 20)
Insert into Emp Values(1008, 'Vinod', 'Clerk', 1003, '01/15/88', 2400, NULL, 20)
Insert into Emp Values(1009, 'Suneel', 'Clerk', 1004, '05/12/83', 2000, NULL, 30)
Insert into Emp Values(1010, 'Srinivas', 'Analyst', 1004, '03/01/89', 3400, NULL, 30)
Insert into Emp Values(1011, 'Smyth', 'Analyst', 1004, '03/01/89', 3600, NULL, 30)
Insert into Emp Values(1012, 'Madan', 'Analyst', 1004, '01/09/91', 3100, NULL, 30)
Insert into Emp Values(1013, 'JohnSmith', 'Clerk', 1002, '01/06/88', 1800, NULL, 10)
Insert into Emp Values(1014, 'Raju', 'Clerk', 1005, '06/01/89', 2300, NULL, 20)
Insert into Emp Values(1015, 'Ramesh', 'Clerk', 1011, '08/22/90', 2500, NULL, 30)
Insert into Emp Values(1016, 'Aarush', 'Manager', 1001, '07/15/90', 4200, NULL, 40)
Insert into Emp Values(1017, 'Sridhar', 'Clerk', 1016, '07/20/90', 2500, NULL, 40)
Insert into Emp Values(1018, 'Rahul', 'Supervisor', 1016, '08/01/90', 3500, NULL, 40)
Insert into Emp Values(1019, 'Krishna', 'Fabricator', 1018, '08/12/90', 3100, NULL, 40)
Insert into Emp Values(1020, 'Aaron', 'Fabricator', 1018, '08/21/90', 2900, NULL, 40)
Insert into Emp Values(1021, 'Dave', 'Analyst', 1004, '08/22/90', 3500, NULL, 30)
Insert into Emp Values(1022, 'Kristane', 'Administrator', 1002, '08/22/90', 3000, NULL, 10)
Insert into Emp Values(1023, 'Sophia', 'Administrator', 1003, '08/22/90', 3000, NULL, 20)
Insert into Emp Values(1024, 'Racheal', 'Administrator', 1004, '08/22/90', 3000, NULL, 30)
Insert into Emp Values(1025, 'Elizabeth', 'Administrator', 1016, '08/22/90', 3000, NULL, 40)
--------------------------------------------------------------------------------------------------------------------------------------------------------
Create Table SalGrade(Grade Int, LoSal Money, Hisal Money)
173
Insert into SalGrade values(5, 5001, 8000)
Insert into SalGrade values(6, 8001, 10000)
Collections
Arrays are simple data structures used to store data items of a specific type. Although commonly used,
arrays have limited capabilities. For instance, you must specify an array’s size, and if at execution time, you wish to
modify it, you must do so manually by creating a new array or by using Array class’s Resize method, which creates a
new array and copies the existing elements into the new array.
Collections are a set of pre-packaged data structures that offer greater capabilities than traditional arrays.
They are reusable, reliable, powerful and efficient and have been carefully designed and tested to ensure quality
and performance. Collections are similar to arrays but provide additional functionalities, such as dynamic resizing -
they automatically increase their size at execution time to accommodate additional elements, inserting of new
elements and removing of existing elements.
Initially .NET introduced so many collection classes under the namespace System.Collections like Stack,
Queue, LinkedList, SortedList, ArrayList, Hashtable etc and you can work out with these classes in your application
where you need the appropriate behaviour.
To use these classes open a new project of type “Console Application” naming it as “CollectionsProject”,
now under the first class Program.cs write the following code to use the Stack class which works on the principle
First In Last Out (FILO) or Last In First Out (LIFO):
using System.Collections;
class Program {
static void Main(string[] args) {
Stack s = new Stack(); s.Push(10); s.Push("Hello");
s.Push(DateTime.Now.ToShortDateString()); s.Push(true); s.Push(67.8); s.Push('A');
foreach (object obj in s) { Console.Write(obj + " "); } Console.WriteLine();
Console.WriteLine(s.Pop());
foreach (object obj in s) { Console.Write(obj + " "); } Console.WriteLine();
Console.WriteLine(s.Peek());
foreach (object obj in s) { Console.Write(obj + " "); } Console.WriteLine();
Console.WriteLine(s.Count); s.Clear(); Console.WriteLine(s.Count);
Console.ReadLine();
}
}
Using Queue class which works on the principle First In First Out (FIFO):
using System.Collections;
class Class1 {
static void Main() {
Queue q = new Queue(); q.Enqueue(50); q.Enqueue("World"); q.Enqueue(false);
q.Enqueue('A'); q.Enqueue(DateTime.Now.ToShortDateString()); q.Enqueue(45.73);
foreach (object obj in q) { Console.Write(obj + " "); } Console.WriteLine();
Console.WriteLine(q.Dequeue());
foreach (object obj in q) { Console.Write(obj + " "); } Console.ReadLine();
}
}
174
Auto-Resizing of Collections: The capacity of a collection increases dynamically i.e. when we add new elements to a
Collection the size keeps on incrementing automatically. Every collection class has 3 constructors to it and the
behaviour of collections will be as following when created instance using different constructor:
i. Default Constructor: initializes a new instance of the collection class that is empty and has the default
initial capacity as zero which becomes 4 after adding the first element and from then when ever needed
the current capacity doubles.
ii. Collection(int Capacity): Initializes a new instance of the collection class that is empty and has the
specified initial capacity, here also when requirement comes current capacity doubles.
iii. Collection(Collection c): Initializes a new instance of the collection class that contains elements copied
from the specified collection and that has the same initial capacity as the number of elements copied,
here also when requirement comes current capacity doubles.
ArrayList: this collection class works same as an array but provides auto resizing, inserting and deleting of items.
using System.Collections;
class Class2 {
static void Main() {
ArrayList Coll1 = new ArrayList(2); Console.WriteLine("Initial capacity of collection: " + Coll1.Capacity);
Coll1.Add(10); Console.WriteLine("Capacity after adding 1st item: " + Coll1.Capacity);
Coll1.Add(true); Coll1.Add(DateTime.Now.ToShortDateString()); Coll1.Add(true);
Console.WriteLine("Capacity after adding 4th item: " + Coll1.Capacity);
Coll1.Add(45.7); Console.WriteLine("Capacity after adding 5th item: " + Coll1.Capacity); Coll1.Add("Hello");
for (int i = 0; i < Coll1.Count; i++) { Console.Write(Coll1[i] + " "); } Console.WriteLine();
//Coll1.Remove(true); or //Coll1.RemoveAt(1); or Coll1.RemoveRange(1, 1); //Removes an item from ArrayList
foreach (object obj in Coll1) { Console.Write(obj + " "); } Console.WriteLine();
Coll1.Insert(1, 'Z'); foreach (object obj in Coll1) { Console.Write(obj + " "); } Console.WriteLine("\n");
using System.Collections;
class Class3 {
static void Main() {
Hashtable Emp = new Hashtable();
Emp.Add("EmpId", 1005); Emp.Add("Name", "John"); Emp.Add("Job", "Manager");
Emp.Add("HireDate", DateTime.Parse("01/21/2018"));
Emp.Add("MgrId", 1001); Emp.Add("Salary", 25000.00); Emp.Add("Status", true);
Emp.Add("Mobile", "98327 12742"); Emp.Add("Email", "[email protected]");
Emp.Add("DeptId", 30); Emp.Add("Dname", "Sales"); Emp.Add("Location", "Mumbai");
175
foreach(object key in Emp.Keys) { Console.WriteLine(key + ": " + Emp[key]); } Console.ReadLine();
}
}
Generics: Generics are added in CSharp version 2.0 introducing to the .NET Framework the concept of type
parameters, which make it possible to design classes, and methods that defer the specification of one or more types
until the class or method is declared and instantiated by client code. For example, by using a generic type parameter
“T” you can write a single class that other client code can use without incurring the cost or risk of runtime casts or
boxing operations, in simple words Generics allow you to define a class with placeholders for the type of its fields,
methods, parameters, etc. Generics replace these placeholders with some specific type at consumption time. To
understand these add a class naming it as GenericMethods.cs and write the following code:
class GenericMethods {
public bool AreEqual<T>(T a, T b) {
if(a.Equals(b)) { return true; }
else { return false; }
}
static void Main() {
GenericMethods obj = new GenericMethods();
bool b1 = obj.AreEqual<int>(10, 20); bool b2 = obj.AreEqual<string>("Nit", "Nit");
bool b3 = obj.AreEqual<double>(12.34, 56.678); bool b4 = obj.AreEqual<char>(‘A’, ‘A’);
Console.WriteLine(b1); Console.WriteLine(b2); Console.WriteLine(b3); Console.WriteLine(b4); Console.ReadLine();
}
}
Just like we are passing Type parameter to methods it is possible to pass them to a class also, to test this
add a code file naming it as TestGenericClass.cs and write the following:
using System;
namespace CollectionsProject {
class Math<T> {
public void Add(T x, T y) {
dynamic a = x; dynamic b = y; Console.WriteLine(a + b);
}
public void Sub(T x, T y) {
dynamic a = x; dynamic b = y; Console.WriteLine(a - b);
}
public void Mul(T x, T y) {
dynamic a = x; dynamic b = y; Console.WriteLine(a * b);
}
public void Div(T x, T y) {
dynamic a = x; dynamic b = y; Console.WriteLine(a / b);
}
}
class TestGenericClass {
static void Main() {
Math<int> pi = new Math<int>();
pi.Add(100, 200); pi.Sub(234, 123); pi.Mul(12, 46); pi.Div(900, 45);
Math<double> pd = new Math<double>();
pd.Add(145.35, 12.5); pd.Sub(45.6, 23.4); pd.Mul(15.67, 3.4); pd.Div(168.2, 14.5); Console.ReadLine();
176
}
}
}
Generic Collections: these are also introduced in C# 2.0 which are extension to collections we have been discussing
above, in case of collection classes the elements being added in them are of type object, so we can store any type
of values in them which requires boxing and un-boxing, where as in case of generic collections we can store specified
type of values which provides type safety. Microsoft has re-implemented all the existing collection classes under a
new namespace System.Collections.Generic but the main difference is while creating object of generic collection
classes we need to explicitly specify the type of values we want to store under them. In this namespace we have
been provided with many classes like classes in System.Collections namespace as following:
Stack<T>, Queue<T>, LinkedList<T>, SortedList<T>, List<T>, Dictionary<TKey, TValue>
Note: <T> referes to the type of values we want to store under them. For example:
class Class4 {
static void Main() {
List<int> Coll = new List<int>();
Coll.Add(10); Coll.Add(20); Coll.Add(30); Coll.Add(40); Coll.Add(50); Coll.Add(60); Coll.Add(70); Coll.Add(80);
for (int i = 0; i < Coll.Count; i++) { Console.Write(Coll[i] + " "); } Console.WriteLine();
Coll.Insert(4, 45); foreach (int i in Coll) { Console.Write(i + " "); } Console.WriteLine();
Coll.RemoveAt(6); foreach (int i in Coll) { Console.Write(i + " "); } Console.WriteLine(); Console.ReadLine();
}
}
Dictionary: this class is same as Hashtable we have discussed under collections but here while creating the object
we need to specify the type for keys as well as for values also, as following:
Dictionary<TKey, TValue>
class Class5 {
static void Main() {
Dictionary<string, object> dtEmp = new Dictionary<string, object>();
Emp.Add("EmpId", 1005); Emp.Add("Name", "John"); Emp.Add("Job", "Manager");
Emp.Add("HireDate", DateTime.Parse("01/21/2018"));
Emp.Add("MgrId", 1001); Emp.Add("Salary", 25000.00); Emp.Add("Status", true);
Emp.Add("Mobile", "98327 12742"); Emp.Add("Email", "[email protected]");
Emp.Add("DeptId", 30); Emp.Add("Dname", "Sales"); Emp.Add("Location", "Mumbai");
foreach (string key in Emp.Keys) { Console.WriteLine(key + ": " + Emp[key]); } Console.ReadLine();
}
}
Note: The type of values being stored in a generic collection can be of user-defined type values also like a class type
or structure type that is defined to represent an entity as following:
177
List<Customer> Customers = new List<Customer>(); //Assume Customer is a user-defined class type that
represents an entity Customer, so we can store objects of Customer type under the List where each object can
internally represent different attributes of Customer like Id, Name, City, Balance, Status etc. To Test this add a Code
File Class6.cs and write the following code in it:
using System; using System.Collections.Generic;
namespace CollectionsProject {
public class Customer {
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public double Balance { get; set; }
public bool Status { get; set; }
public override string ToString() => Id + " " + Name + " " + City + " " + Balance + " " + Status;
}
class Class6 {
static void Main() {
Customer c1 = new Customer { Id = 101, Name = "Scott", City = "Delhi", Balance = 15000.00, Status = true };
Customer c2 = new Customer { Id = 102, Name = "Dave", City = "Mumbai", Balance = 10000.00, Status = true };
Customer c3 = new Customer { Id = 103, Name = "Bob", City = "Chennai", Balance = 15000.00, Status = true };
Customer c4 = new Customer { Id = 104, Name = "David", City = "Delhi", Balance = 11000.00, Status = true };
Customer c5 = new Customer { Id = 105, Name = "John", City = "Kolkata", Balance = 4000.00, Status = true };
Customer c6 = new Customer { Id = 106, Name = "James", City = "Agra", Balance = 9000.00, Status = true };
Customer c7 = new Customer { Id = 107, Name = "Jane", City = "Mumbai", Balance = 11000.00, Status = true };
Customer c8 = new Customer { Id = 108, Name = "Steve", City = "Pune", Balance = 4000.00, Status = true };
Customer c9 = new Customer { Id = 109, Name = "Suresh", City = "Bhopal", Balance = 6000.00, Status = true };
Customer c10 = new Customer { Id = 110, Name = "Rehman", City = "Delhi", Balance = 9500.00, Status = true };
List<Customer> Customers = new List<Customer>() { c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 };
foreach (Customer C in Customers) { Console.WriteLine(C); } Console.ReadLine();
}
}
}
Collection Initializers: this is a new feature added in C# 3.0 which allows to initialize a collection directly at the time
of declaration like an array, as following: List<int> li = new List<int>() { 10, 20, 30, 40, 50 };
Add a new class in the project naming it as Class7.cs and write the below code in it:
class Class7 {
static void Main() { //Copying values > 40 from 1 list to another list and arranging them in descending order
List<int> coll1 = new List<int>() { 13,56,29,98,24,54,79,39,8,42,22,93,6,73,35,67,48,18,61,32,86,15,21,81,2 };
List<int> coll2 = new List<int>();
foreach (int i in coll1) { if (i > 40) { coll2.Add(i); } } //Retrieving values of list > 40
coll2.Sort(); //Sorting the new list values in ascending order
coll2.Reverse(); //Reversing new list values to arrange in descending order
Console.WriteLine(String.Join(" ", coll2));
Console.ReadLine();
}
}
178
The above program if used an array, code will be as following (Add a new class Class8.cs and write the below):
class Class8 {
static void Main() {
int[] arr = { 13, 56, 29, 98, 24, 54, 79, 39, 8, 42, 22, 93, 6, 73, 35, 67, 48, 18, 61, 32, 86, 15, 21, 81, 2 };
int Count = 0, Index = 0;
foreach(int i in arr) { if (i > 40) Count += 1; }
int[] brr = new int[Count];
for(int i=0;i<arr.Length;i++) {
if (arr[i] > 40) {
brr[Index] = arr[i]; Index += 1;
}
}
Array.Sort(brr); Array.Reverse(brr);
Console.WriteLine(String.Join(" ", brr)); Console.ReadLine();
}
}
In the above programs we are filtering the values of a List and Array which are greater than 40 and then
arranging them in descending order; to do this we have written a substantial amount of code which is the traditional
process of performing filters on Arrays and Collections.
In C# 3.0 Microsoft has introduced a new language known as “LINQ” much like SQL (which we use
universally with relational databases to perform queries). LINQ allows you to write query expressions (similar to SQL
Queries) that can retrieve information from a wide variety of data sources like objects, databases and xml.
Introduction to LINQ: LINQ stands for Language Integrated Query. LINQ is a data querying methodology which
provides querying capabilities to .NET languages with syntax similar to a SQL Query. LINQ has a great power of
querying on any source of data, where the data source could be collections of objects, database or XML files.
LINQ to Objects: used to perform queries against the in-memory data like an array or collection.
LINQ to Databases:
• LINQ to DataSets is used to perform queries against ADO.NET Data Table’s.
• LINQ to SQL is used to perform queries against the relation database, but only Microsoft SQL Server.
• LINQ to Entities is used to perform queries against any relation database like SQL Server, Oracle, etc.
LINQ to XML (XLinq): used to perform queries against the XML source.
Advantages of LINQ:
179
i. LINQ offers an object-based, language-integrated way to query over data, no matter where that data
came from. So through LINQ we can query database, XML as well as collections.
ii. Compile time syntax checking.
iii. It allows you to query collections, arrays, and classes etc. in the native language of your application like
VB or CSharp.
LINQ to Objects
This is designed to write queries against the in-memory data like an array or collection and filter or sort the
information present under them. Syntax of the query we want to use on objects will be as following:
from <alias> in <array_name | collection_name> [<clauses>] select <alias>
• A Linq Query starts with from and ends with select.
• While using clauses we need to use the alias name we have specified under them, just like we use column
names in case of SQL.
• Clauses can be like where, group by and orderby.
• To use LINQ in your application first we need to import System.Linq namespace.
We can write our previous 2 programs where we have filtered the data of a List or Array and arranged in
sorting order as following using LINQ (Add a new class Class9.cs and write the below):
class Class9 {
static void Main() {
List<int> coll1 = new List<int>() { 13,56,29,98,24,54,79,39,8,42,22,93,6,73,35,67,48,18,61,32,86,15,21,81,2 };
var coll2 = from i in coll1 where i > 40 orderby i descending select i; //Retrieves all elements greater than 40
Console.WriteLine(String.Join(" ", coll2));
int[] arr = { 13, 56, 29, 98, 24, 54, 79, 39, 8, 42, 22, 93, 6, 73, 35, 67, 48, 18, 61, 32, 86, 15, 21, 81, 2 };
var brr = from i in arr where i > 40 orderby i descending select i;
Console.WriteLine(String.Join(" ", brr)); Console.ReadLine();
}
}
Note: the values that are returned by a LINQ query can be captured by using implicitly typed local variables, so in
the above case “coll2” & “brr” are implicitly declared collection/array that stores the values retrieved by the query.
In traditional process of filtering data of an array or collection we have repetition statements that filter
arrays focusing on the process of getting the results i.e. iterating through the elements and checking whether they
satisfy the desired criteria, whereas LINQ specifies, not the steps necessary to get the results, but rather the
conditions that selected elements must satisfy and this is known as declarative programming - as opposed to
imperative programming (which we’ve been using so far) in which we specify the actual steps to perform a task.
Procedural & Object Oriented Languages are a subset of imperative. The queries we have used above specifies that
the result should consist of all the int’s in the List that are greater than 40, but it does not specify how to obtain the
result, C# compiler generates all the necessary code automatically, which is one of the great strengths of LINQ.
LINQ Providers: The syntax of LINQ is built into the language, but LINQ queries may be used in many different
contexts because of libraries known as providers. A LINQ provider is a set of classes that implement LINQ operations
and enable programs to interact with data sources to perform tasks such as sorting, grouping and filtering elements.
When we import the “System.Linq” namespace it contains the LINQ to Objects provider, without importing it the
compiler cannot locate a provider for the LINQ queries and issues errors on LINQ queries.
class Class10 {
static void Main() {
180
string[] colors = { "Red", "Blue", "Green", "Black", "White", "Brown", "Orange", "Purple", "Yellow", "Aqua" };
//var coll = from s in colors select s; //Gets the list of all colors as is
//var coll = from s in colors orderby s select s; //Gets the list of all colors in ascending order
//var coll = from s in colors orderby s descending select s; //Gets the list of all colors in descending order
//var coll = from s in colors where s.Length == 5 select s; //Gets the list of colors whose length is 5 characters
//Getting the list of colors whose name starts with character "B":
//var coll = from s in colors where s[0] == 'B' select s;
//var coll = from s in colors where s.StartsWith("B") select s;
//var coll = from s in colors where s.Substring(0, 1) == "B" select s;
//var coll = from s in colors where s.IndexOf("B") == 0 select s;
//Getting the list of colors whose name ends with character "e":
//var coll = from s in colors where s.EndsWith("e") select s;
//var coll = from s in colors where s[s.Length - 1] == 'e' select s;
//var coll = from s in colors where s.Substring(s.Length - 1) == "e" select s;
//var coll = from s in colors where s.LastIndexOf("e") == s.Length - 1 select s;
//Getting the list of colors whose name contains character "a" at 3rd place:
//var coll = from s in colors where s[2] == 'a' select s;
//var coll = from s in colors where s.IndexOf("a") == 2 select s;
//var coll = from s in colors where s.Substring(2, 1) == "a" select s;
//Getting the list of colors whose name contains character "o" in it:
//var coll = from s in colors where s.Contains('o') || s.Contains('O') select s;
//var coll = from s in colors where s.IndexOf('o') >= 0 || s.IndexOf('O') >= 0 select s;
//var coll = from s in colors where s.ToLower().Contains('o') select s;
//var coll = from s in colors where s.ToUpper().IndexOf('O') >= 0 select s;
//Getting the list of colors whose name dosn't contains character "o" in it:
//var coll = from s in colors where s.IndexOf('o') == -1 && s.IndexOf('O') == -1 select s;
//var coll = from s in colors where s.Contains('o') == false && s.Contains('O') == false select s;
//var coll = from s in colors where s.ToUpper().Contains('O') == false select s;
var coll = from s in colors where s.ToLower().IndexOf('o') == -1 select s;
Console.WriteLine(String.Join(" ", coll)); Console.ReadLine();
}
}
LINQ to SQL
Probably the biggest and most exciting addition to the .Net Framework 3.5 is the addition of the .Net
Language Integrated Query Framework (LINQ) into C# 3.0. Basically, what LINQ provides is a lightweight façade over
programmatic data integration. This is such a big deal because data is King. Pretty much every application deals with
data in some manner, whether that data comes from memory, databases, XML files, text files, or something else.
In C#, programming with objects means a wonderful strongly typed ability to work with code. You can
navigate very easily through the namespaces; work with a debugger in the Visual Studio IDE, and more. However,
when you have to access data, you will notice that things are dramatically different. You end up in a world that is
181
not strongly typed, where debugging is a pain or even non-existent, and you end up spending most of the time
sending strings to the database as commands and as a developer, you also have to be aware of the underlying data
and how it is. Many developers find it very difficult to move from the strongly typed object-oriented world of C# to
the data tier where objects are second-class citizens.
Microsoft has provided LINQ as a lightweight façade that provides a strongly typed interface to the
underlying data stores. LINQ provides the means for developers to stay within the coding environment they are used
to and access the underlying data as objects that work with the IDE, Intellisense, and even debugging. With LINQ,
the queries that you create now become first-class citizens within the .NET Framework alongside everything else you
are used to. When you work with queries for the data store you are working with, you will quickly realize that they
now work and behave as if they are types in the system. This means that you can now use any .NET-compliant
language and query the underlying data stores as you never have before.
LINQ to SQL and Visual Studio:
LINQ to SQL in particular is a means to have a strongly typed interface against a SQL Server database. You
will find the approach that LINQ to SQL provides is by far the easiest approach for querying SQL Server available at
the moment. It is important to remember that LINQ to SQL is not only about querying data, but you can also perform
Insert/Update/Delete operations that you need to perform which are known as CRUD operations
(Create/Read/Update/Delete). Visual Studio comes into strong play with LINQ to SQL in that you will find an
extensive user interface that allows you to design the LINQ to SQL classes you will work with.
To start using LINQ to SQL first open a new Windows Project naming it as “LinqDBProject”, then open the
Server Explorer and create a new table under our database naming the table as “Customer” with the following
columns and also store some initial data in it:
Custid (Int) [PK] Name (Varchar(50)) City (Varchar(50)) Balance (Money) Status (Bit) Not Null Default 1
Adding a LINQ to SQL Class:
To work with LINQ first you need to convert relational objects of DB into object oriented types under the
language and the process of this conversion is known as ORM (Object Relational Mapping) and to perform this we
are provided with a tool under visual studio i.e. OR Designer (Object Relational Designer) which does an outstanding
job of making it as easy as possible.
To start this task, open the “Add New Item” dialog window and in the items of dialog box, you will find
“LINQ to SQL Classes” as an option, name the item as “CSDB.dbml” (Database Markup Language). We can give any
name to the .dbml file but it is always suggested to use our Database name as a name to this, because our database
name is “CSDB” we have named it as “CSDB.dbml”, now click “Add” button which will do the following:
1. Adds a reference to System.Data.Linq assembly which is required to work with “LINQ to Sql”.
2. Under Solution Explorer we will find “CSDB.dbml” and under it we will find 2 sub-items “CSDB.dbml.layout”
and “CSDB.Designer.cs” and under this file only OR-Designer writes all the ORM code converting Relational
Objects into Object Oriented Types.
3. The O/R Designer is added in the studio which will appear as a tab within the document window directly in
the IDE and this is made up of two parts. The first part on the left is for Data Classes, which map to Tables,
Views, etc, dragging such items on this surface will give us a visual representation of those objects. The
second part on the right is for Methods, which map to the Stored Procedures within the DB.
Let us have a look into the code added in CSDB.designer.cs file where we will find a class CSDBDataContent
inheriting from DataContext class; we can view this as something that maps to a Connection type object binding with
the DB. This object works with the connection string and connects to the database for any required operations when
182
we create instance of the class. DataContext class also provides methods like CreateDatabase, DeleteDatabase,
GetTable, ExecuteCommand, ExecuteQuery, SubmitChanges etc, using which we can perform action directly on the
database. Currently this class contains 4 parameterized constructors for creating instance of the class.
Creating the Customer Entity:
For this example, we want to work with the Customer (Entity) table from the CSDB database, which means
that you are going to create a Customer (Entity) class that will use LINQ to SQL map to Customer table. To
accomplishing this task simply open the “Server Explorer” within Visual Studio, configure your DB i.e. CSDB under it,
now drag and drop the Customer table onto the design surface of the O/R Designer in LHS which will prompt with a
window asking for storing of “Connection String” under Config File, so select Yes in it which will add the connection
string into it and also a bunch of code is added to the CSDB.designer.cs file on our behalf with a set of classes in it,
and those classes will give you a strongly typed access to the Customer (Entity) table. When we drag and drop a table
on OR Designer the following actions gets performed internally:
1. Defines a class representing the table (Entity) we have dragged and dropped on the OR Designer where the
name of the class will be same as the table name, as we dropped the Customer table on OR Designer
Customer class gets defined.
2. Defines properties under the class defined representing the table (Entity), where each property represents
each column of the table.
3. Defines a property under the CSDBDataContext class for referring to the table we are working with and the
type of the property will be Table<Entity>, for example, because we are working with Customer Entity the
property name will be Customers and the type of the property will be Table<Customer>.
Apart from the above 3 when we place the first object on the OR Designer it will also perform these activities also:
1. Writes the Connection String in the Web.config file targeting to the database we are working with.
2. Defines a new parameter less constructor under the ASPDBDataContext class and we can use this for
creating the instance for connecting to DB and it only will read the connection string from Web.config.
Note: Table<Entity> is a generic class under System.Data.Linq namespace which contains a set of methods
DeleteOnSubmit, InsertOnSubmit, SingleOrDefault etc. for performing the CRUD operations.
Now place a DataGridView control on the first Form of project, change the name of DataGridView as dgView
and write the following code.
using System.Data.Linq;
Under Form Load:
CSDBDataContext dc = new CSDBDataContext();
Table<Customer> tab = dc.GetTable<Customer>(); or Table<Customer> tab = dc.Customers;
dgView.DataSource = tab;
Note: In the above case, the CSDBDataContext object is used to connect with CSDB database and then the
GetTable<TEntity>() method or Customers property is called to populate the table of type Customer.
183
Now add a new form in project and design it as following:
Declarations: List<Customer> Cust; int Index = 0;
Under Form Load:
CSDBDataContext dc = new CSDBDataContext(); Cust = dc.Customers.ToList(); ShowData();
private void ShowData() {
textBox1.Text = Cust[Index].Custid.ToString(); textBox2.Text = Cust[Index].Name; textBox3.Text = Cust[Index].City;
textBox4.Text = Cust[Index].Balance.ToString(); checkBox1.Checked = Cust[Index].Status;
}
Under Prev Button:
if (Index > 0) {
Index -= 1; ShowData();
}
else {
MessageBox.Show("You are at first record of the table", "Information", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
Under Next Button:
if (Index < Cust.Count - 1) {
Index += 1; ShowData();
}
else {
MessageBox.Show("You are at last record of the table", "Information", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
Under Close Button: this.Close();
Performing CRUD operations using LINQ:
Create 2 new Forms as following, change the name of DataGridView on 1st form as dgView and also make
it non-editable. In 2nd form change the modifier of 4 TextBox’s, CheckBox and Clear Button as Internal.
184
Code under First Form
Declarations: CSDBDataContext dc;
private void LoadData() {
dc = new CSDBDataContext(); dgView.DataSource = dc.Customers;
}
Under Form Load: LoadData();
Under Insert Button:
Form4 f = new Form4(); f.ShowDialog(); LoadData();
Under Update Button:
if (dgView.SelectedRows.Count > 0) {
Form4 f = new Form4();
f.textBox1.Text = dgView.SelectedRows[0].Cells[0].Value.ToString();
f.textBox2.Text = dgView.SelectedRows[0].Cells[1].Value.ToString();
f.textBox3.Text = dgView.SelectedRows[0].Cells[2].Value.ToString();
f.textBox4.Text = dgView.SelectedRows[0].Cells[3].Value.ToString();
f.checkBox1.Checked = (bool)dgView.SelectedRows[0].Cells[4].Value;
f.textBox1.ReadOnly = true; f.btnClear.Enabled = false; f.ShowDialog(); LoadData();
}
else {
MessageBox.Show("Select a record for updating", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
Under Delete Button:
if (dgView.SelectedRows.Count > 0) {
if (MessageBox.Show("Do you wish to delete the record?", "Confirmation", MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes) {
int Custid = Convert.ToInt32(dgView.SelectedRows[0].Cells[0].Value);
Customer obj = dc.Customers.SingleOrDefault(C => C.Custid == Custid);
dc.Customers.DeleteOnSubmit(obj); dc.SubmitChanges(); LoadData();
}
}
else{
MessageBox.Show("Select a record for deleting", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
Code under Second Form
Under Save Button:
CSDBDataContext dc = new CSDBDataContext();
if (textBox1.ReadOnly == false) {
Customer obj = new Customer {
Custid = int.Parse(textBox1.Text),
Name = textBox2.Text,
City = textBox3.Text,
Balance = decimal.Parse(textBox4.Text),
Status = checkBox1.Checked
};
dc.Customers.InsertOnSubmit(obj); dc.SubmitChanges();
185
MessageBox.Show("Record added to database table", "Information", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
else {
Customer obj = dc.Customers.FirstOrDefault(C => C.Custid == int.Parse(textBox1.Text));
obj.Name = textBox2.Text;
obj.City = textBox3.Text;
obj.Balance = decimal.Parse(textBox4.Text);
obj. Status = checkBox1.Checked;
dc.SubmitChanges();
MessageBox.Show("Record modified under database table", "Information", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
Under Clear Button:
textBox1.Text = textBox2.text = textBox3.Text = textBox4.Text = “”;
checkBox1.Checked = false; textBox1.Focus();
Under Close Button: this.Close();
Performing CRUD (Create (Insert), Read (Select), Update and Delete) Opertions:
To Perform CRUD operations on Sql Server Database by using Linq to Sql we need to adopt the following
process for Insert, Update and Delete:
186
The method gets created as following:
public int Add(int? x, int? y, ref int? z)
If the SP contains any non-query operations in it, in such cases the return type of method will be int, where
as if the SP has any select statements in it that returns table results then the return type of the method will be
ISingleResult<T>, where T represents a class that is newly defined when we drag and drop the select SP whose name
will be SP Name suffixed with “Result” i.e. for example if the procedure name is “Employee_Select” then the class
name will be “Employee_SelectResult”. We can find those methods and classes under designer.cs file.
Calling Employee_Select Procedure:
Drag and drop Employee_Select SP on the RHS panel of OR-Designer, take a new form place a ComboBox
control on top center and add values (All, Active and In-Active) using it’s Items property, place a DataGridView
control below the ComboBox, change the name as dgView and write the below code:
187
if (Emp[0].Photo != null) {
ImgData = Emp[0].Photo.ToArray(); MemoryStream ms = new MemoryStream(ImgData);
pictureBox1.Image = Image.FromStream(ms);
}
}
else { MessageBox.Show("No employee exists with given No."); }
}
else { MessageBox.Show("Employee No. should be integer value."); }
Code under Insert Button:
try {
int? Eno = null;
dc.Employee_Insert(textBox2.Text, textBox3.Text, decimal.Parse(textBox4.Text),
ImgPath.Trim().Length > 0 ? new Binary(File.ReadAllBytes(ImgPath)) : null, ref Eno);
if (Eno != null) { textBox1.Text = Eno.ToString(); ImgPath = ""; }
else {
MessageBox.Show("Failed inserting record into the table.", "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
catch (Exception ex) { MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }
Code under Clear Button:
ImgPath = “”; ImgData = null;
textBox1.Text = textBox2.Text = textBox3.Text = textBox4.Text = ""; pictureBox1.Image = null; textBox2.Focus();
Code under Close Button:
this.Close();
Code under Update Button:
try {
if (ImgData != null && ImgPath.Trim().Length == 0) {
dc.Employee_Update(int.Parse(textBox1.Text), textBox2.Text, textBox3.Text, decimal.Parse(textBox4.Text),
new Binary(ImgData));
}
else {
if (ImgPath.Trim().Length > 0) { ImgData = File.ReadAllBytes(ImgPath); }
dc.Employee_Update(int.Parse(textBox1.Text), textBox2.Text, textBox3.Text, decimal.Parse(textBox4.Text),
ImgPath.Trim().Length > 0 ? new Binary(ImgData) : null);
}
MessageBox.Show("Record updated in the table.", "Success);
}
catch(Exception ex) { MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }
Code under Delete Button:
try {
dc.Employee_Delete(int.Parse(textBox1.Text)); btnClear.PerformClick();
MessageBox.Show("Record deleted from the table.", "Success");
}
catch (Exception ex) { MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }
188
Code under Load Image Button:
openFileDialog1.FileName = "";
openFileDialog1.Filter = "Jpeg Images (*.jpg)|*.jpg|Bitmap Images (*.bmp)|*.bmp|All Files (*.*)|*.*";
DialogResult dr = openFileDialog1.ShowDialog();
if (dr == DialogResult.OK) {
imgPath = openFileDialog1.FileName;
pictureBox1.ImageLocation = imgPath;
}
Querying data from tables using LINQ to SQL: we can query and retrieve data from table(s) using a query statement
which should be used as following:
from <alias> in <table_name> [<clauses>] select <alias> | new { <list of columns> }
Now drag and drop, Emp and Dept tables under our CSDB database, on LHS panel of OR-Designer. Create a
new form as following, change the DataGridView name as dgView and write the following code:
189
Under Button9: dgView.DataSource = from E in dc.Emps group E by E.Deptno into G select new
{ Deptno = G.Key, MaxSal = G.Max(E => E.Salary) };
Under Button10: dgView.DataSource = from E in dc.Emps group E by E.Job into G select new
{ Job = G.Key, MinSal = G.Min(E => E.Salary) };
Under Button11: dgView.DataSource = from E in dc.Emps join D in dc.Depts on E.Deptno equals D.Deptno select
new { E.Empno, E.Ename, E.Job, E.Mgr, E.Salary, E.Comm, D.Deptno, D.DName, D.Loc };
Note: Linq doesn’t have having clause, where clause is only provided with the behavior of where as well as having
also. If we use where before group by it works like where and if used after group by it works like having clause.
Assemblies
Assemblies are the building blocks of .NET Framework applications. An assembly is a collection of types and
resources that are built to work together and form a logical unit of functionality. An assembly provides the common
language runtime with the information it needs to be aware of type implementations. To the runtime, a type does
not exist outside the context of an assembly.
Every project after compilation will generate an output file known as assembly. The name of an assembly
file will be same as the project name. The assembly corresponding to a project will be present under
bin\Debug\netcoreapp3.1 folder of that project folder. Assemblies are known as units of deployment because once
the project development is completed what we carry and install on the client systems is the assembly files only. The
extension of an assembly file will be either .exe (executable) or .dll (dynamic link library).
Windows Forms Applications, Console Applications, WPF Applications and Windows Services projects will
generate an .exe assembly. Class Library and Windows Forms Control Library projects will generate a .dll assembly.
Exe assemblies are known as in-process components which were capable of running on their own as well
as provide the support for others to execute. When we work with project templates like Windows Forms
Applications, Console Applications, WPF Applications and Windows Services they generate an exe assembly when
compiled.
Dll assemblies are known as out-process components which were not capable of running on their own they
can only support others to execute. When we work with project templates like Class Library and Windows Forms
Control Library they generate a dll assembly when compiled.
Note: every application is a blend of both .dll’s and .exe assemblies combined together to give better efficiency.
190
Creating an assembly to test it is by default private:
Open a new project of type Class Library and name it as “PAssembly”, which will by default come with a
class Class1 under the file Class1.cs. Now write the following code under the class:
public string SayHello() {
return "Hello from private assembly.";
}
Now compile the project by opening the Solution Explorer, right click on the project and select “Build” which
will compile and generate an assembly with the name as PAssembly.dll.
Note: we can find path of assembly in the output window present at bottom of the studio after build.
Testing the assembly we have created:
Open a new project of type Windows, name it as “TestPAssembly”, place a button on Form and set its text
as “Call SayHello() Method of Class1 in PAssembly.dll”. Now add the reference of PAssembly.dll from its physical
location and write the following code under click of button:
PAssembly.Class1 obj = new PAssembly.Class1();
MessageBox.Show(obj.SayHello());
Run the project to test it, then go and verify under bin/debug folder of current project where we can find a
copy of PAssembly.dll as it is private assembly.
Note: the advantage of a private assembly is faster execution as it was in the local folder, whereas the draw back
was multiple copies gets created when multiple projects adds the reference to consume it.
Shared Assemblies:
If we intend to use an assembly among several applications private assemblies are not feasible, in such
cases we can install it into a centralized location known as the global assembly cache. Each computer where the
common language runtime is installed has this machine-wide code cache. The global assembly cache stores
assemblies specifically designated to be shared by several applications on the computer. All BCL assemblies are
shared .dll assemblies only, so we can find them under GAC. If an assembly is shared multiple copies of the assembly
will not be created even if being consumed by multiple projects, only a single copy under GAC serves all the projects.
Note: administrators often protect the Windows directory using an access control list (ACL) to control write and
execute access. Because the global assembly cache is installed in the Windows directory, it inherits that directory's
191
ACL. It is recommended that only users with Administrator privileges be allowed to add or delete files from the global
assembly cache.
Strong Name:
A strong name consists of the assembly's identity - its simple text name, version number, and public key.
1. Name: it was the name of an assembly used for identification. Every assembly by default has name.
2. Version: software’s maintain versions for discriminating changes that has been made from time to time. As
an assembly is also a software component it will maintain versions, whenever the assembly is created it has
a default version for it i.e. 1.0.0.0, which can be changed when required.
3. Public Key: as GAC contains multiple assemblies in it, to identify each assembly it will maintain a key value
for the assembly known as public key, which should be generated by us and associate with the assembly to
make it Strong Named.
You can ensure that a name is globally unique by signing an assembly with a strong name. In particular,
strong names satisfy the following requirements:
Strong names guarantee name uniqueness by relying on unique key pairs. No one can generate the same
assembly name that you can. Strong names protect the version lineage of an assembly.
A strong name can ensure that no one can produce a subsequent version of your assembly. Users can be
sure that a version of the assembly they are loading comes from the same publisher that created the version the
application was built with.
Strong names provide a strong integrity check. Passing the .NET Framework security checks guarantees that
the contents of the assembly have not been changed since it was built.
192
Note: the above statement generates a key value and writes it into the file “Key.snk”. Key pair files usually have .snk
extension.
Creating a Shared Assembly:
Step 1: generate a public key. Open VS command prompt, go into your folder and generate a public key as following:
<drive>:\<folder> sn -k key.snk
Step 2: develop a new project and add the key file to it before compilation so that the assembly which is generated
will be Strong Named.
To do this open a new project of type Class Library, name it as “SAssembly” and write the following code
under the class Class1:
public string SayHello() {
return "Hello from shared assembly 1.0.0.0";
}
To associate key file we have generated with the project, open project properties window, select Signing
tab on LHS, which displays a CheckBox as “Sign the Assembly” select it, now in the ComboBox below select browse,
select the key.snk file from its physical location which adds the file under solution explorer, then compile the project
using “Build” option that will generate SAssembly.dll which is Strong Named.
Step 3: installing assembly into GAC by using the “Global Assembly Cache Tool”.
To install the assembly into GAC open Visual Studio Command Prompt, go to the location where
SAssembly.dll was present and write the following:
Run the project, test it and verify under bin/debug folder of current project where we will not find any copy
of SAssembly.dll as it is shared assembly.
Versioning Assemblies:
Every assembly is associated with a set of attributes that describes about general info of an assembly like
Title, Company, Description, Version etc. These attributes will be under AssemblyInfo.cs file of each project. To view
them expand properties node under the project in Solution Explorer where we find AssemblyInfo.cs file. We can
change values of any attribute as per our requirements.
193
1. Major Version 2. Minor Version 3. Build Number 4. Revision
What are the criteria for changing the version no. of an assembly?
Ans: we change version no. of an assembly basing on the following criteria:
1. Change the Major version value when we add new types under the assembly.
2. Change the Minor version value when we modify any existing types under the assembly.
3. Change the Build Number when we add new members under types.
4. Change the Revision value when we modify any existing members under types.
Note: GAC allows placing of multiple versions of an assembly in it and provides different applications using different
versions of the assembly to execute correctly using their required version. Now open the GAC folder where we find
2 versions of SAssembly i.e. 1.0.0.0 and 1.0.1.0
194
In general, an assembly consists of four elements:
• The assembly manifest, which contains assembly metadata.
• Type metadata.
• Microsoft intermediate language (MSIL) code or CIL Code that implements the types.
• A set of resources.
Assembly Manifest: contains information about the attributes that are associated with an assembly like Assembly
Name, Version Number, Culture, Strong Name Information, List of files in the assembly etc.
Type Metadata: describes every type and member defined in your code in a language-neutral manner. Metadata
stores the following information:
• Description of the assembly.
▪ Identity (name, version, culture, public key).
▪ Other assemblies that this assembly depends on.
▪ Security permissions needed to run.
• Description of types.
▪ Name, visibility, base class, and interfaces implemented.
▪ Members (methods, fields, properties, events, nested types).
MSIL Code or CIL Code: during compilation of any .NET programming languages, the source code is translated into
CIL code rather than platform or processor-specific code. CIL is a CPU and platform-independent instruction set that
can be executed in any environment supporting the Common Language Infrastructure, such as the .NET runtime on
Windows, or the cross-platform Mono runtime.
195
Software Development
Every software or application we develop will be having 2 parts in it:
1. Front End (Application developed using a Programming Language.)
2. Back End (Data Source where data is stored.)
Application (Front End): for better managing of an application we divide it into 3 different layers as following:
▪ Presentation Layer (UI)
▪ Business Logic Layer (BL)
▪ Data Logic/Access Layer (DL)
Presentation Layer:
▪ Contains all the user interfaces that are required for the application.
▪ The first stage in an application development is designing the UI required for the application.
▪ The presentation layer is a combination of 2 projects i.e. Windows Forms Control Library (.dll) under which
we design all the UI's in the form of UserControls and Windows Forms Application (.exe), the main entry
point of the application.
Note: Never write any logic under the presentation layer to maintain it as lite weight.
196
▪ This contains all the code which is required for managing the data present under the data source and this
is also full of code only.
▪ We write the data accessing logic also under a separate project i.e. Class Library (.dll).
Configuration File: as we are aware that every application requires a configuration file for maintaining application
setting values, it should be present under the exe project only.
Note: while developing an app. make sure we are not developing it as a monolithic unit; it should be better divided
into smaller libraries and then must be integrated as software, as following:
CREATE TABLE Customer(Custid int CONSTRAINT Custid_PK PRIMARY KEY, FirstName varchar(50) NOT NULL,
LastName varchar(50) NOT NULL, Balance money NOT NULL, Phone bigint NOT NULL, EmailId Varchar(50) NOT NULL,
Address varchar(200) NOT NULL, City varchar(50) NOT NULL, State varchar(50) NOT NULL, PinCode int NOT NULL,
Photo image, Status bit NOT NULL Default 1);
CREATE TABLE Employee(Empno int CONSTRAINT Empno_PK PRIMARY KEY, FirstName varchar(50) NOT NULL,
LastName varchar(50) NOT NULL, Salary money NOT NULL, Phone bigint NOT NULL, EmailId Varchar(50) NOT NULL,
Address varchar(200) NOT NULL, DOJ Date Not Null, DeptName varchar(50) NOT NULL, Location varchar(50) NOT
NULL, Photo image, Status bit NOT NULL Default 1);
Step 3: Define procedures to perform Select, Insert, Update and Delete Operations.
Create Procedure Customer_Select(@Custid int = null, @Status bit = null)
As
Begin
197
If @Custid Is Null And @Status Is Null
Select Custid, FirstName, LastName, Balance, Phone, EmailId, Address, City, State, PinCode, Photo From Customer
Order By Custid;
Else If @Custid Is Null and @Status Is Not Null
Select Custid, FirstName, LastName, Balance, Phone, EmailId, Address, City, State, PinCode, Photo From Customer
Where Status=@Status Order By Custid;
Else
Select Custid, FirstName, LastName, Balance, Phone, EmailId, Address, City, State, PinCode, Photo From Customer
Where Custid=@Custid And Status=@Status;
End;
Create Procedure Customer_Insert(@FirstName varchar(50), @LastName varchar(50), @Balance money,
@Phone bigint, @EmailId Varchar(50), @Address varchar(50), @City varchar(50), @State varchar(50),
@PinCode int, @Photo image, @Custid int out)
As
Begin
Begin Transaction
Select @Custid = IsNull(Max(Custid), 100) + 1 From Customer;
Insert Into Customer (Custid, FirstName, LastName, Balance, Phone, EmailId, Address, City, State, PinCode, Photo)
Values(@Custid, @FirstName, @LastName, @Balance, @Phone, @EmailId, @Address, @City, @State, @PinCode,
@Photo);
Commit Transaction;
End;
Create Procedure Customer_Update(@Custid int, @FirstName varchar(50), @LastName varchar(50),
@Balance money, @Phone bigint, @EmailId Varchar(50), @Address varchar(50), @City varchar(50),
@State varchar(50), @PinCode int, @Photo image)
As
Update Customer Set FirstName=@FirstName, LastName=@LastName, Balance=@Balance, Phone=@Phone,
EmailId=@EmailId, Address=@Address, City=@City, State=@State, PinCode=@PinCode, Photo=@Photo
Where Custid=@Custid;
Create Procedure Customer_Delete(@Custid int)
As
Update Customer Set Status=0 Where Custid=@Custid;
Create Procedure Employee_Select(@Empno int = null, @Status bit = null)
As
Begin
If @Empno Is Null And @Status Is Null
Select Empno, FirstName, LastName, Salary, Phone, EmailId, DeptName, Location, DOJ, Address, Photo From
Employee Order By Empno;
Else If @Empno Is Null and @Status Is Not Null
Select Empno, FirstName, LastName, Salary, Phone, EmailId, DeptName, Location, DOJ, Address, Photo From
Employee Where Status=@Status Order By Empno;
Else
Select Empno, FirstName, LastName, Salary, Phone, EmailId, DeptName, Location, DOJ, Address, Photo From
Employee Where Empno=@Empno And Status=@Status;
End;
198
Create Procedure Employee_Insert(@FirstName varchar(50), @LastName varchar(50), @Salary money,
@Phone bigint, @EmailId Varchar(50), @DeptName varchar(50), @Location varchar(50), @DOJ Date, @Address
varchar(200), @Photo image, @Empno int out)
As
Begin
Begin Transaction
Select @Empno = IsNull(Max(Empno), 100) + 1 From Employee;
Insert Into Employee (Empno, FirstName, LastName, Salary, Phone, EmailId, DeptName, Location, DOJ, Address,
Photo) Values(@Empno, @FirstName, @LastName, @Salary, @Phone, @EmailId, @DeptName, @Location,
@DOJ, @Address, @Photo);
Commit Transaction;
End;
Create Procedure Employee_Update(@Empno int, @FirstName varchar(50), @LastName varchar(50), @Salary
money, @Phone bigint, @EmailId Varchar(50), @DeptName varchar(50), @Location varchar(50), @DOJ Date,
@Address varchar(200), @Photo image )
As
Update Employee Set FirstName=@FirstName, LastName=@LastName, Salary=@Salary, Phone=@Phone,
EmailId=@EmailId, DeptName=@DeptName, Location=@Location, DOJ=@DOJ, Address=@Address,
Photo=@Photo Where Empno=@Empno;
Create Procedure Employee_Delete(@Empno int)
As
Update Employee Set Status=0 Where Empno=@Empno;
Step 4: Now add a new Class Library project under the “BankPro” Solution naming it as “DLProject”, add a OR-
Designer in it naming it as “CompanyDB.dbml”, configure the “CompanyDB” Database under Server Explorer, “Drag
& Drop” all the 8 “Stored Procedures” on RHS Panel of OR-Designer to generate methods to perform CRUD
Operations.
Now under the click of the buttons in the 4 “UserControls”, call the methods to perform select, insert,
update and delete operations.
Note: Under “DLProject” Project we find an “app.config” file and in that we find “<connectionStrings>
</connectionStrings> tag, copy it as it is and paste it into “App.config” file which is present under “MainProject”.
Note: Before preparing Setup for an application in the top of the Visual Studio we find a ComboBox showing the
options Debug and Release, default will be Debug change it as Release and then right click on the solution node in
solution explorer and Select Build, which will compile all the Projects and re-generates the assemblies again but not
in the “bin\Debug” folder but in “bin\Release” folder and these assemblies will be used in the setup process.
Note: In Visual Studio 2019, Setup Project is not available by default so we need to install it manually and to do that,
in Visual Studio goto “Extensions” Menu and select “Manage Extensions” which will open a new window and in that
Search for “Installer” which will display “Microsoft Visual Studio Installer Projects” with a download button beside,
click on it and download.
199
To add Setup and Deployment Project in our “BankApplication” solution, open “Add New Project” window
and search for “Setup” which displays all “Setup Projects”, select “Setup Project” in it and click next and specify a
name to the Project i.e. “BankSetup” and click “Create”.
When Setup Project is selected and opened it shows the options as following:
❖ File System on Target Machine
➢ Application Folder
➢ User's Desktop
➢ User's Programs Menu
File System on Target Machine in sense the target system where the project is being installed referring to
folders on that machine. Application Folder refers to the project installation folder, which has to be specified while
installing. User's Desktop refers to the desktop folder of target machine. User's Programs Menu refers to the
programs menu folder of target machine.
We can still add other folders referring to the target machine like Program Files, Fonts, and Global Assembly
Cache Folders etc. To add a new folder right click on "File System on Target Machine" and select "Add Special Folder"
which displays a list of options as below, choose the required folder from the list.
200
Now copy the appropriate content into the appropriate folders so that they get installed on the target
machine in appropriate locations. Under Application Folder copy the assemblies (exe’s, dll’s) that has to be installed
on the target machine, to do this right click on the Application Folder and select the option Add => Project Output
as following:
This opens a window showing the list of projects, select the exe project from it:
This will add the necessary exe's, dll's and config file to the project as following:
201
Apart from Project Output we can also choose Folder or File or Assembly and add them under the
Application Folder. Add Folder is used for adding a new folder for storing any images. Add File is used for adding any
help documents. Add Assembly is used for adding any assemblies that are created outside of the solution.
If we want any shortcuts to be created for our application and place them either on desktop or added to
programs menu do the following: Right click on the exe assembly (item of type output) under the application folder
and select "Create Shortcut":
202
For a shortcut we need to bind an display image of type Icon (.ico), to add an icon image go to the properties
of shortcut => select icon property and select browse from the list, which opens a window => click on browse =>
select application folder => Images folder => click on the add file button => select the image from its physical location
=> click on ok button => again ok button.
Now to place the short cut on desktop or program's menu folder, go to properties of shortcut again and
select the property "Folder" => click on the button beside it which opens a window, from it select user's desktop or
user's programs menu folder which copies the shortcut to the selected folder.
203
We can still add new user interfaces like Splash, License Agreement, Register User, Read Me, Customer
Information etc. To add a new user interface right click on the node Start => select add dialog which displays the list
of interface, choose what u require. e.g.: Splash, License Agreement.
After adding required user interfaces, we can order them by right clicking on them and select MoveUp
and MoveDown options.
Splash requires a bitmap image to be set that has to be displayed, to set it go to the properties of splash
interface => under SplashBitmap property => select browse => choose an .bmp or .jpg image from its physical
location same as we selected the .ico image previously for the shortcut.
License Agreement and ReadMe requires any .rtf file to be set for displaying, which needs to be added same
as above using the LicenseFile property and ReadMeFile properties of the interfaces.
204
Customer Information will prompt for Name, Organization and Serial Number options; by default Serial
Number Options will not be visible to make it visible set the ShowSerialNumber property as true:
Setting the SerialNumberTemplate property to "<### - %%%%>" creates two text boxes separated by a
dash surrounded by spaces. Validation for the first box (###) simply verifies that the user has entered three digits.
The second box (%%%%) is validated by an algorithm that adds the digits together and divides the sum by 7. If the
remainder is 0, validation succeeds; otherwise, it fails.
After configuring all the things under the Setup project, right click on the SetUp Project in the solution
explorer and Select Build, which will compile all the Projects and prepare's the SetUp File which u can find them
under the SetUp Project’s Release Folder which can is copied to a Disk, and carried to client system for installing.
Note: If the application is associated with a Database, take the back copy of database also to client machines by
copying it on to another Disk.
Installing the Software or Project on Client Machines:
While installing the software on client machines, if the software has to be installed on multiple computers
we need to first configure the “LAN” and on the server machine, install Database software and restore the back-up
of database we have brought. Install .Net Framework on all client machines, install the Project Setup we have
brought on all those machine, open the Config File on all client machines and specify the Connection String taget to
the machine where Database is installed which should finally look as following:
205