C# classes allow for modularity, data encapsulation, inheritance, and polymorphism. They act as blueprints for generating object instances. The document discusses key object-oriented programming concepts in C# like encapsulation, inheritance, polymorphism, casting, exception handling, garbage collection, interfaces, collections, comparables, and delegates. It provides examples to illustrate concepts like shallow cloning using ICloneable, implementing IComparable, overloading operators, and using XML documentation comments.
prashanth updated resume 2024 for Teaching Profession
C# Unit 2 notes
1. 10
OBJECT ORIENTED PROGRAMMING WITH C#
C# Classes
Classes are used to accomplish:
Modularity:
Separating the functionality of a program into independent, interchangeable modules,
such that each contains everything necessary to execute only one aspect of the desired
functionality.
Blueprints(a design plan) for generating objects or instances :
Per instance data and method signatures (Method name, number of parameters,
parameter type and order of parameters)
Classes support:
Data encapsulation - private data and implementation.
Inheritance - code reuse
Polymorphism - The ability for classes to provide different implementations of methods that
are called by the same name
Encapsulation
Encapsulation refers to language’s ability to hide unnecessary implementation
details from the object user (Wrapping up of data).
For example, assume you are using a class named DatabaseReader that has two
methods named Open() and Close():
// DatabaseReader encapsulates the details of database manipulation.
DatabaseReader dbObj = new DatabaseReader();
dbObj.Open(@"C:Employees.mdf");
// Do something with database...
dbObj.Close();
DatabaseReader class has encapsulated the inner details of locating, loading,
manipulating, and closing the data file.
Inheritance
Inheritance is the language’s ability to allow you to build new class definitions based
on existing class definitions.
Inheritance allows us to extend the behavior of a base (or parent) class by enabling a
subclass to inherit core functionality (also called a derived class or child class)
Inheritance can be classified to 5 types.
1. Single Inheritance
2. Multi-Level Inheritance
3. Hierarchical Inheritance
4. Hybrid Inheritance
5. Multiple Inheritance
2. 11
Single Inheritance
Multi-level Inheritance
Hierarchical Inheritance
Hybrid Inheritance
Any combination of single, hierarchical and multi-level inheritances is called as
hybrid inheritance.
Multiple Inheritance
When a derived class is created from more than one base class then that inheritance
is called as multiple inheritance. But multiple inheritance is not supported by .net
using classes and can be done using interfaces.
3. 12
Example program for Multiple Inheritance:
using System;
//Example Program for Multiple Inheritance
namespace ProgramCall {
interface Icar { int WheelsCount(); }
interface IPlane { String CanFly(); }
class SuperCar : Icar, IPlane { //Multiple Inheritance
public int WheelsCount() { return 4; }
public String CanFly() { return "true"; }
}
class Program{
static void Main(string[] args) {
SuperCar mysupercar = new SuperCar();
Console.WriteLine("My Super Car has " + mysupercar.WheelsCount() + " Wheels and can fly is " +
mysupercar.CanFly());
Console.ReadLine(); } } }
Output: My Super Car has 4 Wheels and can fly is true.
Polymorphism
Polymorphism means having more than one form
Overloading and overriding are used to implement polymorphism
Polymorphism is classified into 2 types:
1. Compile time polymorphism (aka, early binding or static binding)
2. Runtime polymorphism (aka, late binding or dynamic binding)
Compile time polymorphism
The polymorphism in which compiler identifies which polymorphic form it has to execute at
compile time itself is called as compile time polymorphism.
Examples of early binding are overloaded methods, overloaded operators
Note: Overloading allows creating several methods with the same name which differ in parameters.
Runtime polymorphism
The polymorphism in which compiler identifies which polymorphic form to execute at
runtime but not at compile time is called as runtime polymorphism
Example of late binding is overridden methods that are called using base class object.
Note: Overriding allows a subclass or child class to provide a specific implementation of a method that is
already provided by one of its superclasses or parent classes
Casting
Cast, in the context of C#, is a method by which a value is converted from one data type to
another. Cast is an explicit conversion by which the compiler is informed about the
conversion and the resulting possibility of data loss.
In C#, you can perform the following kinds of conversions:
Implicit conversions
Explicit Conversions
User-defined conversions
Conversions with helper classes
4. 13
Implicit Conversion
int num = 2147483647;
long bigNum = num;
Explicit Conversions
class Test{
static void Main() {
double x = 1234.7;
int a;
// Cast double to int.
a = (int)x;
System.Console.WriteLine(a);
}
}
// Output: 1234
Exception Handling
An exception is a problem that arises during the execution of a program.
A C# exception is a response to an exceptional circumstance that arises while a
program is running, such as an attempt to divide by zero
C# exception handling is built upon four keywords: try, catch, finally and throw.
try: A try block identifies a block of code for which particular exceptions will be
activated. It's followed by one or more catch blocks.
catch: A program catches an exception with an exception handler at the place
in a program where you want to handle the problem.
finally: The finally block is used to execute a given set of statements, whether
an exception is thrown or not thrown. For example, if you open a file, it must be
closed whether an exception is raised or not.
throw: A program throws an exception when a problem shows up. This is done
using a throw keyword.
try
{
// statements causing exception
}
catch( ExceptionName e1 )
{
// error handling code
}
catch( ExceptionName e2 )
{
// error handling code
}
catch( ExceptionName eN )
{
// error handling code
}
finally
{
// statements to be executed
}
5. 14
Garbage Collector
In the common language runtime (CLR), the garbage collector serves as an automatic
memory manager. It provides the following benefits:
Enables you to develop your application without having to free memory.
Allocates objects on the managed heap efficiently.
Reclaims objects that are no longer being used, clears their memory, and keeps the
memory available for future allocations. Managed objects automatically get clean
content to start with, so their constructors do not have to initialize every data field.
Provides memory safety by making sure that an object cannot use the content of
another object.
Note: Heap is the portion of memory where dynamically allocated memory resides
6. 15
Interfaces and Collections
Enumerator
Iterators in the .NET Framework are called "enumerators" and represented by the
IEnumerator interface.
An enumerator helps you enumerate (iterate) over a collection of items.
IEnumerator provides a MoveNext() method, which advances to the next element
and indicates whether the end of the collection has been reached.
A Current property, to obtain the value of the element currently being pointed at.
Otional Reset() method, to rewind the enumerator back to its initial position.
The enumerator initially points to a special value before the first element, so a call to
MoveNext() is required to begin iterating.
Enumerators are typically obtained by calling the GetEnumerator() method of an
object implementing the IEnumerable interface. Container classes typically
implement this interface.
Note: A container class is a class that is used to hold objects in memory or external storage
using System;
using System.Collections;
class car : IEnumerable
{
private car[] carr;
public string name;
public car(string s)
{ name = s; }
public car()
{
carr = new car[3];
carr[0] = new car("Benz");
carr[1] = new car("Audi");
carr[2] = new car("BMW");
}
public IEnumerator GetEnumerator() //to get Enumerator
{ return carr.GetEnumerator(); }
}
class enumInterface
{
public static void Main(string[] args)
{
car o = new car();
foreach (car c in o)
{
Console.WriteLine(c.name);
}
}
}
Output:
7. 16
Cloneable Objects
Objects in C# and in the CLR live on the heap and are accessed through references.
You’re not actually making a copy of the object when you assign one object variable to
another, as in the following code.
Object obj = new Object();
Object objCopy = obj;
After this code executes, objCopy doesn’t refer to a copy of obj; rather, you now
have two references to the same Object instance
Sometimes it makes sense to be able to make a copy of an object. For that purpose, the
Standard Library defines the ICloneable interface. When your object implements this
interface, it is saying that it supports the ability to have copies of itself made. In other
words, it claims that it can be used as a prototype to create new instances of objects.
Let’s have a look at the ICloneable interface:
public interface ICloneable
{
object Clone();
}
The interface only defines one method, Clone(), that returns an object reference. That
object reference is intended to be the copy.
The documentation for the interface doesn’t indicate whether the copy returned should
be a deep copy or a shallow copy. The documentation leaves it open for the class
designer to decide.
Shallow Copy: A shallow copy of an object creates a copy of the object whose contained
object references refer to the same objects as the prototype’s references.
Note: Prototype-based programming is a style of object-oriented programming in which behaviour reuse
(known as inheritance) is performed via a process of cloning existing objects that serve as prototypes
8. 17
Deep Copy: Deep copy creates a copy of the prototype where all of the contained objects
are copied as well. In a deep copy, the object containment tree is traversed all the way
down to the bottom and copies of each of those objects are made. Therefore, the result of a
deep copy shares no underlying objects with the prototype.
//Implementing Shallow clone using icloneable interface
using System;
public class one
{ public int a;}
public class two : ICloneable
{
public int b;
public one o = new one();
public two(int a1, int b1)
{
o.a = a1;
b = b1;
}
public void show() { Console.WriteLine(o.a + " " + b); }
public Object Clone()
{
return (this.MemberwiseClone());
}
}
class tth
{
public static void Main(string[] args)
{
two t1 = new two(1, 2);
two t2 = (two)t1.Clone();
t1.show();
t2.show();
t1.o.a = 5;
t1.b = 5;
t1.show();
t2.show();
}
}
Output:
9. 18
Comparable objects
The IComparable interface specifies a behavior that allows an object to be sorted based on
some specified key.
Aim: Implementing icomparable interface
using System;
class combo : IComparable
{
public int id;
public string name;
public combo(string name1, int x)
{
name = name1;
id = x;
}
public int CompareTo(object o)
{
combo temp = (combo)o;
if (this.id > temp.id)
return (1);
if (this.id < temp.id)
return (-1);
else
return (0);
}
}
class icomparable
{
public static void Main(string[] args)
{
combo[] c = new combo[3];
c[0] = new combo("Aan", 123);
c[1] = new combo("Bob", 14);
c[2] = new combo("Cop", 1);
Array.Sort(c);
foreach (combo m in c)
Console.WriteLine(m.name + "t" + m.id);
}
}
Output:
10. 19
Collections
We use an array when the number of elements that need to be inserted into the array is
known at compile time and it remains fixed throughout the execution of the program.
We can use a collection when we do not know how many elements we will need to
store. So basically a collection can grow in size dynamically. The data structure used to
implement the collection depends on the collection type.
A list is generally faster than an array if you must insert items at the beginning or in the
middle of the collection
public class TestCollections {
public static void TestList() {
List<string> sandwich = new List<string>( );
sandwich.Add("bacon");
sandwich.Add("tomato");
sandwich.Insert(1, "lettuce");
foreach (string ingredient in sandwich) {
System.Console.WriteLine(ingredient); } }
Output:
bacon
lettuce
tomato
Notice that unlike the Array class, items can be inserted into the middle of the list.
11. 20
Indexes
An indexer allows an object to be indexed like an array. When we define an indexer for a
class, this class behaves like a virtual array. We can then access the instance of this class
using the array access operator ([ ]).
A one dimensional indexer has the following syntax:
element-type this[int index]
{
// The get accessor.
get
{
// return the value specified by index
}
// The set accessor.
set
{
// set the value specified by index
}
}
12. 21
Overloading Operators
operator overloading is a specific case of polymorphism, where different operators have different
implementations depending on their arguments
Unary operator overloading:
using System;
namespace program1
{
class uniaryop {
public int a, b;
public uniaryop(int a1, int b1){
a = a1;
b = b1;
}
public void disp() {
Console.WriteLine(a);
Console.WriteLine(b);
}
public static uniaryop operator -(uniaryop ob) {
ob.a = -ob.a;
ob.b = -ob.b;
return ob;
}
}
class unaryoperator {
public static void Main(string[] args){
uniaryop u = new uniaryop(-10, -10);
u.disp();
uniaryop u1 = -u;
u1.disp();
Console.ReadKey(); } } }
Output:
-10
-10
10
10
13. 22
Binary Operator Overloading
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace binaryop
{
class binop
{
public int real, im;
public binop() { }
public binop(int r, int i)
{
real = r;
im = i;
}
public void disp() { Console.WriteLine(real + "+i" + im); }
public static binop operator +(binop ob1, binop ob2)
{
binop b = new binop();
b.real = ob1.real + ob2.real;
b.im = ob1.im + ob2.im;
return b;
}
}
class binaryoperator
{
public static void Main(string[] args)
{
binop b1 = new binop(15, 5);
b1.disp();
binop b2 = new binop(2, 12);
b2.disp();
binop b3 = b1 + b2;
b3.disp();
}
}
}
14. 23
Delegates
C# delegates are similar to pointers to functions, in C or C++. A delegate is a
reference type variable that holds the reference to a method. The reference can be
changed at runtime.
Delegates are especially used for implementing events and the call-back methods. All
delegates are implicitly derived from the System.Delegate class.
Syntax for delegate declaration is:
delegate <return type> <delegate-name> <parameter list>
public delegate double Delegate_Prod(int a,int b);
class Class1
{
static double fn_Prodvalues(int val1,int val2)
{
return val1*val2;
}
static void Main(string[] args)
{
//Creating the Delegate Instance
Delegate_Prod delObj = new Delegate_Prod(fn_Prodvalues); //delObj holds reference to Delegate_Prod
Console.Write("Please Enter Values");
int v1 = Int32.Parse(Console.ReadLine());
int v2 = Int32.Parse(Console.ReadLine());
//use a delegate for processing
double res = delObj(v1,v2);
Console.WriteLine ("Result :"+res);
Console.ReadLine();
}
}
Events
An event in C# is a way for a class to provide notifications to clients of that class when some
interesting thing happens to an object. The most familiar use for events is in graphical user
interfaces; typically, the classes that represent controls in the interface have events that are
notified when the user does something to the control (for example, click a button).
Declaring an event “ To declare an event inside a class, first a delegate type for the event
must be declared, if none is already declared.
public delegate void ChangedEventHandler(object sender, EventArgs e);
Next, the event itself is declared.
public event ChangedEventHandler Changed;
Invoking an event
if (Changed != null)
Changed(this, e);
15. 24
XML Documentation
C# provides a mechanism for developers to document their code using XML. In source code
files, lines that begin with /// and that precede a user-defined type such as a class, delegate,
or interface; a member such as a field, event, property, or method; or a namespace
declaration can be processed as comments and placed in a file.
Code Discussion
XML documentation starts with ///. When you create a new project, the wizards put some
starter /// lines in for you. The processing of these comments has some restrictions:
The documentation must be well-formed XML. If the XML is not well-formed, a
warning is generated and the documentation file will contain a comment saying that an
error was encountered. For more information on well-formed XML, see XML Glossary.
Developers are free to create their own set of tags. There is a recommended set of tags
(see the Further Reading section). Some of the recommended tags have special
meanings:
The <param> tag is used to describe parameters. If used, the compiler will
verify that the parameter exists and that all parameters are described in the
documentation. If the verification failed, the compiler issues a warning.
The cref attribute can be attached to any tag to provide a reference to a code
element. The compiler will verify that this code element exists. If the
verification failed, the compiler issues a warning. The compiler also respects
any using statements when looking for a type described in the cref attribute.
The <summary> tag is used by IntelliSense inside Visual Studio to display
additional information about a type or member.