SlideShare a Scribd company logo
1 of 74
In this article we will discuss key concepts of object orientation with their
practical implementation in C#. We will discuss here basics of OOPS
including Interfaces, Access Modifiers, inheritance, polymorphism etc. This is
my second article on csharp-corner.com.
My first article was "Memory Management in .NET",
you can find this article at http://www.c-sharpcorner.com/
UploadFile/tkagarwal/
MemoryManagementInNet11232005064832AM/
MemoryManagementInNet.aspx
Key Concepts of Object Orientation
Abstraction
Encapsulation
Polymorphism
Inheritance.
Abstraction is the ability to generalize an object as a data type that has a
specific set of characteristics and is able to perform a set of actions.
Object-oriented languages provide abstraction via classes. Classes define the
properties and methods of an object type.
Examples:
You can create an abstraction of a dog with characteristics, such as
color, height, and weight, and actions such as run and bite. The
characteristics are called properties, and the actions are called
methods.
A Recordset object is an abstract representation of a set of data.
Classes are blueprints for Object.
Objects are instance of classes.
C# Example of Class:
public class Draw
{
// Class code.
}
Object References
When we work with an object we are using a reference to that object. On the
other hand, when we are working with simple data types such as Integer, we
are working with the actual value rather than a reference.
When we create a new object using the New keyword, we store a reference
to that object in a variable. For instance:
Draw MyDraw = new Draw;
This code creates a new instance of Draw. We gain access to this new object
via the MyDraw variable. This variable holds a reference to the object.
Now we have a second variable, which also has a reference to that same
object. We can use either variable interchangeably, since they both
reference the exact same object. The thing we need to remember is that the
variable we have is not the object itself but, rather, is just a reference or
pointer to the object itself.
Early binding means that our code directly interacts with the object, by
directly calling its methods. Since the compiler knows the object's data type
ahead of time, it can directly compile code to invoke the methods on the
object. Early binding also allows the IDE to use IntelliSense to aid our
development efforts; it allows the compiler to ensure that we are referencing
methods that do exist and that we are providing the proper parameter
values.
Late binding means that our code interacts with an object dynamically at
run-time. This provides a great deal of flexibility since our code literally
doesn't care what type of object it is interacting with as long as the object
supports the methods we want to call. Because the type of the object isn't
known by the IDE or compiler, neither IntelliSense nor compile-time syntax
checking is possible but we get unprecedented flexibility in exchange.
If we enable strict type checking by using Option Strict On at the top of our
code modules, then the IDE and compiler will enforce early binding behavior.
By default, Option Strict is turned off and so we have easy access to the use
of late binding within our code.
Access Modifiers
Access Modifiers are keywords used to specify the declared accessibility of a
member of a type.
Public is visible to everyone. A public member can be accessed using an
instance of a class, by a class's internal code, and by any descendants of a
class.
Private is hidden and usable only by the class itself. No code using a class
instance can access a private member directly and neither can a descendant
class.
Protected members are similar to private ones in that they are accessible
only by the containing class. However, protected members also may be used
by a descendant class. So members that are likely to be needed by a
descendant class should be marked protected.
Internal/Friend is public to the entire application but private to any
outside applications. Internal is useful when you want to allow a class to be
used by other applications but reserve special functionality for the
application that contains the class. Internal is used by C# and Friend by VB
.NET.
Protected Internal may be accessed only by a descendant class that's
contained in the same application as its base class. You use protected
internal in situations where you want to deny access to parts of a class
functionality to any descendant classes found in other applications.
Composition of an OBJECT
We use an interface to get access to an object's data and behavior. The
object's data and behaviors are contained within the object, so a client
application can treat the object like a black box accessible only through its
interface. This is a key object-oriented concept called Encapsulation. The
idea is that any programs that make use of this object won't have direct
access to the behaviors or data-but rather those programs must make use of
our object's interface.
There are three main parts of Object:
1. Interface
2. Implementation or Behavior
3. Member or Instance variables
Interface
The interface is defined as a set of methods (Sub and Function routines),
properties (Property routines), events, and fields (variables or attributes)
that are declared Public in scope.
Implementation or Behavior
The code inside of a method is called the implementation. Sometimes it is
also called behavior since it is this code that actually makes the object do
useful work.
Client applications can use our object even if we change the implementation-
as long as we don't change the interface. As long as our method name and
its parameter list and return data type remain unchanged, we can change
the implementation all we want.
So Method Signature depends on:
Method name
Data types of parameters
Either Parameter is passed ByVal or ByRef.
Return type of method.
It is important to keep in mind that encapsulation is a syntactic tool-it allows
our code to continue to run without change. However, it is not semantic-
meaning that, just because our code continues to run, doesn't mean it
continues to do what we actually wanted it to do.
Member or Instance Variables
The third key part of an object is its data, or state. Every instance of a class
is absolutely identical in terms of its interface and its implementation-the
only thing that can vary at all is the data contained within that particular
object.
Member variables are those declared so that they are available to all code
within our class. Typically member variables are Private in scope-available
only to the code in our class itself. They are also sometimes referred to as
instance variables or as attributes. The .NET Framework also refers to them
as fields.
We shouldn't confuse instance variables with properties. A Property is a type
of method that is geared around retrieving and setting values, while an
instance variable is a variable within the class that may hold the value
exposed by a Property.
Interface looks like a class, but has no implementation.
The only thing it contains is definitions of events, indexers, methods and/or
properties. The reason interfaces only provide definitions is because they are
inherited by classes and structs, which must provide an implementation for
each interface member defined. So, what are interfaces good for if they
don't implement functionality? They're great for putting together plug-n-play
like architectures where components can be interchanged at will. Since all
interchangeable components implement the same interface, they can be
used without any extra programming. The interface forces each component
to expose specific public members that will be used in a certain way.
Because interfaces must be defined by inheriting classes and structs, they
define a contract. For instance, if class foo inherits from the IDisposable
interface, it is making a statement that it guarantees it has the Dispose()
method, which is the only member of the IDisposable interface. Any code
that wishes to use class foo may check to see if class foo inherits
IDisposable. When the answer is true, then the code knows that it can call
foo.Dispose().
Defining an Interface: MyInterface.c
interface IMyInterface
{
void MethodToImplement();
}
Above listing shows defines an interface named IMyInterface. A common
naming convention is to prefix all interface names with a capital "I", but this
is not mandatory. This interface has a single method named
MethodToImplement(). This could have been any type of method declaration
with different parameters and return types. Notice that this method does not
have an implementation (instructions between curly braces- {}), but instead
ends with a semi-colon, ";". This is because the interface only specifies the
signature of methods that an inheriting class or struct must implement.
All the methods of Interface are public by default and no access modifiers
(like private, public) are allowed with any method of Interface.
Using an Interface: InterfaceImplementer.cs
class InterfaceImplementer : IMyInterface
{
public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
}
The InterfaceImplementer class in above listing implements the IMyInterface
interface. Indicating that a class inherits an interface is the same as
inheriting a class. In this case, the following syntax is used:
class InterfaceImplementer : IMyInterface
Note that this class inherits the IMyInterface interface; it must implement its
all members. While implementing interface methods all those needs to be
declared public only. It does this by implementing the MethodToImplement()
method. Notice that this method implementation has the exact same
signature, parameters and method name, as defined in the IMyInterface
interface. Any difference will cause a compiler error. Interfaces may also
inherit other interfaces. Following listing shows how inherited interfaces are
implemented.
Interface Inheritance: InterfaceInheritance.cs
using System;
interface IParentInterface
{
void ParentInterfaceMethod();
}
interface IMyInterface : IParentInterface
{
void MethodToImplement();
}
class InterfaceImplementer : IMyInterface
{
public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
public void ParentInterfaceMethod()
{
Console.WriteLine("ParentInterfaceMethod() called.");
}
}
The code in above listing contains two interfaces: IMyInterface and the
interface it inherits, IParentInterface. When one interface inherits another,
any implementing class or struct must implement every interface member in
the entire inheritance chain. Since the InterfaceImplementer class in above
listing inherits from IMyInterface, it also inherits IParentInterface. Therefore,
the InterfaceImplementer class must implement the MethodToImplement()
method specified in the IMyInterface interface and the
ParentInterfaceMethod() method specified in the IParentInterface interface.
In summary, you can implement an interface and use it in a class. Interfaces
may also be inherited by other interface. Any class or struct that inherits an
interface must also implement all members in the entire interface
inheritance chain.
Inheritance is the idea that one class, called a subclass, can be based on
another class, called a base class. Inheritance provides a mechanism for
creating hierarchies of objects.
Inheritance is the ability to apply another class's interface and code to your
own class.
Normal base classes may be instantiated themselves, or inherited. Derived
classes can inherit base class members marked with protected or greater
access. The derived class is specialized to provide more functionality, in
addition to what its base class provides. Inheriting base class members in
derived class is not mandatory.
Access Keywords
base -> Access the members of the base class.
this -> Refer to the current object for which a method is called.
The base keyword is used to access members of the base class from within a
derived class:
Call a method on the base class that has been overridden by another
method. Specify which base-class constructor should be called when creating
instances of the derived class. A base class access is permitted only in a
constructor, an instance method, or an instance property accessor.
In following example, both the base class, Person, and the derived class,
Employee, have a method named Getinfo. By using the base keyword, it is
possible to call the Getinfo method on the base class, from within the
derived class.
// Accessing base class members
using System;
public class Person
{
protected string ssn = "444-55-6666";
protected string name = "John L. Malgraine";
public virtual void GetInfo()
{
Console.WriteLine("Name: {0}", name);
Console.WriteLine("SSN: {0}", ssn);
}
}
class Employee: Person
{
public string id = "ABC567EFG";
public override void GetInfo()
{
// Calling the base class GetInfo method:
base.GetInfo();
Console.WriteLine("Employee ID: {0}", id);
}
}
class TestClass
{
public static void Main()
{
Employee E = new Employee();
E.GetInfo();
}
}
Output
Name: John L. Malgraine
SSN: 444-55-6666
Employee ID: ABC567EFG
Base class constructors can be called from derived classes. To call a base
class constructor, use the base() constructor reference. This is desirable
when it's necessary to initialize a base class appropriately.
Here's an example that shows the derived class constructor with an address
parameter:
abstract public class Contact
{
private string address;
public Contact(string b_address)
{
this.address = b_address;
}
}
public class Customer : Contact
{
public Customer(string c_address) : base(C_address)
{
}
}
In this code, the Customer class does not have an address, so it passes the
parameter to its base class constructor by adding a colon and the base
keyword with the parameter to its declaration. This calls the Contact
constructor with the address parameter, where the address field in Contact
is initialized.
One more example which shows how base-class constructor is called when
creating instances of a derived class:
using System;
public class MyBase
{
int num;
public MyBase()
{
Console.WriteLine("In MyBase()");
}
public MyBase(int i)
{
num = i;
Console.WriteLine("in MyBase(int i)");
}
public int GetNum()
{
return num;
}
}
public class MyDerived : MyBase
{
static int i = 32;
// This constructor will call MyBase.MyBase()
public MyDerived(int ii) : base()
{
}
// This constructor will call MyBase.MyBase(int i)
public MyDerived() : base(i)
{
}
public static void Main()
{
MyDerived md = new MyDerived(); // calls public MyDerived() : base(i) and
// passes i=32 in base class
MyDerived md1 = new MyDerived(1); // call public MyDerived() : base(i)
}
}
Output
in MyBase(int i)
in MyBase()
The following example will not compile. It illustrates the effects of not
including a default constructor in a class definition:
abstract public class Contact
{
private string address;
public Contact(string address)
{
this.address = address;
}
}
public class Customer : Contact
{
public Customer(string address)
{
}
}
In this example, the Customer constructor does not call the base class
constructor. This is obviously a bug, since the address field will never be
initialized.
When a class has no explicit constructor, the system assigns a default
constructor. The default constructor automatically calls a default or
parameterless base constructor. Here's an example of automatic default
constructor generation that would occur for the preceding example:
public Customer() : Contact()
{
}
When a class does not declare any constructors, the code in this example is
automatically generated. The default base class constructor is called
implicitly when no derived class constructors are defined. Once a derived
class constructor is defined, whether or not it has parameters, a default
constructor will not be automatically defined, as the preceding code showed.
Calling Base Class Members
Derived classes can access the members of their base class if those
members have protected or greater access. Simply use the member name in
the appropriate context, just as if that member were a part of the derived
class itself. Here's an example:
abstract public class Contact
{
private string address;
private string city;
private string state;
private string zip;
public string FullAddress()
{
string fullAddress = address + 'n' + city + ',' + state + ' ' + zip;
return fullAddress;
}
}
public class Customer : Contact
{
public string GenerateReport()
{
string fullAddress = FullAddress();
// do some other stuff...
return fullAddress;
}
}
In above example, the GenerateReport() method of the Customer class calls
the FullAddress() method in its base class, Contact. All classes have full
access to their own members without qualification. Qualification refers to
using a class name with the dot operator to access a class member-
MyObject.SomeMethod(), for instance. This shows that a derived class can
access its base class members in the same manner as its own.
More Tips regarding Inheritance:
A static member cannot be marked as override, virtual, or abstract. So
following is an error:
public static virtual void GetSSN()
You can't call static methods of base class from derived class using
base keyword.
In above example if you declare a static method as follows:
public class Person
{
protected string ssn = "444-55-6666";
protected string name = "John L. Malgraine";
public static void GetInfo()
{
// Implementation
}
}
now you can't call this method using base.GetInfo() from derived class
instead you have to call Person.GetInfo() from derived class.
Inside Static members we can access only static fields, methods etc.
Following example will give error, because we can't access name in GetInfo()
because name is not static.
public class Person
{
protected string ssn = "444-55-6666";
protected string name = "John L. Malgraine";
public static void GetInfo()
{
Console.WriteLine("Name: {0}", name);
Console.WriteLine("SSN: {0}", ssn);
}
}
Virtual or abstract members cannot be private.
If you are not overriding a virtual method of base class in derived
class, you can't use base class method by using base keyword in
derived class. Also when you will create an instance of derived class, it
will call derived class method and you will only be able to access base
class method when you will create instance of base class.
You can't decrease access level of a method in derived class when you
are overriding a base class method in derived class, vice versa is
possible.
Means you can make protected method of base class to public in
derived class.
The "this" keyword refers to:
the current instance for which a method is called. Static member
functions do not have a this pointer. The this keyword can be used to
access members from within constructors, instance methods, and
instance accessors.
The following are common uses of this:
To qualify members hidden by similar names, for example:
public Employee(string name, string alias)
{
this.name = name;
this.alias = alias;
}
In above example, this.name refers to private variable name in the class. If
we write name = name, then this will refer to argument name of the
constructor Employee and not to private variable name in the class. In this
case private variable name will never be initialized.
To pass an object as a parameter to other methods, for example:
CalcTax(this);
To declare indexers, for example:
public int this [int param]
{
get
{
return array[param];
}
set
{
array[param] = value;
}
}
It is an error to refer to this in a static method, static property accessor, or
variable initializer of a field declaration.
In this example, this is used to qualify the Employee class members, name
and alias, which are hidden by similar names. It is also used to pass an
object to the method CalcTax, which belongs to another class.
// keywords_this.cs
// this example
using System;
public class Employee
{
public string name;
public string alias;
public decimal salary = 3000.00m;
// Constructor:
public Employee(string name, string alias)
{
// Use this to qualify the fields, name and alias:
this.name = name;
this.alias = alias;
}
// Printing method:
public void printEmployee()
{
Console.WriteLine("Name: {0}nAlias: {1}", name, alias);
// Passing the object to the CalcTax method by using this:
Console.WriteLine("Taxes: {0:C}", Tax.CalcTax(this));
}
}
public class Tax
{
public static decimal CalcTax(Employee E)
{
return (0.08m*(E.salary));
}
}
public class MainClass
{
public static void Main()
{
// Create objects:
Employee E1 = new Employee ("John M. Trainer", "jtrainer");
// Display results:
E1.printEmployee();
}
}
Output
Name: John M. Trainer
Alias: jtrainer
Taxes: $240.00
Abstract Classes
Abstract classes are a special type of base classes. In addition to normal
class members, they have abstract class members. These Abstract class
members are methods and properties that are declared without an
implementation. All classes derived directly from abstract classes must
implement all of these abstract methods and properties.
Abstract classes can never be instantiated. This would be illogical, because
of the members without implementations.So what good is a class that can't
be instantiated? Lots! Abstract classes sit toward the top of a class
hierarchy. They establish structure and meaning to code. They make
frameworks easier to build. This is possible because abstract classes have
information and behavior common to all derived classes in a framework.
Take a look at the following example:
abstract public class Contact // Abstract Class Contact.
{
protected string name;
public Contact()
{
// statements...
}
public abstract void generateReport();
abstract public string Name
{
get;
set;
}
}
Contact, is an abstract class. Contact has two abstract members, and it has
an abstract method named generateReport(). This method is declared with
the abstract modifier in front of the method declaration. It has no
implementation (no braces) and is terminated with a semicolon. The Name
property is also declared abstract. The accessors of properties are
terminated with semicolons.
public class Customer : Contact // Customer Inherits Abstract Class Contact.
{
string gender;
decimal income;
int numberOfVisits;
public Customer()
{
// statements
}
public override void generateReport()
{
// unique report
}
public override string Name
{
get
{
numberOfVisits++;
return name;
}
set
{
name = value;
numberOfVisits = 0;
}
}
}
public class SiteOwner : Contact
{
int siteHits;
string mySite;
public SiteOwner()
{
// statements
}
public override void generateReport()
{
// unique report
}
public override string Name
{
get
{
siteHits++;
return name;
}
set
{
name = value;
siteHits = 0;
}
}
}
The abstract base class Contact has two derived classes, Customer and
SiteOwner. Both of these derived classes implement the abstract members
of the Contact class. The generateReport() method in each derived class has
an override modifier in its declaration. Likewise, the Name declaration
contains an override modifier in both Customer and SiteOwner.
C# requires explicit declaration of intent when overriding methods. This
feature promotes safe code by avoiding the accidental overriding of base
class methods, which is what actually does happen in other languages.
Leaving out the override modifier generates an error. Similarly, adding a
new modifier also generates an error. Abstract methods must be overridden
and cannot be hidden, which the new modifier or the lack of a modifier
would be trying to do.
The most famous of all abstract classes is the Object class. It may be
referred to as object or Object, but it's still the same class. Object is the
base class for all other classes in C#. It's also the default base class when a
base class is not specified. The following class declarations produce the same
exact results:
abstract public class Contact : Object
{
// class members
}
abstract public class Contact
{
// class members
}
Object is implicitly included as a base class if it is not already declared.
Besides providing the abstract glue to hold together the C# class framework,
object includes built-in functionality, some of which is useful for derived
classes to implement.
Difference between Interface and Abstract Class
Interfaces are closely related to abstract classes that have all
members abstract.
For an abstract class, at least one method of the class must be an
abstract method that means it may have concrete methods.
For an interface, all the methods must be abstract
Class that implements an interface much provide concrete
implementation of all the methods definition in an interface or else
must be declare an abstract class
In C#, multiple inheritance is possible only through implementation of
multiple interfaces. Abstract class can only be derived once.
An interface defines a contract and can only contains four entities viz
methods, properties, events and indexes. An interface thus cannot
contain constants, fields, operators, constructors, destructors, static
constructors, or types.
Also an interface cannot contain static members of any kind. The
modifiers abstract, public, protected, internal, private, virtual, override
is disallowed, as they make no sense in this context.
Class members that implement the interface members must be
publicly accessible.
Overriding Summery:
A derived class may override a virtual method of the base class with the
keyword override. The following restrictions must be followed.
Keyword override is used in the definition of child class method that is
going to override the base class's virtual method.
The return type must be the same as the virtual method have in base
class.
The name of the method should also be same.
The parameter-list must also be same in order, number and type of
parameters.
The accessibility of the overriding method should not be more
restricted than that of the accessibility defined with virtual method of
the base class. This accessibility either be the same or less restricted.
The virtual methods can be sealed in the child or derived classes to
prevent further modifications in the implementation of the virtual
method in the derived classes, by declaring them as sealed methods.
Hiding Base Class Members
Sometimes derived class members have the same name as a corresponding
base class member. In this case, the derived member is said to be "hiding"
the base class member.
When hiding occurs, the derived member is masking the functionality of the
base class member. Users of the derived class won't be able to see the
hidden member; they'll see only the derived class member. The following
code shows how hiding a base class member works.
abstract public class Contact
{
private string address;
private string city;
private string state;
private string zip;
public string FullAddress()
{
string fullAddress =address + 'n' +city + ',' + state + ' ' + zip;
return fullAddress;
}
}
public class SiteOwner : Contact
{
public string FullAddress()
{
string fullAddress;
// create an address...
return fullAddress;
}
}
In this example, both SiteOwner and its base class, Contact, have a method
named FullAddress(). The FullAddress() method in the SiteOwner class hides
the FullAddress() method in the Contact class. This means that when an
instance of a SiteOwner class is invoked with a call to the FullAddress()
method, it is the SiteOwner class FullAddress() method that is called, not the
FullAddress() method of the Contact class.
Although a base class member may be hidden, the derived class can still
access it. It does this through the base identifier. Sometimes this is
desirable. It is often useful to take advantage of the base class functionality
and then add to it with the derived class code. The next example shows how
to refer to a base class method from the derived class.
abstract public class Contact
{
private string address;
private string city;
private string state;
private string zip;
public string FullAddress()
{
string fullAddress =address + 'n' +city + ',' + state + ' ' + zip;
return fullAddress;
}
}
public class SiteOwner : Contact
{
public string FullAddress()
{
string fullAddress = base.FullAddress();
// do some other stuff...
return fullAddress;
}
}
In this particular example, the FullAddress() method of the Contact class is
called from within the FullAddress() method of the SiteOwner class. This is
accomplished with a base class reference. This provides another way to
reuse code and add on to it with customized behavior.
Versioning
Versioning, in the context of inheritance, is a C# mechanism that allows
modification of classes (creating new versions) without accidentally changing
the meaning of the code. Hiding a base class member with the methods
previously described generates a warning message from the compiler. This is
because of the C# versioning policy. It's designed to eliminate a class of
problems associated with modifications to base classes.
Here's the scenario: A developer creates a class that inherits from a third-
party library. For the purposes of this discussion, we assume that the
Contact class represents the third-party library. Here's the example:
public class Contact
{
// does not include FullAddress() method
}
public class SiteOwner : Contact
{
public string FullAddress()
{
string fullAddress = mySite.ToString();
return fullAddress;
}
}
In this example, the FullAddress() method does not exist in the base class.
There is no problem yet. Later on, the creators of the third-party library
update their code. Part of this update includes a new member in a base class
with the exact same name as the derived class:
public class Contact
{
private string address;
private string city;
private string state;
private string zip;
public string FullAddress()
{
string fullAddress =address + 'n' +city + ',' + state + ' ' + zip;
return fullAddress;
}
}
public class SiteOwner : Contact
{
public string FullAddress()
{
string fullAddress = mySite.ToString();
return fullAddress;
}
}
In this code, the base class method FullAddress() contains different
functionality than the derived class method. In other languages, this
scenario would break the code because of implicit polymorphism. However,
this does not break any code in C# because when the FullAddress() method
is called on SiteOwner, it is still the SiteOwner class method that gets called.
This scenario generates a warning message. One way to eliminate the
warning message is to place a new modifier in front of the derived class
method name, as the following example shows:
using System;
public class WebSite
{
public string SiteName;
public string URL;
public string Description;
public WebSite()
{
}
public WebSite( string strSiteName, string strURL, string strDescription )
{
SiteName = strSiteName;
URL = strURL;
Description = strDescription;
}
public override string ToString()
{
return SiteName + ", " +URL + ", " +Description;
}
}
public class Contact
{
public string address;
public string city;
public string state;
public string zip;
public string FullAddress()
{
string fullAddress =address + 'n' +city + ',' + state + ' ' + zip;
return fullAddress;
}
}
public class SiteOwner : Contact
{
int siteHits;
string name;
WebSite mySite;
public SiteOwner()
{
mySite = new WebSite();
siteHits = 0;
}
public SiteOwner(string aName, WebSite aSite)
{
mySite = new WebSite(aSite.SiteName,aSite.URL,aSite.Description);
Name = aName;
}
new public string FullAddress()
{
string fullAddress = mySite.ToString();
return fullAddress;
}
public string Name
{
get
{
siteHits++;
return name;
}
set
{
name = value;
siteHits = 0;
}
}
}
public class Test
{
public static void Main()
{
WebSite mySite = new WebSite("Le Financier","http://www.LeFinancier.com","Fancy
Financial Site");
SiteOwner anOwner = new SiteOwner("John Doe", mySite);
string address;
anOwner.address = "123 Lane Lane";
anOwner.city = "Some Town";
anOwner.state = "HI";
anOwner.zip = "45678";
address = anOwner.FullAddress(); // Different Results
Console.WriteLine("Address: n{0}n", address);
}
}
Here's the output:
Address:
Le Financier, http://www.LeFinancier.com, Fancy Financial Site
This has the effect of explicitly letting the compiler know the developer's
intent. Placing the new modifier in front of the derived class member states
that the developers know there is a base class method with the same name,
and they definitely want to hide that member. This prevents breakage of
existing code that depends on the implementation of the derived class
member. With C#, the method in the derived class is called when an object
of the derived class type is used. Likewise, the method in the base class is
called when an object of the Base class type is called. Another problem this
presents is that the base class may present some desirable new features
that wouldn't be available through the derived class.
To use these new features requires one of a few different workarounds. One
option would be to rename the derived class member, which would allow
programs to use a base class method through a derived class member. The
drawback to this option would be if there were other classes relying upon the
implementation of the derived class member with the same name. This
scenario will break code and, for this reason, is considered extremely bad
form.
Another option is to define a new method in the derived class that called the
base class method. This allows users of the derived class to have the new
functionality of the base class, yet retain their existing functionality with the
derived class. While this would work, there are maintainability concerns for
the derived class.
Sealed Classes
Sealed classes are classes that can't be derived from. To prevent other
classes from inheriting from a class, make it a sealed class. There are a
couple good reasons to create sealed classes, including optimization and
security.
Sealing a class avoids the system overhead associated with virtual methods.
This allows the compiler to perform certain optimizations that are otherwise
unavailable with normal classes.
Another good reason to seal a class is for security. Inheritance, by its very
nature, dictates a certain amount of protected access to the internals of a
potential base class. Sealing a class does away with the possibility of
corruption by derived classes. A good example of a sealed class is the String
class. The following example shows how to create a sealed class:
public sealed class CustomerStats
{
string gender;
decimal income;
int numberOfVisits;
public CustomerStats()
{
}
}
public class CustomerInfo : CustomerStats // error
{
}
This example generates a compiler error. Since the CustomerStats class is
sealed, it can't be inherited by the CustomerInfo class.The CustomerStats
class was meant to be used as an encapsulated object in another class. This
is shown by the declaration of a CustomerStats object in the Customer class.
public class Customer
{
CustomerStats myStats; // okay
}
Polymorphism
Polymorphism is reflected in the ability to write one routine that can operate
on objects from more than one class-treating different objects from different
classes in exactly the same way. For instance, if both Customer and Vendor
objects have a Name property, and we can write a routine that calls the
Name property regardless of whether we're using a Customer or Vendor
object, then we have polymorphism.
A vehicle is a good example of polymorphism. A vehicle interface would only
have those properties and methods that all vehicles have, a few of which
might include paint color, number of doors, accelerator, and ignition. These
properties and methods would apply to all types of vehicles including cars,
trucks, and semi-trucks.
Polymorphism will not implement code behind the vehicle's properties and
methods. Instead, polymorphism is the implementation of an interface. If
the car, truck, and semitruck all implement the same vehicle interface, then
the client code for all three classes can be exactly the same.
C# gives us polymorphism through inheritance. C# provides a keyword
virtual that is used in the definition of a method to support polymorphism.
Child class are now free to provide their own implementation of this virtual
method, that is called overriding. The following points are important
regarding virtual keyword:-
If the method is not virtual, the compiler simply uses the reference type to
invoke the appropriate method.
If the method is virtual, the compiler will generate code to checkup the
reference type at runtime it is actually denoting to, then the appropriate
method is called from the class of the reference type.
When a virtual method is called, runtime check (late method binding) is
made to identify the object and appropriate method is invoked, all this is
done at runtime.
In case of non-virtual methods, this information is available at compile time,
so no runtime check to identify the object is made, so slightly efficient in the
way non-virtual methods are called. But the behavior of virtual method is
useful in many ways; the functionality they provide is fair enough to bear
this slight loss of performance.
Implementing Polymorphism
The key factor here is the ability to dynamically invoke methods in a class
based on their type. Essentially, a program would have a group of objects,
examine the type of each one, and execute the appropriate method. Here's
an example:
using System;
public class WebSite
{
public string SiteName;
public string URL;
public string Description;
public WebSite()
{
}
public WebSite( string strSiteName, string strURL, string strDescription )
{
SiteName = strSiteName;
URL = strURL;
Description = strDescription;
}
public override string ToString()
{
return SiteName + ", " +URL + ", " +Description;
}
}
When we inherit above class, we have two choices to invoke constructor of
the class. So this is an example of design time polymorphism. Here at design
time we have to decide which method we need to invoke while inheriting the
class.
Polymorphism is the capability of a program to carry out dynamic operations
by implementing methods of multiple derived classes through a common
base class reference. Another definition of polymorphism is the ability to
treat different objects the same way. This means that the runtime type of an
object determines its behavior rather than the compile-time type of its
reference.
Resource Allocation
The Microsoft .NET common language runtime requires that all resources be
allocated from the managed heap. Objects are automatically freed when
they are no longer needed by the application.
When a process is initialized, the runtime reserves a contiguous region of
address space that initially has no storage allocated for it. This address
space region is the managed heap. The heap also maintains a pointer. This
pointer indicates where the next object is to be allocated within the heap.
Initially, the pointer is set to the base address of the reserved address space
region.
An application creates an object using the new operator. This operator first
makes sure that the bytes required by the new object fit in the reserved
region (committing storage if necessary). If the object fits, then pointer
points to the object in the heap, this object's constructor is called, and the
new operator returns the address of the object.
Above fig shows a managed heap consisting of three objects: A, B, and C.
The next object to be allocated will be placed where NextObjPtr points
(immediately after object C).
When an application calls the new operator to create an object, there may
not be enough address space left in the region to allocate to the object. The
heap detects this by adding the size of the new object to NextObjPtr. If
NextObjPtr is beyond the end of the address space region, then the heap is
full and a collection must be performed.
In reality, a collection occurs when generation 0 is completely full. Briefly, a
generation is a mechanism implemented by the garbage collector in order to
improve performance. The idea is that newly created objects are part of a
young generation, and objects created early in the application's lifecycle are
in an old generation. Separating objects into generations can allow the
garbage collector to collect specific generations instead of collecting all
objects in the managed heap.
The Garbage Collection Algorithm
The garbage collector checks to see if there are any objects in the heap that
are no longer being used by the application. If such objects exist, then the
memory used by these objects can be reclaimed. (If no more memory is
available for the heap, then the new operator throws an
OutOfMemoryException.)
Every application has a set of roots. Roots identify storage locations, which
refer to objects on the managed heap or to objects that are set to null. For
example, all the global and static object pointers in an application are
considered part of the application's roots. In addition, any local
variable/parameter object pointers on a thread's stack are considered part of
the application's roots. Finally, any CPU registers containing pointers to
objects in the managed heap are also considered part of the application's
roots. The list of active roots is maintained by the just-in-time (JIT) compiler
and common language runtime, and is made accessible to the garbage
collector's algorithm.
When the garbage collector starts running, it makes the assumption that all
objects in the heap are garbage. In other words, it assumes that none of the
application's roots refer to any objects in the heap. Now, the garbage
collector starts walking the roots and building a graph of all objects
reachable from the roots. For example, the garbage collector may locate a
global variable that points to an object in the heap.
Following fig shows a heap with several allocated objects where the
application's roots refer directly to objects A, C, D, and F. All of these objects
become part of the graph. When adding object D, the collector notices that
this object refers to object H, and object H is also added to the graph. The
collector continues to walk through all reachable objects recursively.
Once this part of the graph is complete, the garbage collector checks the
next root and walks the objects again. As the garbage collector walks from
object to object, if it attempts to add an object to the graph that it
previously added, then the garbage collector can stop walking down that
path. This serves two purposes. First, it helps performance significantly since
it doesn't walk through a set of objects more than once. Second, it prevents
infinite loops should you have any circular linked lists of objects.
Once all the roots have been checked, the garbage collector's graph contains
the set of all objects that are somehow reachable from the application's
roots; any objects that are not in the graph are not accessible by the
application, and are therefore considered garbage.
The garbage collector now walks through the heap linearly, looking for
contiguous blocks of garbage objects (now considered free space). The
garbage collector then shifts the non-garbage objects down in memory
(using the standard memcpy function), removing all of the gaps in the heap.
Of course, moving the objects in memory invalidates all pointers to the
objects. So the garbage collector must modify the application's roots so that
the pointers point to the objects' new locations. In addition, if any object
contains a pointer to another object, the garbage collector is responsible for
correcting these pointers as well.
Following fig shows the managed heap after a collection.
After all the garbage has been identified, all the non-garbage has been
compacted, and all the non-garbage pointers have been fixed-up, the
NextObjPtr is positioned just after the last non-garbage object. At this point,
the new operation is tried again and the resource requested by the
application is successfully created.
GC generates a significant performance hit, and this is the major downside
of using a managed heap. However, keep in mind that GCs only occur when
the heap is full and, until then, the managed heap is significantly faster than
a C-runtime heap. The runtime's garbage collector also offers some
optimizations using Generations that greatly improve the performance of
garbage collection.
You no longer have to implement any code that manages the lifetime of any
resources that your application uses. Now it is not possible to leak resources,
since any resource not accessible from your application's roots can be
collected at some point. Also it is not possible to access a resource that is
freed, since the resource won't be freed if it is reachable. If it's not
reachable, then your application has no way to access it.
Following code demonstrates how resources are allocated and managed:
class Application
{
public static int Main(String[] args)
{
// ArrayList object created in heap, myArray is now in root
ArrayList myArray = new ArrayList();
// Create 10000 objects in the heap
for (int x = 0; x < 10000; x++)
{
myArray.Add(new Object()); // Object object created in heap
}
// Right now, myArray is a root (on the thread's stack). So,
// myArray is reachable and the 10000 objects it points to are also reachable.
Console.WriteLine(myArray.Count);
// After the last reference to myArray in the code, myArray is not a root.
// Note that the method doesn't have to return, the JIT compiler knows
// to make myArray not a root after the last reference to it in the code.
// Since myArray is not a root, all 10001 objects are not reachable
// and are considered garbage. However, the objects are not
// collected until a GC is performed.
}
}
If GC is so great, you might be wondering why it isn't in ANSI C++. The
reason is that a garbage collector must be able to identify an application's
roots and must also be able to find all object pointers. The problem with
C++ is that it allows casting a pointer from one type to another, and there's
no way to know what a pointer refers to. In the common language runtime,
the managed heap always knows the actual type of an object, and the
metadata information is used to determine which members of an object refer
to other objects.
Generations
One feature of the garbage collector that exists purely to improve
performance is called generations. A generational garbage collector (also
known as an ephemeral garbage collector) makes the following
assumptions:
The newer an object is, the shorter its lifetime will be.
The older an object is, the longer its lifetime will be.
Newer objects tend to have strong relationships to each other and are
frequently accessed around the same time.
Compacting a portion of the heap is faster than compacting the whole
heap.
When initialized, the managed heap contains no objects. Objects added to
the heap are said to be in generation 0, as you can see in following fig.
Stated simply, objects in generation 0 are young objects that have never
been examined by the garbage collector.
Now, if more objects are added to the heap, the heap fills and a garbage
collection must occur. When the garbage collector analyzes the heap, it
builds the graph of garbage (shown here in Green) and non-garbage objects.
Any objects that survive the collection are compacted into the left-most
portion of the heap. These objects have survived a collection, are older, and
are now considered to be in generation 1.
As even more objects are added to the heap, these new, young objects are
placed in generation 0. If generation 0 fills again, a GC is performed. This
time, all objects in generation 1 that survive are compacted and considered
to be in generation 2 (see following fig). All survivors in generation 0 are
now compacted and considered to be in generation 1. Generation 0 currently
contains no objects, but all new objects will go into generation 0.
Currently, generation 2 is the highest generation supported by the runtime's
garbage collector. When future collections occur, any surviving objects
currently in generation 2 simply stay in generation 2.
Generational GC Performance Optimizations
Generational garbage collecting improves performance. When the heap fills
and a collection occurs, the garbage collector can choose to examine only
the objects in generation 0 and ignore the objects in any greater
generations. After all, the newer an object is, the shorter its lifetime is
expected to be. So, collecting and compacting generation 0 objects is likely
to reclaim a significant amount of space from the heap and be faster than if
the collector had examined the objects in all generations.
A generational collector can offer more optimizations by not traversing every
object in the managed heap. If a root or object refers to an object in an old
generation, the garbage collector can ignore any of the older objects' inner
references, decreasing the time required to build the graph of reachable
objects. Of course, it is possible that an old object refers to a new object. So
that these objects are examined, the collector can take advantage of the
system's write-watch support (provided by the Win32 GetWriteWatch
function in Kernel32.dll). This support lets the collector know which old
objects (if any) have been written to since the last collection. These specific
old objects can have their references checked to see if they refer to any new
objects.
If collecting generation 0 doesn't provide the necessary amount of storage,
then the collector can attempt to collect the objects from generations 1 and
0. If all else fails, then the collector can collect the objects from all
generations-2, 1, and 0.
One of the assumptions stated earlier was that newer objects tend to have
strong relationships to each other and are frequently accessed around the
same time. Since new objects are allocated contiguously in memory, you
gain performance from locality of reference. More specifically, it is highly
likely that all the objects can reside in the CPU's cache. Your application will
access these objects with phenomenal speed since the CPU will be able to
perform most of its manipulations without having cache misses which forces
RAM access.
Microsoft's performance tests show that managed heap allocations are faster
than standard allocations performed by the Win32 HeapAlloc function. These
tests also show that it takes less than 1 millisecond on a 200 MHz Pentium
to perform a full GC of generation 0. It is Microsoft's goal to make GCs take
no more time than an ordinary page fault.
Disadvantages of Win32 heap:
Most heaps (like the C runtime heap) allocate objects wherever they
find free space. Therefore, if I create several objects consecutively, it
is quite possible that these objects will be separated by megabytes of
address space. However, in the managed heap, allocating several
objects consecutively ensures that the objects are contiguous in
memory.
When memory is allocated from a Win32 heap, the heap must be
examined to find a block of memory that can satisfy the request. This
is not required in managed heap, since here objects are contiguous in
memory.
In Win32 heap, data structures that the heap maintains must be
updated. The managed heap, on the other hand, only needs to
increment the heap pointer.
Finalization
The garbage collector offers an additional feature that you may want to take
advantage of: finalization. Finalization allows a resource to gracefully clean
up after itself when it is being collected. By using finalization, a resource
representing a file or network connection is able to clean itself up properly
when the garbage collector decides to free the resource's memory.
When the garbage collector detects that an object is garbage, the garbage
collector calls the object's Finalize method (if it exists) and then the object's
memory is reclaimed. For example, let's say you have the following type (in
C#):
public class BaseObj
{
public BaseObj()
{
}
protected override void Finalize()
{
// Perform resource cleanup code here
// Example: Close file/Close network connection
Console.WriteLine("In Finalize.");
}
}
Now you can create an instance of this object by calling:
BaseObj bo = new BaseObj();
Some time in the future, the garbage collector will determine that this object
is garbage. When that happens, the garbage collector will see that the type
has a Finalize method and will call the method, causing "In Finalize" to
appear in the console window and reclaiming the memory block used by this
object.
Many developers who are used to programming in C++ draw an immediate
correlation between a destructor and the Finalize method. However, object
finalization and destructors have very different semantics and it is best to
forget everything you know about destructors when thinking about
finalization. Managed objects never have destructors.
When designing a type it is best to avoid using a Finalize method. There are
several reasons for this:
Finalizable objects get promoted to older generations, which increases
memory pressure and prevents the object's memory from being
collected when the garbage collector determines the object is garbage.
In addition, all objects referred to directly or indirectly by this object
get promoted as well.
Finalizable objects take longer to allocate.
Forcing the garbage collector to execute a Finalize method can
significantly hurt performance. Remember, each object is finalized. So
if I have an array of 10,000 objects, each object must have its Finalize
method called.
Finalizable objects may refer to other (non-finalizable) objects,
prolonging their lifetime unnecessarily. In fact, you might want to
consider breaking a type into two different types: a lightweight type
with a Finalize method that doesn't refer to any other objects, and a
separate type without a Finalize method that does refer to other
objects.
You have no control over when the Finalize method will execute. The
object may hold on to resources until the next time the garbage
collector runs.
When an application terminates, some objects are still reachable and
will not have their Finalize method called. This can happen if
background threads are using the objects or if objects are created
during application shutdown or AppDomain unloading. In addition, by
default, Finalize methods are not called for unreachable objects when
an application exits so that the application may terminate quickly. Of
course, all operating system resources will be reclaimed, but any
objects in the managed heap are not able to clean up gracefully. You
can change this default behavior by calling the System.GC type's
RequestFinalizeOnShutdown method. However, you should use this
method with care since calling it means that your type is controlling a
policy for the entire application.
The runtime doesn't make any guarantees as to the order in which
Finalize methods are called. For example, let's say there is an object
that contains a pointer to an inner object. The garbage collector has
detected that both objects are garbage. Furthermore, say that the
inner object's Finalize method gets called first. Now, the outer object's
Finalize method is allowed to access the inner object and call methods
on it, but the inner object has been finalized and the results may be
unpredictable. For this reason, it is strongly recommended that Finalize
methods not access any inner, member objects.
If you determine that your type must implement a Finalize method, then
make sure the code executes as quickly as possible. Avoid all actions that
would block the Finalize method, including any thread synchronization
operations. Also, if you let any exceptions escape the Finalize method, the
system just assumes that the Finalize method returned and continues calling
other objects' Finalize methods.
When the compiler generates code for a constructor, the compiler
automatically inserts a call to the base type's constructor. Likewise, when a
C++ compiler generates code for a destructor, the compiler automatically
inserts a call to the base type's destructor. Finalize methods are different
from destructors. The compiler has no special knowledge about a Finalize
method, so the compiler does not automatically generate code to call a base
type's Finalize method. If you want this behavior-and frequently you do-then
you must explicitly call the base type's Finalize method from your type's
Finalize method:
public class BaseObj
{
public BaseObj()
{
}
protected override void Finalize()
{
Console.WriteLine("In Finalize.");
base.Finalize(); // Call base type's Finalize
}
}
Note that you'll usually call the base type's Finalize method as the last
statement in the derived type's Finalize method. This keeps the base object
alive as long as possible. Since calling a base type Finalize method is
common, C# has a syntax that simplifies your work. In C#, the following
code:
class MyObject
{
MyObject()
{
}
}
causes the compiler to generate this code:
class MyObject
{
protected override void Finalize()
{
base.Finalize();
}
}
Note that this C# syntax looks identical to the C++ language's syntax for
defining a destructor. But remember, C# doesn't support destructors. Don't
let the identical syntax fool you.
Finalization Internals
When an application creates a new object, the new operator allocates the
memory from the heap. If the object's type contains a Finalize method, then
a pointer to the object is placed on the finalization queue. The finalization
queue is an internal data structure controlled by the garbage collector. Each
entry in the queue points to an object that should have its Finalize method
called before the object's memory can be reclaimed.
Following fig shows a heap containing several objects. Some of these objects
are reachable from the application's roots, and some are not. When objects
C, E, F, I, and J were created, the system detected that these objects had
Finalize methods and pointers to these objects were added to the finalization
queue.
When a GC occurs, objects B, E, G, H, I, and J are determined to be
garbage. The garbage collector scans the finalization queue looking for
pointers to these objects. When a pointer is found, the pointer is removed
from the finalization queue and appended to the freachable queue
(pronounced "F-reachable"). The freachable queue is another internal data
structure controlled by the garbage collector. Each pointer in the freachable
queue identifies an object that is ready to have its Finalize method called.
After the collection, the managed heap looks like following fig. Here, you see
that the memory occupied by objects B, G, and H has been reclaimed
because these objects did not have a Finalize method that needed to be
called. However, the memory occupied by objects E, I, and J could not be
reclaimed because their Finalize method has not been called yet.
There is a special runtime thread dedicated to calling Finalize methods.
When the freachable queue is empty (which is usually the case), this thread
sleeps. But when entries appear, this thread wakes, removes each entry
from the queue, and calls each object's Finalize method. Because of this, you
should not execute any code in a Finalize method that makes any
assumption about the thread that's executing the code. For example, avoid
accessing thread local storage in the Finalize method.
The interaction of the finalization queue and the freachable queue is quite
fascinating. First, let me tell you how the freachable queue got its name. The
f is obvious and stands for finalization; every entry in the freachable queue
should have its Finalize method called. The "reachable" part of the name
means that the objects are reachable. To put it another way, the freachable
queue is considered to be a root just like global and static variables are
roots. Therefore, if an object is on the freachable queue, then the object is
reachable and is not garbage.
In short, when an object is not reachable, the garbage collector considers
the object garbage. Then, when the garbage collector moves an object's
entry from the finalization queue to the freachable queue, the object is no
longer considered garbage and its memory is not reclaimed. At this point,
the garbage collector has finished identifying garbage. Some of the objects
identified as garbage have been reclassified as not garbage. The garbage
collector compacts the reclaimable memory and the special runtime thread
empties the freachable queue, executing each object's Finalize method.
The next time the garbage collector is invoked, it sees that the finalized
objects are truly garbage, since the application's roots don't point to it and
the freachable queue no longer points to it. Now the memory for the object
is simply reclaimed. The important thing to understand here is that two GCs
are required to reclaim memory used by objects that require finalization. In
reality, more than two collections may be necessary since the objects could
get promoted to an older generation. Above fig shows what the managed
heap looks like after the second GC.
Dispose Method
Use this method to close or release unmanaged resources such as files,
streams, and handles held by an instance of the class that implements this
interface. This method is, by convention, used for all tasks associated with
freeing resources held by an object, or preparing an object for reuse.
When implementing this method, objects must seek to ensure that all held
resources are freed by propagating the call through the containment
hierarchy. For example, if an object A allocates an object B, and object B
allocates an object C, then A's Dispose implementation must call Dispose on
B, which must in turn call Dispose on C. Objects must also call the Dispose
method of their base class if the base class implements IDisposable.
If an object's Dispose method is called more than once, the object must
ignore all calls after the first one. The object must not throw an exception if
its Dispose method is called multiple times. Dispose can throw an exception
if an error occurs because a resource has already been freed and Dispose
had not been called previously.
Because the Dispose method must be called explicitly, objects that
implement IDisposable must also implement a finalizer to handle freeing
resources when Dispose is not called. By default, the garbage collector will
automatically call an object's finalizer prior to reclaiming its memory.
However, once the Dispose method has been called, it is typically
unnecessary for the garbage collector to call the disposed object's finalizer.
To prevent automatic finalization, Dispose implementations can call the
GC.SuppressFinalize method.
Direct Control with System.GC
The System.GC type allows your application some direct control over the
garbage collector. You can query the maximum generation supported by the
managed heap by reading the GC.MaxGeneration property. Currently, the
GC.MaxGeneration property always returns 2.
It is also possible to force the garbage collector to perform a collection by
calling one of the two methods shown here:
void GC.Collect(Int32 Generation)
void GC.Collect()
The first method allows you to specify which generation to collect. You may
pass any integer from 0 to GC.MaxGeneration, inclusive. Passing 0 causes
generation 0 to be collected; passing 1 cause generation 1 and 0 to be
collected; and passing 2 causes generation 2, 1, and 0 to be collected. The
version of the Collect method that takes no parameters forces a full
collection of all generations and is equivalent to calling:
GC.Collect(GC.MaxGeneration);
Under most circumstances, you should avoid calling any of the Collect
methods; it is best to just let the garbage collector run on its own accord.
However, since your application knows more about its behavior than the
runtime does, you could help matters by explicitly forcing some collections.
For example, it might make sense for your application to force a full
collection of all generations after the user saves his data file. I imagine
Internet browsers performing a full collection when pages are unloaded. You
might also want to force a collection when your application is performing
other lengthy operations; this hides the fact that the collection is taking
processing time and prevents a collection from occurring when the user is
interacting with your application.
The GC type also offers a WaitForPendingFinalizers method. This method
simply suspends the calling thread until the thread processing the freachable
queue has emptied the queue, calling each object's Finalize method. In most
applications, it is unlikely that you will ever have to call this method.
Lastly, the garbage collector offers two methods that allow you to determine
which generation an object is currently in:
Int32 GetGeneration(Object obj)
Int32 GetGeneration(WeakReference wr)
The first version of GetGeneration takes an object reference as a parameter,
and the second version takes a WeakReference reference as a parameter. Of
course, the value returned will be somewhere between 0 and
GC.MaxGeneration, inclusive.
Chapter Objective
OOP's overview
Classes and Objects
Constructor and Destructor
Function Overloading
Encapsulation
Inheritance
Interface
Polymorphism
OOP's overview
Object-oriented programming (OOP) is the core ingredient of the .NET framework. OOP is so important
that, before embarking on the road to .NET, you must understand its basic principles and terminology to
write even a simple program. The fundamental idea behind OOP is to combine into a single unit both
data and the methods that operate on that data; such units are called an object. All OOP languages
provide mechanisms that help you implement the object-oriented model. They are encapsulation,
inheritance, polymorphism and reusability. Let's now take a brief look at these concepts.
Encapsulation
Encapsulation binds together code and the data it manipulates and keeps them both safe from outside
interference and misuse. Encapsulation is a protective container that prevents code and data from being
accessed by other code defined outside the container.
Inheritance
Inheritance is the process by which one object acquires the properties of another object. A type derives
from a base type, taking all the base type members fields and functions. Inheritance is most useful when
you need to add functionality to an existing type. For example all .NET classes inherit from the
System.Object class, so a class can include new functionality as well as use the existing object's class
functions and properties as well.
Polymorphism
Polymorphism is a feature that allows one interface to be used for a general class of action. This concept
is often expressed as "one interface, multiple actions". The specific action is determined by the exact
nature of circumstances.
Reusability
Once a class has been written, created and debugged, it can be distributed to other programmers for use
in their own program. This is called reusability, or in .NET terminology this concept is called a component
or a DLL. In OOP, however, inheritance provides an important extension to the idea of reusability. A
programmer can use an existing class and without modifying it, add additional features to it.
Simple "Hello World" C# Program
This simple one-class console "Hello world" program demonstrates many fundamental concepts
throughout this article and several future articles.
C# code
using System;
namespace oops
{
//class definition
public class SimpleHelloWorld
{
//Entry point of the program
static void Main(string[] args)
{
//print Hello world"
Console.WriteLine("Hello World!");
}
}
}
So SimpleHelloWorld is the name of the class that contains the Main () method. On line 1 , a using
directive indicates to the compiler that this source file refers to classes and constructs declared within the
System namespace. Line 6 with the public keyword indicates the program accessibility scope for other
applications or components.
At line 7 there appears an opening curly brace ("{") which indicates the beginning of the
SimpleHelloWorld class body. Everything belongs to the class, like fields, properties and methods appear
in the class body between the opening and closing braces. The purpose of the Main () method is to
provide an entry point for application execution.
The static keyword in the Main () method states that this method would be executed without instantiating
the class.
Compiling the Program
You can compile a C# program into either an assembly or a module. If the program has one class that
contains a Main () method then it can be compiled directly into an assembly. This file has an ".exe"
extension. A program with no Main() method can be compiled into a module as in the following:
csc /target:module "program name"
You can then compile this program by F9 or by simply running the C# command line compiler (csc.exe)
against the source file as the following:
csc oops.cs
Classes and Objects
Classes are special kinds of templates from which you can create objects. Each object contains data and
methods to manipulate and access that data. The class defines the data and the functionality that each
object of that class can contain.
A class declaration consists of a class header and body. The class header includes attributes, modifiers,
and the class keyword. The class body encapsulates the members of the class, that are the data members
and member functions. The syntax of a class declaration is as follows:
Attributes accessibility modifiers class identifier: baselist { body }
Attributes provide additional context to a class, like adjectives; for example the Serializable attribute.
Accessibility is the visibility of the class. The default accessibility of a class is internal. Private is the default
accessibility of class members. The following table lists the accessibility keywords;
Keyword Description
public Public class is visible in the current and referencing assembly.
private Visible inside current class.
protected Visible inside current and derived class.
Internal Visible inside containing assembly.
Internal protected Visible inside containing assembly and descendent of the current class.
Modifiers refine the declaration of a class. The list of all modifiers defined in the table are as follows;
Modifier Description
sealed Class can't be inherited by a derived class.
static Class contains only static members.
unsafe The class that has some unsafe construct likes pointers.
Abstract The instance of the class is not created if the Class is abstract.
The baselist is the inherited class. By default, classes inherit from the System.Object type. A class can
inherit and implement multiple interfaces but doesn't support multiple inheritances.
Step-by-step Tutorial for Creating a Class
1. Open Visual Studio 2010 from start menu.
2. Go to "File" > "New" > "Project...", select "Console Application" in the right pane and provide the
name "oops" for the project.
3. Then in the Solution Explorer, you will notice some files that are automatically created as:
4. You can also write your own code in the default program.cs file that is created but it is a good
programming practice to create a new class.
5. For adding a new class, right-click over the project name (oops) in the Solution Explorer, then click
"Add" > "Class". Give the name to the class "customer" as in the following;
6. When you open the customer.cs class. you will find some default-generated code as in the
following;
C# code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace oops
{
class customer
{
}
}
Note: the C# console application project must require a single entry point Main () function that is already
generated in the program class. For example if you add a new customer class and want to define one or
more Main () entry points here then .NET will throw an error of multiple entry points. So it is advisable to
delete or exclude the program.cs file from the solution.
So here in this example the customer class defines fields such as CustID, Name and Address to hold
information about a particular customer. It might also define some functionality that acts upon the data
stored in these fields.
C# code
using System;
namespace oops
{
class customer
{
// Member Variables
public int CustID;
public string Name;
public string Address;
//constuctor for initializing fields
customer()
{
CustID=1101;
Name="Tom";
Address="USA";
}
//method for displaying customer records (functionality)
public void displayData()
{
Console.WriteLine("Customer="+CustID);
Console.WriteLine("Name="+Name);
Console.WriteLine("Address="+Address);
}
// Code for entry point
}
}
At line 9, we are defining a constructor of the customer class for initializing the class member fields. The
constructor is a special function that is automatically called when the customer class object is created
(instantiated). And at line 11 we are printing these fields to the console by creating a user defined method
displayData().
You can then instantiate an object of this class to represent one specific customer, set the field value for
that instance and use its functionality, as in:
C# code
class customer
{
// class members code
//Entry point
static void Main(string[] args)
{
// object instantiation
customer obj = new customer();
//Method calling
obj.displayData();
//fields calling
Console.WriteLine(obj.CustID);
Console.WriteLine(obj.Name);
Console.WriteLine(obj.Address);
}
}
Here you use the keyword new to declare the customer class instance. This keyword creates the object
and initializes it. When you create an object of the customer class, the .NET framework IDE provides a
special feature called Intellisense that provides access to all the class member fields and functions
automatically. This feature is invoke when the "." Operator is put right after the object, as in the following;
Image 1.1 Intellisense feature
Normally, as the program grows in size and the code becomes more complex, the Intellisense feature
increases the convenience for the programmer by showing all member fields, properties and functions.
Multiple Class Declaration
Sometimes circumstances require multiple classes to be declared in a single namespace. So in that case it
is not mandatory to add a separate class to the solution, instead you can attach the new class into the
existing program.cs or another one as in the following;
C# code
using System;
namespace oops
{
class Program
{
public void MainFunction()
{
Console.WriteLine("Main class");
}
static void Main(string[] args)
{
//main class instance
Program obj = new Program();
obj.MainFunction();
//other class instace
demo dObj=new demo();
dObj.addition();
}
}
class demo
{
int x = 10;
int y = 20;
int z;
public void addition()
{
z = x + y;
Console.WriteLine("other class in Namespace");
Console.WriteLine(z);
}
}
}
Here in this example, we are creating an extra class "demo" in the program.cs file at line 12 and finally we
are instantiating the demo class with the program class inside the Main() entry in lines 6 to 11. So it
doesn't matter how many classes we are defining in a single assembly.
Partial classes
Typically, a class will reside entirely in a single file. However, in situations where multiple developers need
access to the same class, then having the class in multiple files can be beneficial. The partial keywords
allow a class to span multiple source files. When compiled, the elements of the partial types are combined
into a single assembly.
There are some rules for defining a partial class as in the following;
A partial type must have the same accessibility.
Each partial type is preceded with the "partial" keyword.
If the partial type is sealed or abstract then the entire class will be sealed and abstract.
In the following example we are adding two files, partialPart1.cs and partialPart2.cs, and declare a partial
class, partialclassDemo, in both classes.
partialPart1.cs
using System;
namespace oops
{
public partial class partialclassDemo
{
public void method1()
{
Console.WriteLine("method from part1 class");
}
}
}
partialPart2.cs
using System;
namespace oops
{
public partial class partialclassDemo
{
public void method2()
{
Console.WriteLine("method from part2 class");
}
}
}
And finally we are creating an instance of the partialclassDemo in the program.cs file as the following:
Program.cs
using System;
namespace oops
{
class Program
{
static void Main(string[] args)
{
//partial class instance
partialclassDemo obj = new partialclassDemo();
obj.method1();
obj.method2();
}
}
}
Static classes
A static class is declared using the "static" keyword. If the class is declared as static then the compiler
never creates an instance of the class. All the member fields, properties and functions must be declared as
static and they are accessed by the class name directly not by a class instance object.
C# code
using System;
namespace oops
{
static class staticDemo
{
//static fields
static int x = 10, y;
//static method
static void calcute()
{
y = x * x;
Console.WriteLine(y);
}
static void Main(string[] args)
{
//function calling directly
staticDemo.calcute();
}
}
}
Creating and accessing Class Component Library
.NET provides the capability of creating libraries (components) of a base application rather than an
executable (".exe"). Instead the library project's final build version will be ".DLL" that can be
referenced from other outside applications to expose its entire functionality.
Step-by-step tutorial
1. First create a class library based application as:
2. Then we are implementing a math class library that is responsible of calculating square root and the
addition of two numbers as:
using System;
namespace LibraryUtil
{
public class MathLib
{
public MathLib() { }
public void calculareSum(int x, int y)
{
int z = x + y;
Console.WriteLine(z);
}
public void calculareSqrt(double x)
{
double z = Math.Sqrt(x);
Console.WriteLine(z);
}
}
}
3. Build this code and you will notice that a DLL file was created, not an executable, in the root directory of
the application (path = D:tempLibraryUtilLibraryUtilbinDebug LibraryUtil.dll).
4. Now create another console based application where you utilize all the class library's functionality.
5. Then you have to add the class library dll file reference to access the declared class in the library dll.
(Right-click on the Reference then "Add reference" then select the path of the dll file.)
6. When you add the class library reference then you will notice in the Solution Explorer that a new
LibraryUtil is added as in the following;
7. Now add the namespace of the class library file in the console application and create the instance of
the class declared in the library as in the following;
using System;
using LibraryUtil; // add library namespace
namespace oops
{
public class LibraryClass
{
static void Main()
{
//library class instance
MathLib obj = new MathLib();
//method populate
obj.calculareSum(2, 5);
obj.calculareSqrt(25);
}
}
}
8. Finally run the application.
Constructor and Destructor
A constructor is a specialized function that is used to initialize fields. A constructor has the same name as
the class. Instance constructors are invoked with the new operator and can't be called in the same manner
as other member functions. There are some important rules pertaining to constructors as in the following;
Classes with no constructor have an implicit constructor called the default constructor, that is
parameterless. The default constructor assigns default values to fields.
A public constructor allows an object to be created in the current assembly or referencing
assembly.
Only the extern modifier is permitted on the constructor.
A constructor returns void but does not have an explicitly declared return type.
A constructor can have zero or more parameters.
Classes can have multiple constructors in the form of default, parameter or both.
The following example shows one constructor for a customer class.
C# code
using System;
namespace oops
{
class customer
{
// Member Variables
public string Name;
//constuctor for initializing fields
public customer(string fname, string lname)
{
Name= fname +" "+ lname;
}
//method for displaying customer records
public void AppendData()
{
Console.WriteLine(Name);
}
//Entry point
static void Main(string[] args)
{
// object instantiation
customer obj = new customer("Barack", "Obama");
//Method calling
obj.AppendData();
}
}
}
Note: The moment a new statement is executed, the default constructor is called.
Static Constructor
A constructor can be static. You create a static constructor to initialize static fields. Static constructors are
not called explicitly with the new statement. They are called when the class is first referenced. There are
some limitations of the static constructor as in the following;
Static constructors are parameterless.
Static constructors can't be overloaded.
There is no accessibility specified for Static constructors.
In the following example the customer class has a static constructor that initializes the static field and this
constructor is called when the class is referenced in the Main () at line 26 as in the following:
C# code
using System;
namespace oops
{
class customer
{
// Member Variables
static private int x;
//constuctor for static initializing fields
static customer()
{
x = 10;
}
//method for get static field
static public void getData()
{
Console.WriteLine(x);
}
//Entry point
static void Main(string[] args)
{
//static Method calling
customer.getData();
}
}
}
Destructors
The purpose of the destructor method is to remove unused objects and resources. Destructors are not
called directly in the source code but during garbage collection. Garbage collection is nondeterministic. A
destructor is invoked at an undetermined moment. More precisely a programmer can't control its
execution; rather it is called by the Finalize () method. Like a constructor, the destructor has the same
name as the class except a destructor is prefixed with a tilde (~). There are some limitations of destructors
as in the following;
Destructors are parameterless.
A Destructor can't be overloaded.
Destructors are not inherited.
Destructors can cause performance and efficiency implications.
The following implements a destructor and dispose method. First of all we are initializing the fields via
constructor, doing some calculations on that data and displaying it to the console. But at line 9 we are
implementing the destructor that is calling a Dispose() method to release all the resources.
using System;
namespace oops
{
class customer
{
// Member Variables
public int x, y;
//constuctor for initializing fields
customer()
{
Console.WriteLine("Fields inititalized");
x = 10;
}
//method for get field
public void getData()
{
y = x * x;
Console.WriteLine(y);
}
//method to release resource explicitly
public void Dispose()
{
Console.WriteLine("Fields cleaned");
x = 0;
y = 0;
}
//destructor
~customer()
{
Dispose();
}
//Entry point
static void Main(string[] args)
{
//instance created
customer obj = new customer();
obj.getData();
}
}
}
At line 12 when the instance is created, fields are initialized but it is not necessary that at the same time
the destructor is also called. Its calling is dependent on garbage collection. If you want to see the
destructor being called into action then put a breakpoint (by F9) at line 10 and compile the application.
The CLR indicates its execution at the end of the program by highlighting line 10 using the yellow color.
Function Overloading
Function overloading allows multiple implementations of the same function in a class. Overloaded
methods share the same name but have a unique signature. The number of parameters, types of
parameters or both must be different. A function can't be overloaded on the basis of a different return
type alone.
using System;
namespace oops
{
class funOverload
{
public string name;
//overloaded functions
public void setName(string last)
{
name = last;
}
public void setName(string first, string last)
{
name = first + "" + last;
}
public void setName(string first, string middle, string last)
{
name = first + "" + middle + "" + last;
}
//Entry point
static void Main(string[] args)
{
funOverload obj = new funOverload();
obj.setName("barack");
obj.setName("barack "," obama ");
obj.setName("barack ","hussian","obama");
}
}
}
At lines 3, 4 and 5 we are defining three methods with the same name but with different parameters. In
the Main (), the moment you create an instance of the class and call the functions setName() via obj at
lines 7, 8 and 9 then intellisense will show three signatures automatically.
Encapsulation
Encapsulation is the mechanism that binds together the code and the data it manipulates, and keeps both
safe from outside interference and misuse. In OOP, code and data may be combined in such a way that a
self-contained box is created. When code and data are linked together in this way, an object is created
and encapsulation exists.
Within an object, code, data or both may be private or public to that object. Private code is known to and
accessible only by another part of the object, that is private code or data may not be accessible by a piece
of the program that exists outside the object. When the code and data is public, other portions of your
program may access it even though it is defined within an object.
C# code
using System;
namespace oops
{
class Encapsulation
{
/// <summary>
/// Every member Variable and Function of the class are bind
/// with the Encapsulation class object only and safe with
/// the outside inference
/// </summary>
// Encapsulation Begin
int x;
//class constructor
public Encapsulation(int iX)
{
this.x = iX;
}
//calculating the square
public void MySquare()
{
int Calc = x * x;
Console.WriteLine(Calc);
}
// End of Encapsulation
//Entry point
static void Main(string[] args)
{
//instance created
customer obj = new customer(20);
obj. MySquare();
}
}
}
Inheritance
Inheritance is the process by which one object can acquire the properties of another object. Inheritance is
a "is a kind of" relationship and it supports the concept of classification in which an object needs only
define those qualities that make it unique within the class. Inheritance involves a base class and a derived
class. The derived class inherits from the base class and also can override inherited members as well as
add new members to extend the base class.
A base type represents the generalization, whereas a derived type represents a specification of an
instance. Such as Employees that can have diverse types, such as hourly, salaried and temporary so in that
case Employees is the general base class and hourly, salaried and temporary employee are specialized
derived classes.
Classes can inherit from a single class and one or more interfaces. When inheriting from a class, the
derived class inherits the members including the code of the base class. The important point to remember
is that Constructors and Destructors are not inherited from the base class.
The syntax of inheritance is as in the following;
Class derivedClass : baseClass, Iterface1, Interface2 { body }
For example we are defining two classes, Father and Child. You notice at line 7, we are implementing
inheritance by using a colon (:); at this moment all the properties belonging to the Father Class is
accessible to the Child class automatically.
C# code
using System;
namespace oops
{
//Base Class
public class Father
{
public void FatherMethod()
{
Console.WriteLine("this property belong to Father");
}
}
//Derived class
public class Child : Father
{
public void ChildMethod()
{
Console.WriteLine("this property belong to Child");
}
}
class Inheritance
{
//Entry point
static void Main(string[] args)
{
Father fObj = new Father();
fObj.FatherMethod();
//Here Child object can access both class methods
Child cObj = new Child();
cObj.FatherMethod();
cObj.ChildMethod();
}
}
}
At line 11 , the Intellisense only shows the Father class functions but at line 15 to 16 the Child class object
is able to access both class methods as in the following.
We can create a class in the VB.Net language or another .NET supported language and can inherit them in
a C# .Net class and vice versa. But a class developed in C++ or other unmanaged environment can't be
inherited in .NET.
Note: Cross-language and multiple inheritance is not supported by .NET.
Accessibility
Accessibility sets the visibility of the member to outside assemblies or derived types. The following table
describes member accessibility;
Modifiers Outside Assembly Derived Class
private No No
public Yes Yes
protected No No
internal Yes ( this assembly only) Yes ( this assembly only)
internal protected Yes( this assembly only) Yes
Constructor in Inheritance
Constructors in a base class are not inherited in a derived class. A derived class has a base portion and
derived portion. The base portion initializes the base portion, and the constructor of the derived class
initializes the derived portion.
The following is the syntax of a constructor in inheritance;
Accessibility modifier classname(parameterlist1) : base(parameterlist2) { body }
So the base keyword refers to the base class constructor, while parameterlist2 determines which
overloaded base class constructor is called.
In the following example, the Child class's constructor calls the single-argument constructor of the base
Father class;
C# code
using System;
namespace oops
{
//Base Class
public class Father
{
//constructor
public Father()
{
Console.WriteLine("Father class constructor");
}
public void FatherMethod()
{
Console.WriteLine("this property belong to Father");
}
}
//Derived class
public class Child : Father
{
public Child()
: base()
{
Console.WriteLine("child class constructor");
}
public void ChildMethod()
{
Console.WriteLine("this property belong to Child");
}
}
class Inheritance
{
//Entry point
static void Main(string[] args)
{
//Here Child object can access both class methods
Child cObj = new Child();
cObj.FatherMethod();
cObj.ChildMethod();
Console.ReadKey();
}
}
}
At line 4, we are defining a base Father Class constructor and in the derived class Child, at line 8 we are
initializing it explicitly via base keyword. If we pass any parameter in the base class constructor then we
have to provide them in the base block of the child class constructor.
Virtual Methods
By declaring a base class function as virtual, you allow the function to be overridden in any derived class.
The idea behind a virtual function is to redefine the implementation of the base class method in the
derived class as required. If a method is virtual in the base class then we have to provide the override
keyword in the derived class. Neither member fields nor static functions can be declared as virtual.
C# code
using System;
namespace oops
{
class myBase
{
//virtual function
public virtual void VirtualMethod()
{
Console.WriteLine("virtual method defined in the base class");
}
}
class myDerived : myBase
{
// redifing the implementation of base class method
public override void VirtualMethod()
{
Console.WriteLine("virtual method defined in the Derive class");
}
}
class virtualClass
{
static void Main(string[] args)
{
// class instance
new myDerived().VirtualMethod();
Console.ReadKey();
}
}
}
Hiding Methods
If a method with the same signature is declared in both base and derived classes, but the methods are not
declared as virtual and overriden respectively, then the derived class version is said to hide the base class
version. In most cases, you would want to override methods rather than hide them. Otherwise .NET
automatically generates a warning.
In the following example, we are defining a VirutalMethod() in the myBase class but not overriding it in
the derived class, so in that case the compiler will generate a warning. The compiler will assume that you
are hiding the base class method. So to overcome that problem, if you prefix the new keyword in the
derived class method then the compiler will prefer the most derived version method. You can still access
the base class method in the derived class by using the base keyword.
C# code
using System;
namespace oops
{
class myBase
{
//virtual function
public virtual void VirtualMethod()
{
Console.WriteLine("virtual method defined in the base class");
}
}
class myDerived : myBase
{
// hiding the implementation of base class method
public new void VirtualMethod()
{
Console.WriteLine("virtual method defined in the Derive class");
//still access the base class method
base.VirtualMethod();
}
}
class virtualClass
{
static void Main(string[] args)
{
// class instance
new myDerived().VirtualMethod();
Console.ReadKey();
}
}
}
Abstract Classes
C# allows both classes and functions to be declared abstract using the abstract keyword. You can't create
an instance of an abstract class. An abstract member has a signature but no function body and they must
be overridden in any non-abstract derived class. Abstract classes exist primarily for inheritance. Member
functions, properties and indexers can be abstract. A class with one or more abstract members must be
abstract as well. Static members can't be abstract.
In this example, we are declaring an abstract class Employess with a method displayData() that does not
have an implementation. Then we are implementing the displayData() body in the derived class. One point
to be noted here is that we have to prefixe the abstract method with the override keyword in the derived
class.
C# code
using System;
namespace oops
{
//abstract class
public abstract class Employess
{
//abstract method with no implementation
public abstract void displayData();
}
//derived class
public class test : Employess
{
//abstract class method implementation
public override void displayData()
{
Console.WriteLine("Abstract class method");
}
}
class abstractClass
{
static void Main(string[] args)
{
// class instance
new test().displayData();
}
}
}
Sealed Classes
Sealed classes are the reverse of abstract classes. While abstract classes are inherited and are refined in
the derived class, sealed classes cannot be inherited. You can create an instance of a sealed class. A sealed
class is used to prevent further refinement through inheritance.
Suppose you are a developer of a class library and some of the classes in the class library are extensible
but other classes are not extensible so in that case those classes are marked as sealed.
C# code
using System;
namespace oops
{
sealed class SealedClass
{
void myfunv();
}
public class test : SealedClass //wrong. will give compilation error
{
}
}
Interface
An interface is a set of related functions that must be implemented in a derived class. Members of an
interface are implicitly public and abstract. Interfaces are similar to abstract classes. First, both types must
be inherited; second, you cannot create an instance of either. Although there are several differences as in
the following;
An Abstract class can contain some implementations but an interface can't.
An Interface can only inherit other interfaces but abstract classes can inherit from other classes
and interfaces.
An Abstract class can contain constructors and destructors but an interface can't.
An Abstract class contains fields but interfaces don't.
So the question is, which of these to choose? Select interfaces because with an interface, the derived type
still can inherit from another type and interfaces are more straightforward than abstract classes.
C# code
using System;
namespace oops
{
// interface
public interface xyz
{
void methodA();
void methodB();
}
// interface method implementation
class test : xyz
{
public void methodA()
{
Console.WriteLine("methodA");
}
public void methodB()
{
Console.WriteLine("methodB");
}
}
class interfaceDemo
{
static void Main(string[] args)
{
test obj = new test();
obj.methodA();
obj.methodB();
}
}
}
An interface can be inherited from other interfaces as in the following:
C# code
public interface xyz
{
void methodA();
void methodB();
}
public interface abc : xyz
{
void methodC();
}
Polymorphism
Polymorphism is the ability to treat the various objects in the same manner. It is one of the significant
benefits of inheritance. We can decide the correct call at runtime based on the derived type of the base
reference. This is called late binding.
In the following example, instead of having a separate routine for the hrDepart, itDepart and
financeDepart classes, we can write a generic algorithm that uses the base type functions. The method
LeaderName() declared in the base abstract class is redefined as per our needs in 2 different classes.
C# code
using System;
namespace oops
{
public abstract class Employee
{
public virtual void LeaderName()
{
}
}
public class hrDepart : Employee
{
public override void LeaderName()
{
Console.WriteLine("Mr. jone");
}
}
public class itDepart : Employee
{
public override void LeaderName()
{
Console.WriteLine("Mr. Tom");
}
}
public class financeDepart : Employee
{
public override void LeaderName()
{
Console.WriteLine("Mr. Linus");
}
}
class PolymorphismDemo
{
static void Main(string[] args)
{
hrDepart obj1 = new hrDepart();
itDepart obj2 = new itDepart();
financeDepart obj3 = new financeDepart();
obj1.LeaderName();
obj2.LeaderName();
obj3.LeaderName();
Console.ReadKey();
}
}
}
Login to add your contents and source code to this article
Article Extensions
Contents added by Zeeshan shan on Feb 06, 2013
OVERIDING EXAMPLE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace overriding
{
abstract class BaseClass
{
public abstract string YourCity();
}
class DerivedClass : BaseClass
{
public override string YourCity() //It is mandatory to implement absract method
{
return "London";
}
private int sum(int a, int b)
{
return a + b;
}
}
class Program
{
static void Main(string[] args)
{
DerivedClass obj = new DerivedClass();
string city = obj.YourCity();
Console.WriteLine(city);
Console.Read();
}
}
}
Introduction
In this article, I want to show you the concept of Object-Oriented Programming in C#. Now what
are objects and why we’d better write Object-Oriented applications? Good Question! We will
cover the following:
What is an object?
Creating a class
Fields
Methods
Instantiating your class
Access Modifiers
Properties
OOP Concepts in C
OOP Concepts in C
OOP Concepts in C
OOP Concepts in C
OOP Concepts in C
OOP Concepts in C

More Related Content

What's hot

8 abstract classes and interfaces
8   abstract classes and interfaces 8   abstract classes and interfaces
8 abstract classes and interfaces Tuan Ngo
 
Abstraction in java [abstract classes and Interfaces
Abstraction in java [abstract classes and InterfacesAbstraction in java [abstract classes and Interfaces
Abstraction in java [abstract classes and InterfacesAhmed Nobi
 
The Go Programing Language 1
The Go Programing Language 1The Go Programing Language 1
The Go Programing Language 1İbrahim Kürce
 
Implementing polymorphism
Implementing polymorphismImplementing polymorphism
Implementing polymorphismrajshreemuthiah
 
EEE oops Vth semester viva questions with answer
EEE oops Vth semester viva questions with answerEEE oops Vth semester viva questions with answer
EEE oops Vth semester viva questions with answerJeba Moses
 
Java interfaces
Java interfacesJava interfaces
Java interfacesjehan1987
 
Java questions for interview
Java questions for interviewJava questions for interview
Java questions for interviewKuntal Bhowmick
 
Ap Power Point Chpt7
Ap Power Point Chpt7Ap Power Point Chpt7
Ap Power Point Chpt7dplunkett
 
Interfaces and abstract classes
Interfaces and abstract classesInterfaces and abstract classes
Interfaces and abstract classesAKANSH SINGHAL
 
Oops abap fundamental
Oops abap fundamentalOops abap fundamental
Oops abap fundamentalbiswajit2015
 
Type variability and completeness of interfaces in java applications
Type variability and completeness of interfaces in java applicationsType variability and completeness of interfaces in java applications
Type variability and completeness of interfaces in java applicationsijseajournal
 
C# Interface | Interfaces In C# | C# Interfaces Explained | C# Tutorial For B...
C# Interface | Interfaces In C# | C# Interfaces Explained | C# Tutorial For B...C# Interface | Interfaces In C# | C# Interfaces Explained | C# Tutorial For B...
C# Interface | Interfaces In C# | C# Interfaces Explained | C# Tutorial For B...Simplilearn
 
Core Java interview questions-ppt
Core Java interview questions-pptCore Java interview questions-ppt
Core Java interview questions-pptMayank Kumar
 
Ap Power Point Chpt5
Ap Power Point Chpt5Ap Power Point Chpt5
Ap Power Point Chpt5dplunkett
 
Master of Computer Application (MCA) – Semester 4 MC0078
Master of Computer Application (MCA) – Semester 4  MC0078Master of Computer Application (MCA) – Semester 4  MC0078
Master of Computer Application (MCA) – Semester 4 MC0078Aravind NC
 
Object-Oriented Concepts
Object-Oriented ConceptsObject-Oriented Concepts
Object-Oriented ConceptsAbdalla Mahmoud
 
Java Programming - Abstract Class and Interface
Java Programming - Abstract Class and InterfaceJava Programming - Abstract Class and Interface
Java Programming - Abstract Class and InterfaceOum Saokosal
 

What's hot (20)

8 abstract classes and interfaces
8   abstract classes and interfaces 8   abstract classes and interfaces
8 abstract classes and interfaces
 
Abstraction in java [abstract classes and Interfaces
Abstraction in java [abstract classes and InterfacesAbstraction in java [abstract classes and Interfaces
Abstraction in java [abstract classes and Interfaces
 
The Go Programing Language 1
The Go Programing Language 1The Go Programing Language 1
The Go Programing Language 1
 
Implementing polymorphism
Implementing polymorphismImplementing polymorphism
Implementing polymorphism
 
EEE oops Vth semester viva questions with answer
EEE oops Vth semester viva questions with answerEEE oops Vth semester viva questions with answer
EEE oops Vth semester viva questions with answer
 
Java interfaces
Java interfacesJava interfaces
Java interfaces
 
Classes objects in java
Classes objects in javaClasses objects in java
Classes objects in java
 
Java questions for interview
Java questions for interviewJava questions for interview
Java questions for interview
 
Ap Power Point Chpt7
Ap Power Point Chpt7Ap Power Point Chpt7
Ap Power Point Chpt7
 
Interfaces and abstract classes
Interfaces and abstract classesInterfaces and abstract classes
Interfaces and abstract classes
 
Oops abap fundamental
Oops abap fundamentalOops abap fundamental
Oops abap fundamental
 
Type variability and completeness of interfaces in java applications
Type variability and completeness of interfaces in java applicationsType variability and completeness of interfaces in java applications
Type variability and completeness of interfaces in java applications
 
C# Interface | Interfaces In C# | C# Interfaces Explained | C# Tutorial For B...
C# Interface | Interfaces In C# | C# Interfaces Explained | C# Tutorial For B...C# Interface | Interfaces In C# | C# Interfaces Explained | C# Tutorial For B...
C# Interface | Interfaces In C# | C# Interfaces Explained | C# Tutorial For B...
 
Core Java interview questions-ppt
Core Java interview questions-pptCore Java interview questions-ppt
Core Java interview questions-ppt
 
Towards Improving Interface Modularity in Legacy Java Software Through Automa...
Towards Improving Interface Modularity in Legacy Java Software Through Automa...Towards Improving Interface Modularity in Legacy Java Software Through Automa...
Towards Improving Interface Modularity in Legacy Java Software Through Automa...
 
Ap Power Point Chpt5
Ap Power Point Chpt5Ap Power Point Chpt5
Ap Power Point Chpt5
 
Master of Computer Application (MCA) – Semester 4 MC0078
Master of Computer Application (MCA) – Semester 4  MC0078Master of Computer Application (MCA) – Semester 4  MC0078
Master of Computer Application (MCA) – Semester 4 MC0078
 
Essential language features
Essential language featuresEssential language features
Essential language features
 
Object-Oriented Concepts
Object-Oriented ConceptsObject-Oriented Concepts
Object-Oriented Concepts
 
Java Programming - Abstract Class and Interface
Java Programming - Abstract Class and InterfaceJava Programming - Abstract Class and Interface
Java Programming - Abstract Class and Interface
 

Similar to OOP Concepts in C

Similar to OOP Concepts in C (20)

ActionScript 3.0 Fundamentals
ActionScript 3.0 FundamentalsActionScript 3.0 Fundamentals
ActionScript 3.0 Fundamentals
 
oopsinvb-191021101327.pdf
oopsinvb-191021101327.pdfoopsinvb-191021101327.pdf
oopsinvb-191021101327.pdf
 
Cocoa and MVC in ios, iOS Training Ahmedbad , iOS classes Ahmedabad
Cocoa and MVC in ios, iOS Training Ahmedbad , iOS classes Ahmedabad Cocoa and MVC in ios, iOS Training Ahmedbad , iOS classes Ahmedabad
Cocoa and MVC in ios, iOS Training Ahmedbad , iOS classes Ahmedabad
 
OOP interview questions & answers.
OOP interview questions & answers.OOP interview questions & answers.
OOP interview questions & answers.
 
C#.net interview questions for dynamics 365 ce crm developers
C#.net interview questions for dynamics 365 ce crm developersC#.net interview questions for dynamics 365 ce crm developers
C#.net interview questions for dynamics 365 ce crm developers
 
My c++
My c++My c++
My c++
 
Java interview questions
Java interview questionsJava interview questions
Java interview questions
 
MCA NOTES.pdf
MCA NOTES.pdfMCA NOTES.pdf
MCA NOTES.pdf
 
C# program structure
C# program structureC# program structure
C# program structure
 
Advance oops concepts
Advance oops conceptsAdvance oops concepts
Advance oops concepts
 
Object Oriented Programming In .Net
Object Oriented Programming In .NetObject Oriented Programming In .Net
Object Oriented Programming In .Net
 
Oops
OopsOops
Oops
 
Java interview questions and answers
Java interview questions and answersJava interview questions and answers
Java interview questions and answers
 
M.c.a. (sem iv)- java programming
M.c.a. (sem   iv)- java programmingM.c.a. (sem   iv)- java programming
M.c.a. (sem iv)- java programming
 
Encapsulation
EncapsulationEncapsulation
Encapsulation
 
JAVA-PPT'S.pdf
JAVA-PPT'S.pdfJAVA-PPT'S.pdf
JAVA-PPT'S.pdf
 
POP vs OOP Introduction
POP vs OOP IntroductionPOP vs OOP Introduction
POP vs OOP Introduction
 
Selenium Training .pptx
Selenium Training .pptxSelenium Training .pptx
Selenium Training .pptx
 
Java chapter 3
Java   chapter 3Java   chapter 3
Java chapter 3
 
01. design pattern
01. design pattern01. design pattern
01. design pattern
 

Recently uploaded

Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESmohitsingh558521
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 

Recently uploaded (20)

Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 

OOP Concepts in C

  • 1. In this article we will discuss key concepts of object orientation with their practical implementation in C#. We will discuss here basics of OOPS including Interfaces, Access Modifiers, inheritance, polymorphism etc. This is my second article on csharp-corner.com. My first article was "Memory Management in .NET", you can find this article at http://www.c-sharpcorner.com/ UploadFile/tkagarwal/ MemoryManagementInNet11232005064832AM/ MemoryManagementInNet.aspx Key Concepts of Object Orientation Abstraction Encapsulation Polymorphism Inheritance. Abstraction is the ability to generalize an object as a data type that has a specific set of characteristics and is able to perform a set of actions. Object-oriented languages provide abstraction via classes. Classes define the properties and methods of an object type. Examples: You can create an abstraction of a dog with characteristics, such as color, height, and weight, and actions such as run and bite. The characteristics are called properties, and the actions are called methods. A Recordset object is an abstract representation of a set of data. Classes are blueprints for Object. Objects are instance of classes. C# Example of Class: public class Draw { // Class code. } Object References When we work with an object we are using a reference to that object. On the
  • 2. other hand, when we are working with simple data types such as Integer, we are working with the actual value rather than a reference. When we create a new object using the New keyword, we store a reference to that object in a variable. For instance: Draw MyDraw = new Draw; This code creates a new instance of Draw. We gain access to this new object via the MyDraw variable. This variable holds a reference to the object. Now we have a second variable, which also has a reference to that same object. We can use either variable interchangeably, since they both reference the exact same object. The thing we need to remember is that the variable we have is not the object itself but, rather, is just a reference or pointer to the object itself. Early binding means that our code directly interacts with the object, by directly calling its methods. Since the compiler knows the object's data type ahead of time, it can directly compile code to invoke the methods on the object. Early binding also allows the IDE to use IntelliSense to aid our development efforts; it allows the compiler to ensure that we are referencing methods that do exist and that we are providing the proper parameter values. Late binding means that our code interacts with an object dynamically at run-time. This provides a great deal of flexibility since our code literally doesn't care what type of object it is interacting with as long as the object supports the methods we want to call. Because the type of the object isn't known by the IDE or compiler, neither IntelliSense nor compile-time syntax checking is possible but we get unprecedented flexibility in exchange. If we enable strict type checking by using Option Strict On at the top of our code modules, then the IDE and compiler will enforce early binding behavior. By default, Option Strict is turned off and so we have easy access to the use of late binding within our code. Access Modifiers Access Modifiers are keywords used to specify the declared accessibility of a member of a type. Public is visible to everyone. A public member can be accessed using an instance of a class, by a class's internal code, and by any descendants of a
  • 3. class. Private is hidden and usable only by the class itself. No code using a class instance can access a private member directly and neither can a descendant class. Protected members are similar to private ones in that they are accessible only by the containing class. However, protected members also may be used by a descendant class. So members that are likely to be needed by a descendant class should be marked protected. Internal/Friend is public to the entire application but private to any outside applications. Internal is useful when you want to allow a class to be used by other applications but reserve special functionality for the application that contains the class. Internal is used by C# and Friend by VB .NET.
  • 4. Protected Internal may be accessed only by a descendant class that's contained in the same application as its base class. You use protected internal in situations where you want to deny access to parts of a class functionality to any descendant classes found in other applications. Composition of an OBJECT We use an interface to get access to an object's data and behavior. The object's data and behaviors are contained within the object, so a client application can treat the object like a black box accessible only through its interface. This is a key object-oriented concept called Encapsulation. The idea is that any programs that make use of this object won't have direct access to the behaviors or data-but rather those programs must make use of our object's interface. There are three main parts of Object:
  • 5. 1. Interface 2. Implementation or Behavior 3. Member or Instance variables Interface The interface is defined as a set of methods (Sub and Function routines), properties (Property routines), events, and fields (variables or attributes) that are declared Public in scope. Implementation or Behavior The code inside of a method is called the implementation. Sometimes it is also called behavior since it is this code that actually makes the object do useful work. Client applications can use our object even if we change the implementation- as long as we don't change the interface. As long as our method name and its parameter list and return data type remain unchanged, we can change the implementation all we want. So Method Signature depends on: Method name Data types of parameters Either Parameter is passed ByVal or ByRef. Return type of method. It is important to keep in mind that encapsulation is a syntactic tool-it allows our code to continue to run without change. However, it is not semantic- meaning that, just because our code continues to run, doesn't mean it continues to do what we actually wanted it to do. Member or Instance Variables The third key part of an object is its data, or state. Every instance of a class is absolutely identical in terms of its interface and its implementation-the only thing that can vary at all is the data contained within that particular object. Member variables are those declared so that they are available to all code within our class. Typically member variables are Private in scope-available only to the code in our class itself. They are also sometimes referred to as instance variables or as attributes. The .NET Framework also refers to them as fields.
  • 6. We shouldn't confuse instance variables with properties. A Property is a type of method that is geared around retrieving and setting values, while an instance variable is a variable within the class that may hold the value exposed by a Property. Interface looks like a class, but has no implementation. The only thing it contains is definitions of events, indexers, methods and/or properties. The reason interfaces only provide definitions is because they are inherited by classes and structs, which must provide an implementation for each interface member defined. So, what are interfaces good for if they don't implement functionality? They're great for putting together plug-n-play like architectures where components can be interchanged at will. Since all interchangeable components implement the same interface, they can be used without any extra programming. The interface forces each component to expose specific public members that will be used in a certain way. Because interfaces must be defined by inheriting classes and structs, they define a contract. For instance, if class foo inherits from the IDisposable interface, it is making a statement that it guarantees it has the Dispose() method, which is the only member of the IDisposable interface. Any code that wishes to use class foo may check to see if class foo inherits IDisposable. When the answer is true, then the code knows that it can call foo.Dispose(). Defining an Interface: MyInterface.c interface IMyInterface { void MethodToImplement(); } Above listing shows defines an interface named IMyInterface. A common naming convention is to prefix all interface names with a capital "I", but this is not mandatory. This interface has a single method named MethodToImplement(). This could have been any type of method declaration with different parameters and return types. Notice that this method does not have an implementation (instructions between curly braces- {}), but instead ends with a semi-colon, ";". This is because the interface only specifies the signature of methods that an inheriting class or struct must implement. All the methods of Interface are public by default and no access modifiers (like private, public) are allowed with any method of Interface.
  • 7. Using an Interface: InterfaceImplementer.cs class InterfaceImplementer : IMyInterface { public void MethodToImplement() { Console.WriteLine("MethodToImplement() called."); } } The InterfaceImplementer class in above listing implements the IMyInterface interface. Indicating that a class inherits an interface is the same as inheriting a class. In this case, the following syntax is used: class InterfaceImplementer : IMyInterface Note that this class inherits the IMyInterface interface; it must implement its all members. While implementing interface methods all those needs to be declared public only. It does this by implementing the MethodToImplement() method. Notice that this method implementation has the exact same signature, parameters and method name, as defined in the IMyInterface interface. Any difference will cause a compiler error. Interfaces may also inherit other interfaces. Following listing shows how inherited interfaces are implemented. Interface Inheritance: InterfaceInheritance.cs using System; interface IParentInterface { void ParentInterfaceMethod(); } interface IMyInterface : IParentInterface { void MethodToImplement(); } class InterfaceImplementer : IMyInterface { public void MethodToImplement() { Console.WriteLine("MethodToImplement() called."); } public void ParentInterfaceMethod() { Console.WriteLine("ParentInterfaceMethod() called."); } } The code in above listing contains two interfaces: IMyInterface and the interface it inherits, IParentInterface. When one interface inherits another,
  • 8. any implementing class or struct must implement every interface member in the entire inheritance chain. Since the InterfaceImplementer class in above listing inherits from IMyInterface, it also inherits IParentInterface. Therefore, the InterfaceImplementer class must implement the MethodToImplement() method specified in the IMyInterface interface and the ParentInterfaceMethod() method specified in the IParentInterface interface. In summary, you can implement an interface and use it in a class. Interfaces may also be inherited by other interface. Any class or struct that inherits an interface must also implement all members in the entire interface inheritance chain. Inheritance is the idea that one class, called a subclass, can be based on another class, called a base class. Inheritance provides a mechanism for creating hierarchies of objects. Inheritance is the ability to apply another class's interface and code to your own class. Normal base classes may be instantiated themselves, or inherited. Derived classes can inherit base class members marked with protected or greater access. The derived class is specialized to provide more functionality, in addition to what its base class provides. Inheriting base class members in derived class is not mandatory. Access Keywords base -> Access the members of the base class. this -> Refer to the current object for which a method is called. The base keyword is used to access members of the base class from within a derived class: Call a method on the base class that has been overridden by another method. Specify which base-class constructor should be called when creating instances of the derived class. A base class access is permitted only in a constructor, an instance method, or an instance property accessor. In following example, both the base class, Person, and the derived class, Employee, have a method named Getinfo. By using the base keyword, it is possible to call the Getinfo method on the base class, from within the derived class. // Accessing base class members using System;
  • 9. public class Person { protected string ssn = "444-55-6666"; protected string name = "John L. Malgraine"; public virtual void GetInfo() { Console.WriteLine("Name: {0}", name); Console.WriteLine("SSN: {0}", ssn); } } class Employee: Person { public string id = "ABC567EFG"; public override void GetInfo() { // Calling the base class GetInfo method: base.GetInfo(); Console.WriteLine("Employee ID: {0}", id); } } class TestClass { public static void Main() { Employee E = new Employee(); E.GetInfo(); } } Output Name: John L. Malgraine SSN: 444-55-6666 Employee ID: ABC567EFG Base class constructors can be called from derived classes. To call a base class constructor, use the base() constructor reference. This is desirable when it's necessary to initialize a base class appropriately. Here's an example that shows the derived class constructor with an address parameter: abstract public class Contact { private string address; public Contact(string b_address) { this.address = b_address; } } public class Customer : Contact
  • 10. { public Customer(string c_address) : base(C_address) { } } In this code, the Customer class does not have an address, so it passes the parameter to its base class constructor by adding a colon and the base keyword with the parameter to its declaration. This calls the Contact constructor with the address parameter, where the address field in Contact is initialized. One more example which shows how base-class constructor is called when creating instances of a derived class: using System; public class MyBase { int num; public MyBase() { Console.WriteLine("In MyBase()"); } public MyBase(int i) { num = i; Console.WriteLine("in MyBase(int i)"); } public int GetNum() { return num; } } public class MyDerived : MyBase { static int i = 32; // This constructor will call MyBase.MyBase() public MyDerived(int ii) : base() { } // This constructor will call MyBase.MyBase(int i) public MyDerived() : base(i) { } public static void Main() { MyDerived md = new MyDerived(); // calls public MyDerived() : base(i) and // passes i=32 in base class MyDerived md1 = new MyDerived(1); // call public MyDerived() : base(i) } }
  • 11. Output in MyBase(int i) in MyBase() The following example will not compile. It illustrates the effects of not including a default constructor in a class definition: abstract public class Contact { private string address; public Contact(string address) { this.address = address; } } public class Customer : Contact { public Customer(string address) { } } In this example, the Customer constructor does not call the base class constructor. This is obviously a bug, since the address field will never be initialized. When a class has no explicit constructor, the system assigns a default constructor. The default constructor automatically calls a default or parameterless base constructor. Here's an example of automatic default constructor generation that would occur for the preceding example: public Customer() : Contact() { } When a class does not declare any constructors, the code in this example is automatically generated. The default base class constructor is called implicitly when no derived class constructors are defined. Once a derived class constructor is defined, whether or not it has parameters, a default constructor will not be automatically defined, as the preceding code showed. Calling Base Class Members Derived classes can access the members of their base class if those members have protected or greater access. Simply use the member name in
  • 12. the appropriate context, just as if that member were a part of the derived class itself. Here's an example: abstract public class Contact { private string address; private string city; private string state; private string zip; public string FullAddress() { string fullAddress = address + 'n' + city + ',' + state + ' ' + zip; return fullAddress; } } public class Customer : Contact { public string GenerateReport() { string fullAddress = FullAddress(); // do some other stuff... return fullAddress; } } In above example, the GenerateReport() method of the Customer class calls the FullAddress() method in its base class, Contact. All classes have full access to their own members without qualification. Qualification refers to using a class name with the dot operator to access a class member- MyObject.SomeMethod(), for instance. This shows that a derived class can access its base class members in the same manner as its own. More Tips regarding Inheritance: A static member cannot be marked as override, virtual, or abstract. So following is an error: public static virtual void GetSSN() You can't call static methods of base class from derived class using base keyword. In above example if you declare a static method as follows: public class Person { protected string ssn = "444-55-6666"; protected string name = "John L. Malgraine"; public static void GetInfo() { // Implementation
  • 13. } } now you can't call this method using base.GetInfo() from derived class instead you have to call Person.GetInfo() from derived class. Inside Static members we can access only static fields, methods etc. Following example will give error, because we can't access name in GetInfo() because name is not static. public class Person { protected string ssn = "444-55-6666"; protected string name = "John L. Malgraine"; public static void GetInfo() { Console.WriteLine("Name: {0}", name); Console.WriteLine("SSN: {0}", ssn); } } Virtual or abstract members cannot be private. If you are not overriding a virtual method of base class in derived class, you can't use base class method by using base keyword in derived class. Also when you will create an instance of derived class, it will call derived class method and you will only be able to access base class method when you will create instance of base class. You can't decrease access level of a method in derived class when you are overriding a base class method in derived class, vice versa is possible. Means you can make protected method of base class to public in derived class. The "this" keyword refers to: the current instance for which a method is called. Static member functions do not have a this pointer. The this keyword can be used to access members from within constructors, instance methods, and instance accessors. The following are common uses of this: To qualify members hidden by similar names, for example: public Employee(string name, string alias) { this.name = name;
  • 14. this.alias = alias; } In above example, this.name refers to private variable name in the class. If we write name = name, then this will refer to argument name of the constructor Employee and not to private variable name in the class. In this case private variable name will never be initialized. To pass an object as a parameter to other methods, for example: CalcTax(this); To declare indexers, for example: public int this [int param] { get { return array[param]; } set { array[param] = value; } } It is an error to refer to this in a static method, static property accessor, or variable initializer of a field declaration. In this example, this is used to qualify the Employee class members, name and alias, which are hidden by similar names. It is also used to pass an object to the method CalcTax, which belongs to another class. // keywords_this.cs // this example using System; public class Employee { public string name; public string alias; public decimal salary = 3000.00m; // Constructor: public Employee(string name, string alias) { // Use this to qualify the fields, name and alias: this.name = name; this.alias = alias; } // Printing method: public void printEmployee() {
  • 15. Console.WriteLine("Name: {0}nAlias: {1}", name, alias); // Passing the object to the CalcTax method by using this: Console.WriteLine("Taxes: {0:C}", Tax.CalcTax(this)); } } public class Tax { public static decimal CalcTax(Employee E) { return (0.08m*(E.salary)); } } public class MainClass { public static void Main() { // Create objects: Employee E1 = new Employee ("John M. Trainer", "jtrainer"); // Display results: E1.printEmployee(); } } Output Name: John M. Trainer Alias: jtrainer Taxes: $240.00 Abstract Classes Abstract classes are a special type of base classes. In addition to normal class members, they have abstract class members. These Abstract class members are methods and properties that are declared without an implementation. All classes derived directly from abstract classes must implement all of these abstract methods and properties. Abstract classes can never be instantiated. This would be illogical, because of the members without implementations.So what good is a class that can't be instantiated? Lots! Abstract classes sit toward the top of a class hierarchy. They establish structure and meaning to code. They make frameworks easier to build. This is possible because abstract classes have information and behavior common to all derived classes in a framework. Take a look at the following example: abstract public class Contact // Abstract Class Contact. { protected string name; public Contact() {
  • 16. // statements... } public abstract void generateReport(); abstract public string Name { get; set; } } Contact, is an abstract class. Contact has two abstract members, and it has an abstract method named generateReport(). This method is declared with the abstract modifier in front of the method declaration. It has no implementation (no braces) and is terminated with a semicolon. The Name property is also declared abstract. The accessors of properties are terminated with semicolons. public class Customer : Contact // Customer Inherits Abstract Class Contact. { string gender; decimal income; int numberOfVisits; public Customer() { // statements } public override void generateReport() { // unique report } public override string Name { get { numberOfVisits++; return name; } set { name = value; numberOfVisits = 0; } } } public class SiteOwner : Contact { int siteHits; string mySite; public SiteOwner() { // statements
  • 17. } public override void generateReport() { // unique report } public override string Name { get { siteHits++; return name; } set { name = value; siteHits = 0; } } } The abstract base class Contact has two derived classes, Customer and SiteOwner. Both of these derived classes implement the abstract members of the Contact class. The generateReport() method in each derived class has an override modifier in its declaration. Likewise, the Name declaration contains an override modifier in both Customer and SiteOwner. C# requires explicit declaration of intent when overriding methods. This feature promotes safe code by avoiding the accidental overriding of base class methods, which is what actually does happen in other languages. Leaving out the override modifier generates an error. Similarly, adding a new modifier also generates an error. Abstract methods must be overridden and cannot be hidden, which the new modifier or the lack of a modifier would be trying to do. The most famous of all abstract classes is the Object class. It may be referred to as object or Object, but it's still the same class. Object is the base class for all other classes in C#. It's also the default base class when a base class is not specified. The following class declarations produce the same exact results: abstract public class Contact : Object { // class members } abstract public class Contact { // class members }
  • 18. Object is implicitly included as a base class if it is not already declared. Besides providing the abstract glue to hold together the C# class framework, object includes built-in functionality, some of which is useful for derived classes to implement. Difference between Interface and Abstract Class Interfaces are closely related to abstract classes that have all members abstract. For an abstract class, at least one method of the class must be an abstract method that means it may have concrete methods. For an interface, all the methods must be abstract Class that implements an interface much provide concrete implementation of all the methods definition in an interface or else must be declare an abstract class In C#, multiple inheritance is possible only through implementation of multiple interfaces. Abstract class can only be derived once. An interface defines a contract and can only contains four entities viz methods, properties, events and indexes. An interface thus cannot contain constants, fields, operators, constructors, destructors, static constructors, or types. Also an interface cannot contain static members of any kind. The modifiers abstract, public, protected, internal, private, virtual, override is disallowed, as they make no sense in this context. Class members that implement the interface members must be publicly accessible. Overriding Summery: A derived class may override a virtual method of the base class with the keyword override. The following restrictions must be followed. Keyword override is used in the definition of child class method that is going to override the base class's virtual method. The return type must be the same as the virtual method have in base class. The name of the method should also be same. The parameter-list must also be same in order, number and type of parameters. The accessibility of the overriding method should not be more restricted than that of the accessibility defined with virtual method of the base class. This accessibility either be the same or less restricted.
  • 19. The virtual methods can be sealed in the child or derived classes to prevent further modifications in the implementation of the virtual method in the derived classes, by declaring them as sealed methods. Hiding Base Class Members Sometimes derived class members have the same name as a corresponding base class member. In this case, the derived member is said to be "hiding" the base class member. When hiding occurs, the derived member is masking the functionality of the base class member. Users of the derived class won't be able to see the hidden member; they'll see only the derived class member. The following code shows how hiding a base class member works. abstract public class Contact { private string address; private string city; private string state; private string zip; public string FullAddress() { string fullAddress =address + 'n' +city + ',' + state + ' ' + zip; return fullAddress; } } public class SiteOwner : Contact { public string FullAddress() { string fullAddress; // create an address... return fullAddress; } } In this example, both SiteOwner and its base class, Contact, have a method named FullAddress(). The FullAddress() method in the SiteOwner class hides the FullAddress() method in the Contact class. This means that when an instance of a SiteOwner class is invoked with a call to the FullAddress() method, it is the SiteOwner class FullAddress() method that is called, not the FullAddress() method of the Contact class. Although a base class member may be hidden, the derived class can still access it. It does this through the base identifier. Sometimes this is desirable. It is often useful to take advantage of the base class functionality
  • 20. and then add to it with the derived class code. The next example shows how to refer to a base class method from the derived class. abstract public class Contact { private string address; private string city; private string state; private string zip; public string FullAddress() { string fullAddress =address + 'n' +city + ',' + state + ' ' + zip; return fullAddress; } } public class SiteOwner : Contact { public string FullAddress() { string fullAddress = base.FullAddress(); // do some other stuff... return fullAddress; } } In this particular example, the FullAddress() method of the Contact class is called from within the FullAddress() method of the SiteOwner class. This is accomplished with a base class reference. This provides another way to reuse code and add on to it with customized behavior. Versioning Versioning, in the context of inheritance, is a C# mechanism that allows modification of classes (creating new versions) without accidentally changing the meaning of the code. Hiding a base class member with the methods previously described generates a warning message from the compiler. This is because of the C# versioning policy. It's designed to eliminate a class of problems associated with modifications to base classes. Here's the scenario: A developer creates a class that inherits from a third- party library. For the purposes of this discussion, we assume that the Contact class represents the third-party library. Here's the example: public class Contact { // does not include FullAddress() method } public class SiteOwner : Contact {
  • 21. public string FullAddress() { string fullAddress = mySite.ToString(); return fullAddress; } } In this example, the FullAddress() method does not exist in the base class. There is no problem yet. Later on, the creators of the third-party library update their code. Part of this update includes a new member in a base class with the exact same name as the derived class: public class Contact { private string address; private string city; private string state; private string zip; public string FullAddress() { string fullAddress =address + 'n' +city + ',' + state + ' ' + zip; return fullAddress; } } public class SiteOwner : Contact { public string FullAddress() { string fullAddress = mySite.ToString(); return fullAddress; } } In this code, the base class method FullAddress() contains different functionality than the derived class method. In other languages, this scenario would break the code because of implicit polymorphism. However, this does not break any code in C# because when the FullAddress() method is called on SiteOwner, it is still the SiteOwner class method that gets called. This scenario generates a warning message. One way to eliminate the warning message is to place a new modifier in front of the derived class method name, as the following example shows: using System; public class WebSite { public string SiteName; public string URL; public string Description; public WebSite()
  • 22. { } public WebSite( string strSiteName, string strURL, string strDescription ) { SiteName = strSiteName; URL = strURL; Description = strDescription; } public override string ToString() { return SiteName + ", " +URL + ", " +Description; } } public class Contact { public string address; public string city; public string state; public string zip; public string FullAddress() { string fullAddress =address + 'n' +city + ',' + state + ' ' + zip; return fullAddress; } } public class SiteOwner : Contact { int siteHits; string name; WebSite mySite; public SiteOwner() { mySite = new WebSite(); siteHits = 0; } public SiteOwner(string aName, WebSite aSite) { mySite = new WebSite(aSite.SiteName,aSite.URL,aSite.Description); Name = aName; } new public string FullAddress() { string fullAddress = mySite.ToString(); return fullAddress; } public string Name { get { siteHits++; return name; } set
  • 23. { name = value; siteHits = 0; } } } public class Test { public static void Main() { WebSite mySite = new WebSite("Le Financier","http://www.LeFinancier.com","Fancy Financial Site"); SiteOwner anOwner = new SiteOwner("John Doe", mySite); string address; anOwner.address = "123 Lane Lane"; anOwner.city = "Some Town"; anOwner.state = "HI"; anOwner.zip = "45678"; address = anOwner.FullAddress(); // Different Results Console.WriteLine("Address: n{0}n", address); } } Here's the output: Address: Le Financier, http://www.LeFinancier.com, Fancy Financial Site This has the effect of explicitly letting the compiler know the developer's intent. Placing the new modifier in front of the derived class member states that the developers know there is a base class method with the same name, and they definitely want to hide that member. This prevents breakage of existing code that depends on the implementation of the derived class member. With C#, the method in the derived class is called when an object of the derived class type is used. Likewise, the method in the base class is called when an object of the Base class type is called. Another problem this presents is that the base class may present some desirable new features that wouldn't be available through the derived class. To use these new features requires one of a few different workarounds. One option would be to rename the derived class member, which would allow programs to use a base class method through a derived class member. The drawback to this option would be if there were other classes relying upon the implementation of the derived class member with the same name. This scenario will break code and, for this reason, is considered extremely bad form. Another option is to define a new method in the derived class that called the base class method. This allows users of the derived class to have the new
  • 24. functionality of the base class, yet retain their existing functionality with the derived class. While this would work, there are maintainability concerns for the derived class. Sealed Classes Sealed classes are classes that can't be derived from. To prevent other classes from inheriting from a class, make it a sealed class. There are a couple good reasons to create sealed classes, including optimization and security. Sealing a class avoids the system overhead associated with virtual methods. This allows the compiler to perform certain optimizations that are otherwise unavailable with normal classes. Another good reason to seal a class is for security. Inheritance, by its very nature, dictates a certain amount of protected access to the internals of a potential base class. Sealing a class does away with the possibility of corruption by derived classes. A good example of a sealed class is the String class. The following example shows how to create a sealed class: public sealed class CustomerStats { string gender; decimal income; int numberOfVisits; public CustomerStats() { } } public class CustomerInfo : CustomerStats // error { } This example generates a compiler error. Since the CustomerStats class is sealed, it can't be inherited by the CustomerInfo class.The CustomerStats class was meant to be used as an encapsulated object in another class. This is shown by the declaration of a CustomerStats object in the Customer class. public class Customer { CustomerStats myStats; // okay } Polymorphism
  • 25. Polymorphism is reflected in the ability to write one routine that can operate on objects from more than one class-treating different objects from different classes in exactly the same way. For instance, if both Customer and Vendor objects have a Name property, and we can write a routine that calls the Name property regardless of whether we're using a Customer or Vendor object, then we have polymorphism. A vehicle is a good example of polymorphism. A vehicle interface would only have those properties and methods that all vehicles have, a few of which might include paint color, number of doors, accelerator, and ignition. These properties and methods would apply to all types of vehicles including cars, trucks, and semi-trucks. Polymorphism will not implement code behind the vehicle's properties and methods. Instead, polymorphism is the implementation of an interface. If the car, truck, and semitruck all implement the same vehicle interface, then the client code for all three classes can be exactly the same. C# gives us polymorphism through inheritance. C# provides a keyword virtual that is used in the definition of a method to support polymorphism. Child class are now free to provide their own implementation of this virtual method, that is called overriding. The following points are important regarding virtual keyword:- If the method is not virtual, the compiler simply uses the reference type to invoke the appropriate method. If the method is virtual, the compiler will generate code to checkup the reference type at runtime it is actually denoting to, then the appropriate method is called from the class of the reference type. When a virtual method is called, runtime check (late method binding) is made to identify the object and appropriate method is invoked, all this is done at runtime. In case of non-virtual methods, this information is available at compile time, so no runtime check to identify the object is made, so slightly efficient in the way non-virtual methods are called. But the behavior of virtual method is useful in many ways; the functionality they provide is fair enough to bear this slight loss of performance. Implementing Polymorphism
  • 26. The key factor here is the ability to dynamically invoke methods in a class based on their type. Essentially, a program would have a group of objects, examine the type of each one, and execute the appropriate method. Here's an example: using System; public class WebSite { public string SiteName; public string URL; public string Description; public WebSite() { } public WebSite( string strSiteName, string strURL, string strDescription ) { SiteName = strSiteName; URL = strURL; Description = strDescription; } public override string ToString() { return SiteName + ", " +URL + ", " +Description; } } When we inherit above class, we have two choices to invoke constructor of the class. So this is an example of design time polymorphism. Here at design time we have to decide which method we need to invoke while inheriting the class. Polymorphism is the capability of a program to carry out dynamic operations by implementing methods of multiple derived classes through a common base class reference. Another definition of polymorphism is the ability to treat different objects the same way. This means that the runtime type of an object determines its behavior rather than the compile-time type of its reference. Resource Allocation The Microsoft .NET common language runtime requires that all resources be allocated from the managed heap. Objects are automatically freed when they are no longer needed by the application. When a process is initialized, the runtime reserves a contiguous region of address space that initially has no storage allocated for it. This address space region is the managed heap. The heap also maintains a pointer. This pointer indicates where the next object is to be allocated within the heap.
  • 27. Initially, the pointer is set to the base address of the reserved address space region. An application creates an object using the new operator. This operator first makes sure that the bytes required by the new object fit in the reserved region (committing storage if necessary). If the object fits, then pointer points to the object in the heap, this object's constructor is called, and the new operator returns the address of the object. Above fig shows a managed heap consisting of three objects: A, B, and C. The next object to be allocated will be placed where NextObjPtr points (immediately after object C). When an application calls the new operator to create an object, there may not be enough address space left in the region to allocate to the object. The heap detects this by adding the size of the new object to NextObjPtr. If NextObjPtr is beyond the end of the address space region, then the heap is full and a collection must be performed. In reality, a collection occurs when generation 0 is completely full. Briefly, a generation is a mechanism implemented by the garbage collector in order to improve performance. The idea is that newly created objects are part of a young generation, and objects created early in the application's lifecycle are in an old generation. Separating objects into generations can allow the garbage collector to collect specific generations instead of collecting all objects in the managed heap. The Garbage Collection Algorithm The garbage collector checks to see if there are any objects in the heap that are no longer being used by the application. If such objects exist, then the memory used by these objects can be reclaimed. (If no more memory is available for the heap, then the new operator throws an OutOfMemoryException.) Every application has a set of roots. Roots identify storage locations, which refer to objects on the managed heap or to objects that are set to null. For example, all the global and static object pointers in an application are considered part of the application's roots. In addition, any local variable/parameter object pointers on a thread's stack are considered part of the application's roots. Finally, any CPU registers containing pointers to
  • 28. objects in the managed heap are also considered part of the application's roots. The list of active roots is maintained by the just-in-time (JIT) compiler and common language runtime, and is made accessible to the garbage collector's algorithm. When the garbage collector starts running, it makes the assumption that all objects in the heap are garbage. In other words, it assumes that none of the application's roots refer to any objects in the heap. Now, the garbage collector starts walking the roots and building a graph of all objects reachable from the roots. For example, the garbage collector may locate a global variable that points to an object in the heap. Following fig shows a heap with several allocated objects where the application's roots refer directly to objects A, C, D, and F. All of these objects become part of the graph. When adding object D, the collector notices that this object refers to object H, and object H is also added to the graph. The collector continues to walk through all reachable objects recursively. Once this part of the graph is complete, the garbage collector checks the next root and walks the objects again. As the garbage collector walks from object to object, if it attempts to add an object to the graph that it previously added, then the garbage collector can stop walking down that path. This serves two purposes. First, it helps performance significantly since it doesn't walk through a set of objects more than once. Second, it prevents infinite loops should you have any circular linked lists of objects. Once all the roots have been checked, the garbage collector's graph contains the set of all objects that are somehow reachable from the application's roots; any objects that are not in the graph are not accessible by the application, and are therefore considered garbage. The garbage collector now walks through the heap linearly, looking for contiguous blocks of garbage objects (now considered free space). The garbage collector then shifts the non-garbage objects down in memory (using the standard memcpy function), removing all of the gaps in the heap. Of course, moving the objects in memory invalidates all pointers to the objects. So the garbage collector must modify the application's roots so that the pointers point to the objects' new locations. In addition, if any object contains a pointer to another object, the garbage collector is responsible for correcting these pointers as well. Following fig shows the managed heap after a collection.
  • 29. After all the garbage has been identified, all the non-garbage has been compacted, and all the non-garbage pointers have been fixed-up, the NextObjPtr is positioned just after the last non-garbage object. At this point, the new operation is tried again and the resource requested by the application is successfully created. GC generates a significant performance hit, and this is the major downside of using a managed heap. However, keep in mind that GCs only occur when the heap is full and, until then, the managed heap is significantly faster than a C-runtime heap. The runtime's garbage collector also offers some optimizations using Generations that greatly improve the performance of garbage collection. You no longer have to implement any code that manages the lifetime of any resources that your application uses. Now it is not possible to leak resources, since any resource not accessible from your application's roots can be collected at some point. Also it is not possible to access a resource that is freed, since the resource won't be freed if it is reachable. If it's not reachable, then your application has no way to access it. Following code demonstrates how resources are allocated and managed: class Application { public static int Main(String[] args) { // ArrayList object created in heap, myArray is now in root ArrayList myArray = new ArrayList(); // Create 10000 objects in the heap for (int x = 0; x < 10000; x++) { myArray.Add(new Object()); // Object object created in heap } // Right now, myArray is a root (on the thread's stack). So, // myArray is reachable and the 10000 objects it points to are also reachable. Console.WriteLine(myArray.Count); // After the last reference to myArray in the code, myArray is not a root. // Note that the method doesn't have to return, the JIT compiler knows // to make myArray not a root after the last reference to it in the code. // Since myArray is not a root, all 10001 objects are not reachable // and are considered garbage. However, the objects are not // collected until a GC is performed. } } If GC is so great, you might be wondering why it isn't in ANSI C++. The reason is that a garbage collector must be able to identify an application's roots and must also be able to find all object pointers. The problem with
  • 30. C++ is that it allows casting a pointer from one type to another, and there's no way to know what a pointer refers to. In the common language runtime, the managed heap always knows the actual type of an object, and the metadata information is used to determine which members of an object refer to other objects. Generations One feature of the garbage collector that exists purely to improve performance is called generations. A generational garbage collector (also known as an ephemeral garbage collector) makes the following assumptions: The newer an object is, the shorter its lifetime will be. The older an object is, the longer its lifetime will be. Newer objects tend to have strong relationships to each other and are frequently accessed around the same time. Compacting a portion of the heap is faster than compacting the whole heap. When initialized, the managed heap contains no objects. Objects added to the heap are said to be in generation 0, as you can see in following fig. Stated simply, objects in generation 0 are young objects that have never been examined by the garbage collector. Now, if more objects are added to the heap, the heap fills and a garbage collection must occur. When the garbage collector analyzes the heap, it builds the graph of garbage (shown here in Green) and non-garbage objects. Any objects that survive the collection are compacted into the left-most portion of the heap. These objects have survived a collection, are older, and are now considered to be in generation 1. As even more objects are added to the heap, these new, young objects are placed in generation 0. If generation 0 fills again, a GC is performed. This time, all objects in generation 1 that survive are compacted and considered to be in generation 2 (see following fig). All survivors in generation 0 are now compacted and considered to be in generation 1. Generation 0 currently contains no objects, but all new objects will go into generation 0.
  • 31. Currently, generation 2 is the highest generation supported by the runtime's garbage collector. When future collections occur, any surviving objects currently in generation 2 simply stay in generation 2. Generational GC Performance Optimizations Generational garbage collecting improves performance. When the heap fills and a collection occurs, the garbage collector can choose to examine only the objects in generation 0 and ignore the objects in any greater generations. After all, the newer an object is, the shorter its lifetime is expected to be. So, collecting and compacting generation 0 objects is likely to reclaim a significant amount of space from the heap and be faster than if the collector had examined the objects in all generations. A generational collector can offer more optimizations by not traversing every object in the managed heap. If a root or object refers to an object in an old generation, the garbage collector can ignore any of the older objects' inner references, decreasing the time required to build the graph of reachable objects. Of course, it is possible that an old object refers to a new object. So that these objects are examined, the collector can take advantage of the system's write-watch support (provided by the Win32 GetWriteWatch function in Kernel32.dll). This support lets the collector know which old objects (if any) have been written to since the last collection. These specific old objects can have their references checked to see if they refer to any new objects. If collecting generation 0 doesn't provide the necessary amount of storage, then the collector can attempt to collect the objects from generations 1 and 0. If all else fails, then the collector can collect the objects from all generations-2, 1, and 0. One of the assumptions stated earlier was that newer objects tend to have strong relationships to each other and are frequently accessed around the same time. Since new objects are allocated contiguously in memory, you gain performance from locality of reference. More specifically, it is highly likely that all the objects can reside in the CPU's cache. Your application will access these objects with phenomenal speed since the CPU will be able to perform most of its manipulations without having cache misses which forces RAM access. Microsoft's performance tests show that managed heap allocations are faster than standard allocations performed by the Win32 HeapAlloc function. These
  • 32. tests also show that it takes less than 1 millisecond on a 200 MHz Pentium to perform a full GC of generation 0. It is Microsoft's goal to make GCs take no more time than an ordinary page fault. Disadvantages of Win32 heap: Most heaps (like the C runtime heap) allocate objects wherever they find free space. Therefore, if I create several objects consecutively, it is quite possible that these objects will be separated by megabytes of address space. However, in the managed heap, allocating several objects consecutively ensures that the objects are contiguous in memory. When memory is allocated from a Win32 heap, the heap must be examined to find a block of memory that can satisfy the request. This is not required in managed heap, since here objects are contiguous in memory. In Win32 heap, data structures that the heap maintains must be updated. The managed heap, on the other hand, only needs to increment the heap pointer. Finalization The garbage collector offers an additional feature that you may want to take advantage of: finalization. Finalization allows a resource to gracefully clean up after itself when it is being collected. By using finalization, a resource representing a file or network connection is able to clean itself up properly when the garbage collector decides to free the resource's memory. When the garbage collector detects that an object is garbage, the garbage collector calls the object's Finalize method (if it exists) and then the object's memory is reclaimed. For example, let's say you have the following type (in C#): public class BaseObj { public BaseObj() { } protected override void Finalize() { // Perform resource cleanup code here // Example: Close file/Close network connection Console.WriteLine("In Finalize."); } }
  • 33. Now you can create an instance of this object by calling: BaseObj bo = new BaseObj(); Some time in the future, the garbage collector will determine that this object is garbage. When that happens, the garbage collector will see that the type has a Finalize method and will call the method, causing "In Finalize" to appear in the console window and reclaiming the memory block used by this object. Many developers who are used to programming in C++ draw an immediate correlation between a destructor and the Finalize method. However, object finalization and destructors have very different semantics and it is best to forget everything you know about destructors when thinking about finalization. Managed objects never have destructors. When designing a type it is best to avoid using a Finalize method. There are several reasons for this: Finalizable objects get promoted to older generations, which increases memory pressure and prevents the object's memory from being collected when the garbage collector determines the object is garbage. In addition, all objects referred to directly or indirectly by this object get promoted as well. Finalizable objects take longer to allocate. Forcing the garbage collector to execute a Finalize method can significantly hurt performance. Remember, each object is finalized. So if I have an array of 10,000 objects, each object must have its Finalize method called. Finalizable objects may refer to other (non-finalizable) objects, prolonging their lifetime unnecessarily. In fact, you might want to consider breaking a type into two different types: a lightweight type with a Finalize method that doesn't refer to any other objects, and a separate type without a Finalize method that does refer to other objects. You have no control over when the Finalize method will execute. The object may hold on to resources until the next time the garbage collector runs. When an application terminates, some objects are still reachable and will not have their Finalize method called. This can happen if
  • 34. background threads are using the objects or if objects are created during application shutdown or AppDomain unloading. In addition, by default, Finalize methods are not called for unreachable objects when an application exits so that the application may terminate quickly. Of course, all operating system resources will be reclaimed, but any objects in the managed heap are not able to clean up gracefully. You can change this default behavior by calling the System.GC type's RequestFinalizeOnShutdown method. However, you should use this method with care since calling it means that your type is controlling a policy for the entire application. The runtime doesn't make any guarantees as to the order in which Finalize methods are called. For example, let's say there is an object that contains a pointer to an inner object. The garbage collector has detected that both objects are garbage. Furthermore, say that the inner object's Finalize method gets called first. Now, the outer object's Finalize method is allowed to access the inner object and call methods on it, but the inner object has been finalized and the results may be unpredictable. For this reason, it is strongly recommended that Finalize methods not access any inner, member objects. If you determine that your type must implement a Finalize method, then make sure the code executes as quickly as possible. Avoid all actions that would block the Finalize method, including any thread synchronization operations. Also, if you let any exceptions escape the Finalize method, the system just assumes that the Finalize method returned and continues calling other objects' Finalize methods. When the compiler generates code for a constructor, the compiler automatically inserts a call to the base type's constructor. Likewise, when a C++ compiler generates code for a destructor, the compiler automatically inserts a call to the base type's destructor. Finalize methods are different from destructors. The compiler has no special knowledge about a Finalize method, so the compiler does not automatically generate code to call a base type's Finalize method. If you want this behavior-and frequently you do-then you must explicitly call the base type's Finalize method from your type's Finalize method: public class BaseObj { public BaseObj() { } protected override void Finalize() { Console.WriteLine("In Finalize.");
  • 35. base.Finalize(); // Call base type's Finalize } } Note that you'll usually call the base type's Finalize method as the last statement in the derived type's Finalize method. This keeps the base object alive as long as possible. Since calling a base type Finalize method is common, C# has a syntax that simplifies your work. In C#, the following code: class MyObject { MyObject() { } } causes the compiler to generate this code: class MyObject { protected override void Finalize() { base.Finalize(); } } Note that this C# syntax looks identical to the C++ language's syntax for defining a destructor. But remember, C# doesn't support destructors. Don't let the identical syntax fool you. Finalization Internals When an application creates a new object, the new operator allocates the memory from the heap. If the object's type contains a Finalize method, then a pointer to the object is placed on the finalization queue. The finalization queue is an internal data structure controlled by the garbage collector. Each entry in the queue points to an object that should have its Finalize method called before the object's memory can be reclaimed. Following fig shows a heap containing several objects. Some of these objects are reachable from the application's roots, and some are not. When objects C, E, F, I, and J were created, the system detected that these objects had Finalize methods and pointers to these objects were added to the finalization queue.
  • 36. When a GC occurs, objects B, E, G, H, I, and J are determined to be garbage. The garbage collector scans the finalization queue looking for pointers to these objects. When a pointer is found, the pointer is removed from the finalization queue and appended to the freachable queue (pronounced "F-reachable"). The freachable queue is another internal data structure controlled by the garbage collector. Each pointer in the freachable queue identifies an object that is ready to have its Finalize method called. After the collection, the managed heap looks like following fig. Here, you see that the memory occupied by objects B, G, and H has been reclaimed because these objects did not have a Finalize method that needed to be called. However, the memory occupied by objects E, I, and J could not be reclaimed because their Finalize method has not been called yet. There is a special runtime thread dedicated to calling Finalize methods. When the freachable queue is empty (which is usually the case), this thread sleeps. But when entries appear, this thread wakes, removes each entry from the queue, and calls each object's Finalize method. Because of this, you should not execute any code in a Finalize method that makes any assumption about the thread that's executing the code. For example, avoid accessing thread local storage in the Finalize method. The interaction of the finalization queue and the freachable queue is quite fascinating. First, let me tell you how the freachable queue got its name. The f is obvious and stands for finalization; every entry in the freachable queue should have its Finalize method called. The "reachable" part of the name means that the objects are reachable. To put it another way, the freachable queue is considered to be a root just like global and static variables are roots. Therefore, if an object is on the freachable queue, then the object is reachable and is not garbage. In short, when an object is not reachable, the garbage collector considers the object garbage. Then, when the garbage collector moves an object's entry from the finalization queue to the freachable queue, the object is no longer considered garbage and its memory is not reclaimed. At this point, the garbage collector has finished identifying garbage. Some of the objects identified as garbage have been reclassified as not garbage. The garbage collector compacts the reclaimable memory and the special runtime thread empties the freachable queue, executing each object's Finalize method.
  • 37. The next time the garbage collector is invoked, it sees that the finalized objects are truly garbage, since the application's roots don't point to it and the freachable queue no longer points to it. Now the memory for the object is simply reclaimed. The important thing to understand here is that two GCs are required to reclaim memory used by objects that require finalization. In reality, more than two collections may be necessary since the objects could get promoted to an older generation. Above fig shows what the managed heap looks like after the second GC. Dispose Method Use this method to close or release unmanaged resources such as files, streams, and handles held by an instance of the class that implements this interface. This method is, by convention, used for all tasks associated with freeing resources held by an object, or preparing an object for reuse. When implementing this method, objects must seek to ensure that all held resources are freed by propagating the call through the containment hierarchy. For example, if an object A allocates an object B, and object B allocates an object C, then A's Dispose implementation must call Dispose on B, which must in turn call Dispose on C. Objects must also call the Dispose method of their base class if the base class implements IDisposable. If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The object must not throw an exception if its Dispose method is called multiple times. Dispose can throw an exception if an error occurs because a resource has already been freed and Dispose had not been called previously. Because the Dispose method must be called explicitly, objects that implement IDisposable must also implement a finalizer to handle freeing resources when Dispose is not called. By default, the garbage collector will automatically call an object's finalizer prior to reclaiming its memory. However, once the Dispose method has been called, it is typically unnecessary for the garbage collector to call the disposed object's finalizer. To prevent automatic finalization, Dispose implementations can call the GC.SuppressFinalize method. Direct Control with System.GC The System.GC type allows your application some direct control over the garbage collector. You can query the maximum generation supported by the
  • 38. managed heap by reading the GC.MaxGeneration property. Currently, the GC.MaxGeneration property always returns 2. It is also possible to force the garbage collector to perform a collection by calling one of the two methods shown here: void GC.Collect(Int32 Generation) void GC.Collect() The first method allows you to specify which generation to collect. You may pass any integer from 0 to GC.MaxGeneration, inclusive. Passing 0 causes generation 0 to be collected; passing 1 cause generation 1 and 0 to be collected; and passing 2 causes generation 2, 1, and 0 to be collected. The version of the Collect method that takes no parameters forces a full collection of all generations and is equivalent to calling: GC.Collect(GC.MaxGeneration); Under most circumstances, you should avoid calling any of the Collect methods; it is best to just let the garbage collector run on its own accord. However, since your application knows more about its behavior than the runtime does, you could help matters by explicitly forcing some collections. For example, it might make sense for your application to force a full collection of all generations after the user saves his data file. I imagine Internet browsers performing a full collection when pages are unloaded. You might also want to force a collection when your application is performing other lengthy operations; this hides the fact that the collection is taking processing time and prevents a collection from occurring when the user is interacting with your application. The GC type also offers a WaitForPendingFinalizers method. This method simply suspends the calling thread until the thread processing the freachable queue has emptied the queue, calling each object's Finalize method. In most applications, it is unlikely that you will ever have to call this method. Lastly, the garbage collector offers two methods that allow you to determine which generation an object is currently in: Int32 GetGeneration(Object obj) Int32 GetGeneration(WeakReference wr) The first version of GetGeneration takes an object reference as a parameter, and the second version takes a WeakReference reference as a parameter. Of course, the value returned will be somewhere between 0 and GC.MaxGeneration, inclusive.
  • 39. Chapter Objective OOP's overview Classes and Objects Constructor and Destructor Function Overloading Encapsulation Inheritance Interface Polymorphism OOP's overview Object-oriented programming (OOP) is the core ingredient of the .NET framework. OOP is so important that, before embarking on the road to .NET, you must understand its basic principles and terminology to write even a simple program. The fundamental idea behind OOP is to combine into a single unit both data and the methods that operate on that data; such units are called an object. All OOP languages provide mechanisms that help you implement the object-oriented model. They are encapsulation, inheritance, polymorphism and reusability. Let's now take a brief look at these concepts. Encapsulation Encapsulation binds together code and the data it manipulates and keeps them both safe from outside interference and misuse. Encapsulation is a protective container that prevents code and data from being accessed by other code defined outside the container. Inheritance Inheritance is the process by which one object acquires the properties of another object. A type derives from a base type, taking all the base type members fields and functions. Inheritance is most useful when you need to add functionality to an existing type. For example all .NET classes inherit from the System.Object class, so a class can include new functionality as well as use the existing object's class functions and properties as well. Polymorphism Polymorphism is a feature that allows one interface to be used for a general class of action. This concept is often expressed as "one interface, multiple actions". The specific action is determined by the exact nature of circumstances. Reusability Once a class has been written, created and debugged, it can be distributed to other programmers for use in their own program. This is called reusability, or in .NET terminology this concept is called a component or a DLL. In OOP, however, inheritance provides an important extension to the idea of reusability. A programmer can use an existing class and without modifying it, add additional features to it. Simple "Hello World" C# Program
  • 40. This simple one-class console "Hello world" program demonstrates many fundamental concepts throughout this article and several future articles. C# code using System; namespace oops { //class definition public class SimpleHelloWorld { //Entry point of the program static void Main(string[] args) { //print Hello world" Console.WriteLine("Hello World!"); } } } So SimpleHelloWorld is the name of the class that contains the Main () method. On line 1 , a using directive indicates to the compiler that this source file refers to classes and constructs declared within the System namespace. Line 6 with the public keyword indicates the program accessibility scope for other applications or components. At line 7 there appears an opening curly brace ("{") which indicates the beginning of the SimpleHelloWorld class body. Everything belongs to the class, like fields, properties and methods appear in the class body between the opening and closing braces. The purpose of the Main () method is to provide an entry point for application execution. The static keyword in the Main () method states that this method would be executed without instantiating the class. Compiling the Program You can compile a C# program into either an assembly or a module. If the program has one class that contains a Main () method then it can be compiled directly into an assembly. This file has an ".exe" extension. A program with no Main() method can be compiled into a module as in the following: csc /target:module "program name"
  • 41. You can then compile this program by F9 or by simply running the C# command line compiler (csc.exe) against the source file as the following: csc oops.cs Classes and Objects Classes are special kinds of templates from which you can create objects. Each object contains data and methods to manipulate and access that data. The class defines the data and the functionality that each object of that class can contain. A class declaration consists of a class header and body. The class header includes attributes, modifiers, and the class keyword. The class body encapsulates the members of the class, that are the data members and member functions. The syntax of a class declaration is as follows: Attributes accessibility modifiers class identifier: baselist { body } Attributes provide additional context to a class, like adjectives; for example the Serializable attribute. Accessibility is the visibility of the class. The default accessibility of a class is internal. Private is the default accessibility of class members. The following table lists the accessibility keywords; Keyword Description public Public class is visible in the current and referencing assembly. private Visible inside current class. protected Visible inside current and derived class. Internal Visible inside containing assembly. Internal protected Visible inside containing assembly and descendent of the current class. Modifiers refine the declaration of a class. The list of all modifiers defined in the table are as follows; Modifier Description sealed Class can't be inherited by a derived class. static Class contains only static members. unsafe The class that has some unsafe construct likes pointers. Abstract The instance of the class is not created if the Class is abstract. The baselist is the inherited class. By default, classes inherit from the System.Object type. A class can inherit and implement multiple interfaces but doesn't support multiple inheritances. Step-by-step Tutorial for Creating a Class 1. Open Visual Studio 2010 from start menu.
  • 42. 2. Go to "File" > "New" > "Project...", select "Console Application" in the right pane and provide the name "oops" for the project. 3. Then in the Solution Explorer, you will notice some files that are automatically created as: 4. You can also write your own code in the default program.cs file that is created but it is a good programming practice to create a new class. 5. For adding a new class, right-click over the project name (oops) in the Solution Explorer, then click "Add" > "Class". Give the name to the class "customer" as in the following;
  • 43. 6. When you open the customer.cs class. you will find some default-generated code as in the following; C# code using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace oops { class customer { } } Note: the C# console application project must require a single entry point Main () function that is already generated in the program class. For example if you add a new customer class and want to define one or more Main () entry points here then .NET will throw an error of multiple entry points. So it is advisable to delete or exclude the program.cs file from the solution. So here in this example the customer class defines fields such as CustID, Name and Address to hold information about a particular customer. It might also define some functionality that acts upon the data stored in these fields. C# code using System; namespace oops { class customer { // Member Variables public int CustID; public string Name; public string Address; //constuctor for initializing fields customer() { CustID=1101; Name="Tom";
  • 44. Address="USA"; } //method for displaying customer records (functionality) public void displayData() { Console.WriteLine("Customer="+CustID); Console.WriteLine("Name="+Name); Console.WriteLine("Address="+Address); } // Code for entry point } } At line 9, we are defining a constructor of the customer class for initializing the class member fields. The constructor is a special function that is automatically called when the customer class object is created (instantiated). And at line 11 we are printing these fields to the console by creating a user defined method displayData(). You can then instantiate an object of this class to represent one specific customer, set the field value for that instance and use its functionality, as in: C# code class customer { // class members code //Entry point static void Main(string[] args) { // object instantiation customer obj = new customer(); //Method calling obj.displayData(); //fields calling Console.WriteLine(obj.CustID); Console.WriteLine(obj.Name); Console.WriteLine(obj.Address); }
  • 45. } Here you use the keyword new to declare the customer class instance. This keyword creates the object and initializes it. When you create an object of the customer class, the .NET framework IDE provides a special feature called Intellisense that provides access to all the class member fields and functions automatically. This feature is invoke when the "." Operator is put right after the object, as in the following; Image 1.1 Intellisense feature Normally, as the program grows in size and the code becomes more complex, the Intellisense feature increases the convenience for the programmer by showing all member fields, properties and functions. Multiple Class Declaration Sometimes circumstances require multiple classes to be declared in a single namespace. So in that case it is not mandatory to add a separate class to the solution, instead you can attach the new class into the existing program.cs or another one as in the following; C# code using System; namespace oops {
  • 46. class Program { public void MainFunction() { Console.WriteLine("Main class"); } static void Main(string[] args) { //main class instance Program obj = new Program(); obj.MainFunction(); //other class instace demo dObj=new demo(); dObj.addition(); } } class demo { int x = 10; int y = 20; int z; public void addition() { z = x + y; Console.WriteLine("other class in Namespace"); Console.WriteLine(z); } } } Here in this example, we are creating an extra class "demo" in the program.cs file at line 12 and finally we are instantiating the demo class with the program class inside the Main() entry in lines 6 to 11. So it doesn't matter how many classes we are defining in a single assembly. Partial classes Typically, a class will reside entirely in a single file. However, in situations where multiple developers need access to the same class, then having the class in multiple files can be beneficial. The partial keywords allow a class to span multiple source files. When compiled, the elements of the partial types are combined
  • 47. into a single assembly. There are some rules for defining a partial class as in the following; A partial type must have the same accessibility. Each partial type is preceded with the "partial" keyword. If the partial type is sealed or abstract then the entire class will be sealed and abstract. In the following example we are adding two files, partialPart1.cs and partialPart2.cs, and declare a partial class, partialclassDemo, in both classes. partialPart1.cs using System; namespace oops { public partial class partialclassDemo { public void method1() { Console.WriteLine("method from part1 class"); } } } partialPart2.cs using System; namespace oops { public partial class partialclassDemo { public void method2() { Console.WriteLine("method from part2 class"); } } } And finally we are creating an instance of the partialclassDemo in the program.cs file as the following:
  • 48. Program.cs using System; namespace oops { class Program { static void Main(string[] args) { //partial class instance partialclassDemo obj = new partialclassDemo(); obj.method1(); obj.method2(); } } } Static classes A static class is declared using the "static" keyword. If the class is declared as static then the compiler never creates an instance of the class. All the member fields, properties and functions must be declared as static and they are accessed by the class name directly not by a class instance object. C# code using System; namespace oops { static class staticDemo { //static fields static int x = 10, y; //static method static void calcute() { y = x * x; Console.WriteLine(y); } static void Main(string[] args)
  • 49. { //function calling directly staticDemo.calcute(); } } } Creating and accessing Class Component Library .NET provides the capability of creating libraries (components) of a base application rather than an executable (".exe"). Instead the library project's final build version will be ".DLL" that can be referenced from other outside applications to expose its entire functionality. Step-by-step tutorial 1. First create a class library based application as: 2. Then we are implementing a math class library that is responsible of calculating square root and the addition of two numbers as: using System;
  • 50. namespace LibraryUtil { public class MathLib { public MathLib() { } public void calculareSum(int x, int y) { int z = x + y; Console.WriteLine(z); } public void calculareSqrt(double x) { double z = Math.Sqrt(x); Console.WriteLine(z); } } } 3. Build this code and you will notice that a DLL file was created, not an executable, in the root directory of the application (path = D:tempLibraryUtilLibraryUtilbinDebug LibraryUtil.dll). 4. Now create another console based application where you utilize all the class library's functionality. 5. Then you have to add the class library dll file reference to access the declared class in the library dll. (Right-click on the Reference then "Add reference" then select the path of the dll file.) 6. When you add the class library reference then you will notice in the Solution Explorer that a new LibraryUtil is added as in the following;
  • 51. 7. Now add the namespace of the class library file in the console application and create the instance of the class declared in the library as in the following; using System; using LibraryUtil; // add library namespace namespace oops { public class LibraryClass { static void Main() { //library class instance MathLib obj = new MathLib(); //method populate obj.calculareSum(2, 5); obj.calculareSqrt(25); } } } 8. Finally run the application. Constructor and Destructor A constructor is a specialized function that is used to initialize fields. A constructor has the same name as the class. Instance constructors are invoked with the new operator and can't be called in the same manner as other member functions. There are some important rules pertaining to constructors as in the following; Classes with no constructor have an implicit constructor called the default constructor, that is parameterless. The default constructor assigns default values to fields. A public constructor allows an object to be created in the current assembly or referencing assembly. Only the extern modifier is permitted on the constructor. A constructor returns void but does not have an explicitly declared return type. A constructor can have zero or more parameters. Classes can have multiple constructors in the form of default, parameter or both. The following example shows one constructor for a customer class.
  • 52. C# code using System; namespace oops { class customer { // Member Variables public string Name; //constuctor for initializing fields public customer(string fname, string lname) { Name= fname +" "+ lname; } //method for displaying customer records public void AppendData() { Console.WriteLine(Name); } //Entry point static void Main(string[] args) { // object instantiation customer obj = new customer("Barack", "Obama"); //Method calling obj.AppendData(); } } } Note: The moment a new statement is executed, the default constructor is called. Static Constructor A constructor can be static. You create a static constructor to initialize static fields. Static constructors are not called explicitly with the new statement. They are called when the class is first referenced. There are some limitations of the static constructor as in the following; Static constructors are parameterless. Static constructors can't be overloaded.
  • 53. There is no accessibility specified for Static constructors. In the following example the customer class has a static constructor that initializes the static field and this constructor is called when the class is referenced in the Main () at line 26 as in the following: C# code using System; namespace oops { class customer { // Member Variables static private int x; //constuctor for static initializing fields static customer() { x = 10; } //method for get static field static public void getData() { Console.WriteLine(x); } //Entry point static void Main(string[] args) { //static Method calling customer.getData(); } } } Destructors The purpose of the destructor method is to remove unused objects and resources. Destructors are not called directly in the source code but during garbage collection. Garbage collection is nondeterministic. A destructor is invoked at an undetermined moment. More precisely a programmer can't control its execution; rather it is called by the Finalize () method. Like a constructor, the destructor has the same name as the class except a destructor is prefixed with a tilde (~). There are some limitations of destructors as in the following;
  • 54. Destructors are parameterless. A Destructor can't be overloaded. Destructors are not inherited. Destructors can cause performance and efficiency implications. The following implements a destructor and dispose method. First of all we are initializing the fields via constructor, doing some calculations on that data and displaying it to the console. But at line 9 we are implementing the destructor that is calling a Dispose() method to release all the resources. using System; namespace oops { class customer { // Member Variables public int x, y; //constuctor for initializing fields customer() { Console.WriteLine("Fields inititalized"); x = 10; } //method for get field public void getData() { y = x * x; Console.WriteLine(y); } //method to release resource explicitly public void Dispose() { Console.WriteLine("Fields cleaned"); x = 0; y = 0; } //destructor ~customer() { Dispose(); } //Entry point
  • 55. static void Main(string[] args) { //instance created customer obj = new customer(); obj.getData(); } } } At line 12 when the instance is created, fields are initialized but it is not necessary that at the same time the destructor is also called. Its calling is dependent on garbage collection. If you want to see the destructor being called into action then put a breakpoint (by F9) at line 10 and compile the application. The CLR indicates its execution at the end of the program by highlighting line 10 using the yellow color. Function Overloading Function overloading allows multiple implementations of the same function in a class. Overloaded methods share the same name but have a unique signature. The number of parameters, types of parameters or both must be different. A function can't be overloaded on the basis of a different return type alone. using System; namespace oops { class funOverload { public string name; //overloaded functions public void setName(string last) { name = last; } public void setName(string first, string last) { name = first + "" + last; } public void setName(string first, string middle, string last) {
  • 56. name = first + "" + middle + "" + last; } //Entry point static void Main(string[] args) { funOverload obj = new funOverload(); obj.setName("barack"); obj.setName("barack "," obama "); obj.setName("barack ","hussian","obama"); } } } At lines 3, 4 and 5 we are defining three methods with the same name but with different parameters. In the Main (), the moment you create an instance of the class and call the functions setName() via obj at lines 7, 8 and 9 then intellisense will show three signatures automatically. Encapsulation Encapsulation is the mechanism that binds together the code and the data it manipulates, and keeps both safe from outside interference and misuse. In OOP, code and data may be combined in such a way that a self-contained box is created. When code and data are linked together in this way, an object is created and encapsulation exists. Within an object, code, data or both may be private or public to that object. Private code is known to and accessible only by another part of the object, that is private code or data may not be accessible by a piece of the program that exists outside the object. When the code and data is public, other portions of your program may access it even though it is defined within an object. C# code using System; namespace oops { class Encapsulation { /// <summary> /// Every member Variable and Function of the class are bind /// with the Encapsulation class object only and safe with
  • 57. /// the outside inference /// </summary> // Encapsulation Begin int x; //class constructor public Encapsulation(int iX) { this.x = iX; } //calculating the square public void MySquare() { int Calc = x * x; Console.WriteLine(Calc); } // End of Encapsulation //Entry point static void Main(string[] args) { //instance created customer obj = new customer(20); obj. MySquare(); } } } Inheritance Inheritance is the process by which one object can acquire the properties of another object. Inheritance is a "is a kind of" relationship and it supports the concept of classification in which an object needs only define those qualities that make it unique within the class. Inheritance involves a base class and a derived class. The derived class inherits from the base class and also can override inherited members as well as add new members to extend the base class. A base type represents the generalization, whereas a derived type represents a specification of an instance. Such as Employees that can have diverse types, such as hourly, salaried and temporary so in that case Employees is the general base class and hourly, salaried and temporary employee are specialized derived classes.
  • 58. Classes can inherit from a single class and one or more interfaces. When inheriting from a class, the derived class inherits the members including the code of the base class. The important point to remember is that Constructors and Destructors are not inherited from the base class. The syntax of inheritance is as in the following; Class derivedClass : baseClass, Iterface1, Interface2 { body } For example we are defining two classes, Father and Child. You notice at line 7, we are implementing inheritance by using a colon (:); at this moment all the properties belonging to the Father Class is accessible to the Child class automatically. C# code using System; namespace oops { //Base Class public class Father { public void FatherMethod() { Console.WriteLine("this property belong to Father"); } } //Derived class public class Child : Father
  • 59. { public void ChildMethod() { Console.WriteLine("this property belong to Child"); } } class Inheritance { //Entry point static void Main(string[] args) { Father fObj = new Father(); fObj.FatherMethod(); //Here Child object can access both class methods Child cObj = new Child(); cObj.FatherMethod(); cObj.ChildMethod(); } } } At line 11 , the Intellisense only shows the Father class functions but at line 15 to 16 the Child class object is able to access both class methods as in the following. We can create a class in the VB.Net language or another .NET supported language and can inherit them in a C# .Net class and vice versa. But a class developed in C++ or other unmanaged environment can't be inherited in .NET. Note: Cross-language and multiple inheritance is not supported by .NET. Accessibility Accessibility sets the visibility of the member to outside assemblies or derived types. The following table describes member accessibility; Modifiers Outside Assembly Derived Class private No No public Yes Yes protected No No internal Yes ( this assembly only) Yes ( this assembly only) internal protected Yes( this assembly only) Yes
  • 60. Constructor in Inheritance Constructors in a base class are not inherited in a derived class. A derived class has a base portion and derived portion. The base portion initializes the base portion, and the constructor of the derived class initializes the derived portion. The following is the syntax of a constructor in inheritance; Accessibility modifier classname(parameterlist1) : base(parameterlist2) { body } So the base keyword refers to the base class constructor, while parameterlist2 determines which overloaded base class constructor is called. In the following example, the Child class's constructor calls the single-argument constructor of the base Father class; C# code using System; namespace oops { //Base Class public class Father { //constructor public Father() { Console.WriteLine("Father class constructor"); } public void FatherMethod() { Console.WriteLine("this property belong to Father"); } } //Derived class public class Child : Father { public Child() : base() { Console.WriteLine("child class constructor"); } public void ChildMethod()
  • 61. { Console.WriteLine("this property belong to Child"); } } class Inheritance { //Entry point static void Main(string[] args) { //Here Child object can access both class methods Child cObj = new Child(); cObj.FatherMethod(); cObj.ChildMethod(); Console.ReadKey(); } } } At line 4, we are defining a base Father Class constructor and in the derived class Child, at line 8 we are initializing it explicitly via base keyword. If we pass any parameter in the base class constructor then we have to provide them in the base block of the child class constructor. Virtual Methods By declaring a base class function as virtual, you allow the function to be overridden in any derived class. The idea behind a virtual function is to redefine the implementation of the base class method in the derived class as required. If a method is virtual in the base class then we have to provide the override keyword in the derived class. Neither member fields nor static functions can be declared as virtual. C# code using System; namespace oops { class myBase { //virtual function public virtual void VirtualMethod() { Console.WriteLine("virtual method defined in the base class"); } }
  • 62. class myDerived : myBase { // redifing the implementation of base class method public override void VirtualMethod() { Console.WriteLine("virtual method defined in the Derive class"); } } class virtualClass { static void Main(string[] args) { // class instance new myDerived().VirtualMethod(); Console.ReadKey(); } } } Hiding Methods If a method with the same signature is declared in both base and derived classes, but the methods are not declared as virtual and overriden respectively, then the derived class version is said to hide the base class version. In most cases, you would want to override methods rather than hide them. Otherwise .NET automatically generates a warning. In the following example, we are defining a VirutalMethod() in the myBase class but not overriding it in the derived class, so in that case the compiler will generate a warning. The compiler will assume that you are hiding the base class method. So to overcome that problem, if you prefix the new keyword in the derived class method then the compiler will prefer the most derived version method. You can still access the base class method in the derived class by using the base keyword. C# code using System; namespace oops { class myBase { //virtual function public virtual void VirtualMethod() { Console.WriteLine("virtual method defined in the base class");
  • 63. } } class myDerived : myBase { // hiding the implementation of base class method public new void VirtualMethod() { Console.WriteLine("virtual method defined in the Derive class"); //still access the base class method base.VirtualMethod(); } } class virtualClass { static void Main(string[] args) { // class instance new myDerived().VirtualMethod(); Console.ReadKey(); } } } Abstract Classes C# allows both classes and functions to be declared abstract using the abstract keyword. You can't create an instance of an abstract class. An abstract member has a signature but no function body and they must be overridden in any non-abstract derived class. Abstract classes exist primarily for inheritance. Member functions, properties and indexers can be abstract. A class with one or more abstract members must be abstract as well. Static members can't be abstract. In this example, we are declaring an abstract class Employess with a method displayData() that does not have an implementation. Then we are implementing the displayData() body in the derived class. One point to be noted here is that we have to prefixe the abstract method with the override keyword in the derived class. C# code using System; namespace oops { //abstract class public abstract class Employess { //abstract method with no implementation
  • 64. public abstract void displayData(); } //derived class public class test : Employess { //abstract class method implementation public override void displayData() { Console.WriteLine("Abstract class method"); } } class abstractClass { static void Main(string[] args) { // class instance new test().displayData(); } } } Sealed Classes Sealed classes are the reverse of abstract classes. While abstract classes are inherited and are refined in the derived class, sealed classes cannot be inherited. You can create an instance of a sealed class. A sealed class is used to prevent further refinement through inheritance. Suppose you are a developer of a class library and some of the classes in the class library are extensible but other classes are not extensible so in that case those classes are marked as sealed. C# code using System; namespace oops { sealed class SealedClass { void myfunv(); } public class test : SealedClass //wrong. will give compilation error { } }
  • 65. Interface An interface is a set of related functions that must be implemented in a derived class. Members of an interface are implicitly public and abstract. Interfaces are similar to abstract classes. First, both types must be inherited; second, you cannot create an instance of either. Although there are several differences as in the following; An Abstract class can contain some implementations but an interface can't. An Interface can only inherit other interfaces but abstract classes can inherit from other classes and interfaces. An Abstract class can contain constructors and destructors but an interface can't. An Abstract class contains fields but interfaces don't. So the question is, which of these to choose? Select interfaces because with an interface, the derived type still can inherit from another type and interfaces are more straightforward than abstract classes. C# code using System; namespace oops { // interface public interface xyz { void methodA(); void methodB(); } // interface method implementation class test : xyz { public void methodA() { Console.WriteLine("methodA"); } public void methodB() { Console.WriteLine("methodB"); } } class interfaceDemo { static void Main(string[] args) { test obj = new test(); obj.methodA(); obj.methodB(); }
  • 66. } } An interface can be inherited from other interfaces as in the following: C# code public interface xyz { void methodA(); void methodB(); } public interface abc : xyz { void methodC(); } Polymorphism Polymorphism is the ability to treat the various objects in the same manner. It is one of the significant benefits of inheritance. We can decide the correct call at runtime based on the derived type of the base reference. This is called late binding. In the following example, instead of having a separate routine for the hrDepart, itDepart and financeDepart classes, we can write a generic algorithm that uses the base type functions. The method LeaderName() declared in the base abstract class is redefined as per our needs in 2 different classes. C# code using System; namespace oops { public abstract class Employee { public virtual void LeaderName() { } } public class hrDepart : Employee { public override void LeaderName() { Console.WriteLine("Mr. jone"); }
  • 67. } public class itDepart : Employee { public override void LeaderName() { Console.WriteLine("Mr. Tom"); } } public class financeDepart : Employee { public override void LeaderName() { Console.WriteLine("Mr. Linus"); } } class PolymorphismDemo { static void Main(string[] args) { hrDepart obj1 = new hrDepart(); itDepart obj2 = new itDepart(); financeDepart obj3 = new financeDepart(); obj1.LeaderName(); obj2.LeaderName(); obj3.LeaderName(); Console.ReadKey(); } } } Login to add your contents and source code to this article Article Extensions Contents added by Zeeshan shan on Feb 06, 2013 OVERIDING EXAMPLE using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace overriding
  • 68. { abstract class BaseClass { public abstract string YourCity(); } class DerivedClass : BaseClass { public override string YourCity() //It is mandatory to implement absract method { return "London"; } private int sum(int a, int b) { return a + b; } } class Program { static void Main(string[] args) { DerivedClass obj = new DerivedClass(); string city = obj.YourCity(); Console.WriteLine(city); Console.Read(); } } } Introduction In this article, I want to show you the concept of Object-Oriented Programming in C#. Now what are objects and why we’d better write Object-Oriented applications? Good Question! We will cover the following: What is an object? Creating a class Fields Methods Instantiating your class Access Modifiers Properties