C-Sharp Tutorial
Table of Contents
- Introduction
- Variables and Data Types
- Operators in C#
- Conditional statements in C#
- Loop Statements in C#
- Arrays in C#
- Exception Handling in C#
- Text File Handling in C#
- Classes and Methods
- Working with Parameters
- Constructors and Destructors in C#
- Delegates and Events
- Inheritance in C#
- Encapsulation & Abstraction
- Polymorphism
- Generics in C#
Introduction
C# is one of the most popular programming languages of this era. Powered by Microsoft’s .NET framework, C# is used to build windows form and WPF based desktop applications, windows phone apps and ASP.NET web applications. This guide provides a brief introduction to core C# concept. This is a bird’s eye view of the most important C# concepts. This guide is for reference purposes and contains a summary of the most important C# concepts.
A Simple C# Program
Let’s write a very basic C# program.
using System;
namespace SampleApplication
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Welcome to C#");
Console.ReadKey();
}
}
}
In C#, everything is written inside a class which is inside a namespace. A namespace is a collection of classes with similar functionality. A class contains methods and member variables. In the above code, Program class contains a Main method. This is starting point of a C# program. Whenever a C# program executes, it starts from a static Main method.
Variables and Data Types
Variables are used to store data in C#. The type of variable depends upon the type of data stored in the variable.
Declaring a variable is simple; just write the data type of the data to be stored in the variable followed by variable name. Variable name should start with upper or lower case letter or number. Following are some of the most commonly used data types in C#.
Type | Represents | Range | Default Value |
---|---|---|---|
bool |
Boolean value | True or False | False |
byte |
8-bit unsigned integer | 0 to 255 | 0 |
char |
16-bit Unicode character | U +0000 to U +ffff | '\0' |
decimal |
128-bit precise decimal values with 28-29 significant digits | (-7.9 x 1028 to 7.9 x 1028) / 100 to 28 | 0.0M |
double |
64-bit double-precision floating point type | (+/-)5.0 x 10-324 to (+/-)1.7 x 10308 | 0.0D |
float |
32-bit single-precision floating point type | e -3.4 x 1038 to + 3.4 x 1038 | 0.0F |
int |
32-bit signed integer type | -2,147,483,648 to 2,147,483,647 | 0 |
long |
64-bit signed integer type | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | 0L |
sbyte |
8-bit signed integer type | -128 to 127 | 0 |
short |
16-bit signed integer type | -32,768 to 32,767 | 0 |
uint |
32-bit unsigned integer type | 0 to 4,294,967,295 | 0 |
ulong |
64-bit unsigned integer type | 0 to 18,446,744,073,709,551,615 | 0 |
ushort |
16-bit unsigned integer type | 0 to 65,535 | 0 |
Operators in C#
There are four major types of operators in C#.
- Logical Operators
- Arithmetic Operators
- Relational Operators
- Assignment Operators
1. Logical Operators
Given the Boolean variables A containing the value true
and B containing the value false
:
Operator | What They Do | Example |
---|---|---|
&& |
Called Logical AND operator. Returns true only if all of the operands are true | (A && B) is false . |
|| |
Called Logical OR Operator. Returns true only if at least one of the operands is true. | (A || B) is true . |
! |
Called Logical NOT Operator. Reverses the logical state of the operand. | !(A && B) is true . |
2. Arithmetic Operators
Given the numeric variables A containing the value 10 and B containing the value 20:
Operator | What They Do | Example |
---|---|---|
+ |
Performs addition | A + B is 30 |
- |
Performs subtraction | A - B is -10 |
* |
Performs multiplication | A * B is 200 |
/ |
Performs division | B / A is 2 |
% |
Takes modulus | B % A is 0 |
++ |
Increment operand by one | A++ is 11 |
-- |
Decrement operand by one. | A-- is 9 |
3. Relational Operators
Operator | What They Do | Example |
---|---|---|
== |
Checks both operands for equality | (A == B) is not true. |
!= |
Checks both operands for inequality | (A != B) is true. |
> |
Checks if the left operand is greater than right operand | (A > B) is not true. |
< |
Checks if the left operand is less than right operand | (A < B) is true. |
>= |
Checks if the left operand is greater than or equal to right operand | (A >= B) is not true. |
<= |
Checks if the left operand is less than or equal to right operand | (A <= B) is true. |
4. Assignment Operators
Operator | Description |
---|---|
= |
num1 = num2 + num3 assigns value of num2 + num3 into num1 |
+= |
num1 += num2 is equivalent to num1 = num1 + num2 |
-= |
num1 -= num2 is equivalent to num1 = num1 - num2 |
*= |
num1 *= num2 is equivalent to num1 = num1 * num2 |
/= |
num1 /= num2 is equivalent to num1 = num1 / num2 |
%= |
num1 %= num2 is equivalent to num1 = num1 % num2 |
There are also similar assignment operators that correspond with bitwise operators.
Conditional Statements in C#
Conditional statements in C# are used to control the flow of the execution of code. Based upon a particular condition, some specific piece of code is either executed or left unexecuted.
There are two types of conditional statements in C#: if
/else
statements and switch
statements.
1. If/else Statement
The if/else statement is very simple. A certain condition is defined using the if
keyword. If the statement evaluates to true
, the code block followed by the if
statement is executed otherwise, the code block is left unexecuted. For instance take a look at the following example.
if (10 > 20)
{
Console.WriteLine("Number 10 is greater than 20");
}
If you try to run the above code, you will not see anything on the console, since the condition 10 > 20
evaluates to false
. You can use else
statement to execute the code, in case if the if
statement evaluates to false
. Check the following example.
if (10 > 20)
{
Console.WriteLine("Number 10 is greater than 20");
}
else
{
Console.WriteLine("Number 10 is not greater than 20");
}
In the above case, since the if
condition evaluates to false
, the code block followed by else
statement will execute. The output will be as follows:
You can chain multiple “if/else” and “if/else if” statements to check as many conditions as you want. Have a look at the following example.
if (10 > 10)
{
Console.WriteLine("Number 10 is greater than 10");
}
else if (10 < 10)
{
Console.WriteLine("Number 10 is less than 10");
}
else
{
Console.WriteLine("Number 10 is equal to 10");
}
The output of above code is:
Number 10 is equal to 102. Switch Statement
If there are too many conditions to evaluate, it is better to use switch statements instead of “if/ else” statements. Take a look at a simple example of switch statements.
string color = "blue";
switch (color)
{
case "red":
Console.WriteLine("China’s Flag has red color");
break;
case "blue":
Console.WriteLine("USA’s flag has blue color in it");
break;
case "Green":
Console.WriteLine("Italy’s flag has green color");
break;
default:
Console.WriteLine("Sorry we can’t fetch country flag with this color");
break;
}
Switch statement starts with switch
keyword followed by opening and closing parentheses. The value to compare is passed inside these parentheses. The value is compared with each case condition. When the value passed matches with the value in the case statement, the statement(s) followed by that case statement executes. The remaining case statements are not checked. If none of the case statements match, the code followed by the default
statement executes. The out of the above code is:
Loop Statements in C#
Loop statements in C# are used to execute piece of code repeatedly for a specific number of times or until a condition is met. Loop statements are also known as iteration statements. There are three types of loop constructs in C#.
- For loop
- While loop
- Do While loop
1. For Loop
For loop executes a piece of code repeatedly for specific number of times. Take a look at the following example.
for (int i = 1; i <= 10; i++)
{
Console.WriteLine(7 + " x " + i + " = " + 7 * i);
}
The above code prints the table of 7 on the console screen.
7 x 1 = 77 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70
For loop consist of three parts: Initialization statement, test expression and increment statement. In the above code i=1
is initialization statement, i <= 10
is test expression and i++
is increment statement. Before the code in the loop executes, the test expression is evaluated, if it is true the code block of the loop is executed. After that the increment condition executes. Then again test expression is evaluated on the updated variable. This code keeps executing repeatedly until the test expression becomes false.
2. While Loop
The “while loop” is used to execute a piece of code repeatedly until some condition is satisfied.
int i =1;
while (i <= 10)
{
Console.WriteLine(7 + " x " + i + " = " + 7 * i);
i++;
}
In “while loop”, the initialization statement is specified before the body of the loop. The test expression is specified inside the opening and closing body while the increment statement is contained by the body of the loop. The above piece of code also prints the table of 7 on console.
3. Do While Loop
Do while loop is similar to while loop with the exception that the test expression in do while loop is specified after the closing of loop body. This makes do while loop execute at least once. Take a look at the following example:
int i =1;
do
{
Console.WriteLine(7 + " x " + i + " = " + 7 * i);
i++;
} while (i <= 10);
There is another type of loop called “for each” loop that iterates through collection of items. You will see “foreach” loop in action in next section.
Arrays in C#
Arrays are used to store collection of items of same data type. If you have a collection of integers, it is not wise to declare 100, or 200 variables to store such data. Rather you can declare an array of the size of collection and store your items in that array. Arrays store data at contiguous memory locations, thereby providing fast data access.
Declaring an Array
Declaring and defining an array is simple. Start with the type of array, followed by opening and closing square brackets and the name of the array. On the right side of the equals sign, use new keyword, followed by the type of array and opening and closing square brackets that contain size of an array. For instance, in the following code, we created a string type array “days” of size seven.
string [] days = new string [7];
Array has zero based index which means first element gets stored at index zero and last element get stored at k-1 index where k is the size of the array
Storing and Retrieving Array Items
It is very easy to store and retrieve items from array. To store items write the name of the array followed by index number in square brackets and equate it to the item that you want to store in that index. Similarly, to retrieve or get an item from array, write the name of array and index number. Take a look at the following example.
string [] days = new string [7];
days[0] = "Monday"; // Storing first item
days[1] = "Tuesday";
days[2] = "Wednesday";
days[3] = "Thursday";
days[4] = "Friday";
days[5] = "Saturday";
days[6] = "Sunday";
Console.WriteLine(days[4]); // Retrieving fifth item
Using For Loop to Iterate an Array
You can easily iterate through an array using for loop and for each loop. Take a look at the following example.
string [] days = new string [7];
days[0] = "Monday";
days[1] = "Tuesday";
days[2] = "Wednesday";
days[3] = "Thursday";
days[4] = "Friday";
days[5] = "Saturday";
days[6] = "Sunday";
for (int i = 0; i < 7; i++) // Iterating via for loop
{
Console.WriteLine(days[i]);
}
foreach (String day in days) // Iterating via foreach loop
{
Console.WriteLine(day);
}
Exception Handling in C#
Exceptions are breaks that occur during the program execution, most commonly because if errors that are not handled at compiled time. There are two types of errors: compile-time errors and run-time errors, run-time errors will generate exceptions if not handled. Compile-time errors are caught at compile time. For instance if you miss a semicolon, the compiler tells you at compile time that there is an error in the program and it can’t be compiled. On the other hand, run-time errors are not caught at compile time. For instance, if you divide a number by zero, your compile will compile the code. However, when you run the code, the program will through exception unless you write special code to handle that case.
There is a mechanism to catch these runtime errors. To do so try
and catch
blocks are used.
Wrap the piece of code that is likely to throw an exception inside a catch
block containing the code you want to execute when exception occurs or use another
code block known as finally
. This code block executes whether or not the exception occurs, you
can have try-catch, try-finally or try-catch-finally blocks.
One common use of the try-catch-finally statement is to manage resource allocation, such as closing any open connections and destroing objects even if a run-time error occurred after opening connections or allocating objects.
Take a look at the simple example of exception handling in C#.
int a = 10;
int b = 0;
try
{
int result = a / b;
}
catch (Exception e)
{
Console.WriteLine("Exception " + e.Message);
}
finally
{
Console.WriteLine("Caution: Division by zero is not allowed");
}
In the above code we are dividing a number by 0 inside the try block. In the output the messages inside the catch block and finally block will be printed. Even if the try block doesn’t throw an exception, the message inside the finally block will still be printed. The output looks like this:
Exception Attempted to divide by zero. Caution: Division by zero is not allowed.Text File Handling in C#
File handling refers to the processes of creating, reading and writing data from and to a text file.
Reading Data From a Text File.
To read data from a text file, we use StreamReader
class. The path of the file to read is passed
in the constructor of the class. Next, inside the while loop, the ReadLine
method of the
StreamReader
is repeatedly called. This method returns the read text as long as there is a line of
text that is not yet read. Once the entire file has been read, this method returns null and the loop
execution discontinues. Take a look at the following example.
try
{
using (StreamReader reader = new StreamReader("d:/countries.txt"))
{
string linestoread;
while ((linestoread = reader.ReadLine()) != null)
{
Console.WriteLine(linestoread);
}
}
}
catch (Exception e)
{
Console.WriteLine("There is an error readin the file.");
Console.WriteLine(e.Message);
}
Console.ReadKey();
It is always a good practice to wrap file reading and writing code inside try
and catch
block since
file is an external resource and interacting with it may throw errors.
Writing Data to a text File
To write data to a text file, StreamWriter
class is used. The StreamWriter’s WriteLine
method is
used to write a new line of data into the file. Take a look at the following example.
string[] colors = new string[] { "Red", "Green", "Blue", "Yellow", "Orange" };
using (StreamWriter writer = new StreamWriter("colors.txt"))
{
foreach (string color in colors)
{
writer.WriteLine(color);
}
}
string linetoread = "";
using (StreamReader reader = new StreamReader("colors.txt"))
{
while ((linetoread = reader.ReadLine()) != null)
{
Console.WriteLine(linetoread);
}
}
Console.ReadKey();
In the above code a file named “colors.txt” is being created and the items in the “colors” array are
being written into this file via WriteLine
method of the StreamWriter
class. Next, to see whether
the file has actually been created and data has been written to it, we read the file using the
StreamReader
class
Classes and Methods
C# is oriented pure object-oriented programming language, that is all code is implemented in terms of classes and objects. In programming, anything that has some properties and can perform some functions can be considered as an object. A class is simply a blue print for an object; it has no physical existence on system’s memory. An object, on the other hand is physical implementation of a class.
Namespaces
In C#, a namespace is like a container of classes. Every class must belong to a namespace. This allows to have multiple classes using the same name as long as they belong to different namespaces.
Creating a Class
It is very simple to create a class in C#. Start with an access modifier which can be either public or
internal for classes, an internal class can only be accessed within classes in the same namespace
while public classes can be accessed anywhere. The access modifier is then followed by keyword
class
and the class name. The body of a class starts and ends with opening and closing curly
braces respectively. Let’s create a class Phone
, by default a class is internal.
namespace Examples
{
class Phone
{
public string Memory = "4mb";
public string Camera = "13mp";
public int Price = 20000;
public void StartPhone ()
{
Console.WriteLine("Phone has started");
}
public void PickCall()
{
Console.WriteLine("Call has been picked");
}
public void TerminateCall()
{
Console.WriteLine("Call has been terminated");
}
}
}
The above class has three member variables: memory, camera and price. The class also contains three
methods: StartPhone
, PickCall
and TerminateCall
. This is close to real world. A cell phone has a camera,
memory and some price. Also, cell phone has functionalities like starting the phone, picking a call
and terminating a call. Here we have encapsulated those real world properties and methods in a class.
Methods
A method is used to implement functionality and is also known as a function. It is very easy to
create methods in C#. Start with an access modifier: public
, private
or protected
, followed by return
type, the name of the method then opening and closing parentheses and optionally parameters in
between them. For instance in the phone class we have a public method StartPhone
which returns
nothing (void
). Parameters are defined in a comma-separated list of parameter types followed by
names. Like class, method body is also enclosed in opening and closing curly braces.
Method Overloading
Method overloading refers to defining multiple methods with same name but different set of parameters and optionally different return type. The method that will be executed depends upon the call to the method. The method whose type, order and number of parameters match with the parameters in the call is executed. Take a look at the following example.
class Messages
{
public void DisplayMessage(int a, int b, int c)
{
Console.WriteLine("This is overloaded method with three parameters.");
}
public void DisplayMessage(int a, int b)
{
Console.WriteLine("This is overloaded method with two parameters.");
}
public void DisplayMessage(int a)
{
Console.WriteLine("This is overloaded method with one parameter.");
}
}
In the above code, we created a class Messages
. It contains three overloaded methods
DisplayMessages
with three different sets of parameters.
The method called will depend upon the number and order of parameters in the call. Take a look at the following code:
Messages m = new Messages();
m.DisplayMessage(10, 20, 30);
m.DisplayMessage(10, 20);
m.DisplayMessage(10);
The output of the above code will be:
This is overloaded method with three parameters.This is overloaded method with two parameters.
This is overloaded method with one parameter.
Creating an Object
Phone p = new Phone();
Console.WriteLine("The price of phone is " + p.Price);
p.PickCall();
It is easy to create an object of a class. Simply define a variable of the class type by typing the
class name then the variable name, then the new
keyword followed by the class name and open
and closed parentheses. In the above example we created an object of Phone
class and stored it
in Phone
type varable p.
To access class members and methods of a variable, type the variable name followed by a dot .
and the name of the member or method. For instance, to call PickCall
method via p
object of
class Phone
, type p.PickCall()
. Calling methods requires open and closed parentheses even if it
doesn’t have parameters.
Working with Parameters
As mentioned earlier, you can also have parameterized methods or methods with parameters. The parameters are defined inside the opening and closing parentheses, each separated by a comma.
Creating a Parameterized Method
Let’s modify our phone class and create a parameterized method SetTime
, the method will take
three parameters minutes
, second
and meridiem
. Take a look at the following code:
class Phone
{
public int Hour = 00;
public int Minute= 00;
public string Meridiem = "am";
public void SetTime (int h, int m, string mr)
{
Hour = h;
Minute = m;
Meridiem = mr;
}
public void ShowTime()
{
Console.WriteLine("Current Time is " + Hour + ":" + Minute + ":" + Meridiem);
}
}
In the above Phone
class, there are three datamember Hour
, Minute
and Meridiem
and two
methods ShowTime
and SetTime
. The ShowTime
function displays values of hour, minute and
meridiem variables. The SetTime
function accepts three parameters h
, m
and mr
. The value
passed to these parameters are stored in Hour
, Minute
and Meridiem
datamembersof the class.
Now, whenever, you call the SetTime
method, you will have to pass values for these parameters in
the same order. Otherwise, the compiler will generate an error. Take a look at the following code
to see how parameterized methods are called.
Phone p = new Phone();
p.ShowTime();
p.SetTime(4, 50, "pm");
p.ShowTime();
In the above example we first call the ShowTime
method that will display the default values of
hour, minute and meridiem member variables. Next, we called SetTime
method and pass values
for the parameters. This will update values for Hour
, Minute
and Meridiem
datamembers. Finally
we again display the updated time via ShowTime
method. The output will look like this:
Current Time is 4:50:pm
Constructors and Destructors in C#
Constructors
A constructor is a special method that is called whenever you create object of a class. A constructor has no return type and its name is the same as the name of the class. Constructors are usually used to initialize datamembers at the time of creating an object.
Destructors
Destructors are also special methods with the name similar to the name of the class in which they
exist. However, destructor name starts with a ~
tilde sign. Destructors are called when the object
of a class is destroyed or removed from the memory. Usually .NET framework performs automatic
garbage collection, however destructors can be used to free any resources occupied by the object.
A class can have one constructor at most
Take a look at the following example of a constructor and a destructor
class Phone
{
public int Price = 0;
public string Color = "Black";
public Phone(int p, string c) // Constructor
{
Price = p;
Color = c;
Console.WriteLine("Constructor Executed");
}
~Phone()
{
Console.WriteLine("Destructor Executed");
}
}
In the above code, the phone class has a constructor that takes two parameters and initializes
Price
and Color
datamembers of the class. The above class also contains a destructor which
simply prints message on console screen. The following code creates object of Phone
class.
Phone p = new Phone(1000, "blue");
The output of the above code will be:
Constructor ExecutedDestructor Executed
Delegates and Events
Delegates
Delegates store references to methods. Delegates are reference type variables and are typically used to implement events and callback functions.
Working with delegaes is simple. The first step is to declare a delegate. The next step is to
instantiate the delegate using new
keyword and pass the function to refer as parameter to the
delegate constructor. Finally call the function using delegate variable. Let us see a simple example
of delegates.
delegate int SquareNum(int num);
class Math
{
public int TakeSquare(int number)
{
return number * number;
}
}
In the above code a delegate named SquareNum
has been defined. To define a delegate, simply
type keyword delegate
followed by return type, name of the delegate and parameters. This delegate
can refer to any function that has same signature i.e. return type and number, types and order of
parameters. We create Math
class that contains a function TakeSquare
. It has return type of integer and
can accept one integer type parameter. This function matches the SquareNum
delegate’s signature.
Now, let’s have a look at how we can instantiate a variable of the delegate type and call the method through it:
Math m = new Math();
SquareNum sn = new SquareNum(m.TakeSquare);
Console.WriteLine("The square root of 4 is: " + sn(4));
Console.ReadKey();
It can be seen that the method TakeSquare
is being passed as parameter to the delegate
constructor. Now when the delegate function is called, the TakeSquare
function executes and
squares the number passed to it as parameter. The output of above code shall look like this:
Events
Events are actions performed by the user such as a key press, a mouse click or double click. Events can also be raised when a network error occurs, or a file download is completed. In C#, events raised in one class can be handled by event handlers in the same class or some other class. The class that raises an event is called publisher class while the class that handles that event is called subscriber. A class can be publisher and subscriber simultaneously. An event is bound to its handler via delegate. Take a look at the following simple example.
delegate void ShowMessage(String str);
class EventGenerator
{
event ShowMessage Showmessageevent;
public void GenerateEvent()
{
EventSubscriber es = new EventSubscriber();
ShowMessage sm = new ShowMessage(es.PrintWelcome);
Showmessageevent += sm;
Showmessageevent("Jones");
}
}
class EventSubscriber
{
public void PrintWelcome(string user)
{
Console.WriteLine("Welcome to C# " + user);
}
}
In the above code we define a delegate, a class that fires an event and a class that handles the
event. The event is bound to its handler via the delegate. The syntax to bind event to its handler is
by using +=
symbols. Let’s fire the event in main function and see the output.
EventGenerator eg = new EventGenerator();
eg.GenerateEvent();
Console.ReadKey();
The output of the above code will look like this:
Welcome to C# JonesInheritance in C#
Inheritance in C# is quite similar to inheritance in real world. Just like a child inherits property of its parents, in C# a class that inherits another class inherits its properties. A class that is inherited by another class is called base class or parent class while class that inherits another class is called child or derived class. In C#, a class can only inherit from one class where as one class can be inherited by multiple classes. Let’s have a look at a simple example of inheritance.
class Phone
{
public int Price;
public string Color;
public void PickCall()
{
Console.WriteLine("Call has been picked");
}
}
class Mobile : Phone
{
public void GetInfo()
{
Console.WriteLine("This is mobile phone. Samsung Brand");
}
}
class Program
{
static void Main(string[] args)
{
Mobile m = new Mobile();
m.Price = 10000;
m.Color = "Black";
m.PickCall();
m.GetInfo();
Console.ReadKey();
}
}
In the above code, there are three classes Phone
, Mobile
and Program
. Phone
class has two member
variables and a function. Mobile
class inherits phone, class. To inherit from a class, simply
type colon followed by the name of the class to inherit. Now, since Mobile
class inherits the Phone
class, it will also inherit the datamembers and methods of Phone
class. To see if Mobile
class has
actually inherited the datamembers and methods of Phone
class we tried to access the datamembers
and methods of Phone
class using Mobile
class object. The output of the above code shall
look like this.
This is mobile phone. Samsung Brand
Encapsulation & Abstraction
Polymorphism, inheritance and encapsulation are considered the three pillars of any object oriented programming language. In this section we shall see Encapsulation and a related concept: Abstraction.
Encapsulation
Encapsulation refers to hiding the internal details of a class by making datamembers private or protected. These datamembers can then be accessed via getters and setters. Encapsulation can be best explained with the help of a simple example.
class Phone
{
int _price = 100;
string _color = "Black";
public int Price
{
get
{
return this._price;
}
set
{
if (value < 100)
this._price = 100;
else
this._price = value;
}
}
public string Color
{
get
{
return this._color;
}
set
{
if (value != "Black" && value != "White" && value != "Blue" )
this._color = "Black";
else
this._color = value;
}
}
}
In the above code Phone
class has two datamembers: _price
and _color
. Corresponding to these
members we create two properties Price
and Color
respectively. Properties have getters and
setters to get values from member variables and store values inside a datamember. The get
and
set
methods are used for this purpose.
Notice that inside the setter for price, we have specified a condition that if the price is less than
100, set the price value to 100, otherwise set the price value to the actual value being passed.
Similarly for Color
property, we have specified that if the value being set is “Black” or “White” or
“Blue” set that value otherwise set color as “Black”.
This is encapsulation, the internal functioning of the datamembers is not known to the user. The
user can only set and get the value for price and color variables. The user cannot directly access
color and price variable. Rather he has to make use of Color
and Price
properties. And these
properties are adding an extra layer of protection to the actual data which is hidden from user.
The following code sets and gets values for Price
and Color
properties.
Phone p = new Phone();
p.Price = 10;
p.Color = "White";
Console.WriteLine(p.Color + " " + p.Price);
Console.ReadKey();
Abstraction
Abstraction refers to an abstract entity which has no physical existence. It is only an idea or thought which does not exist physically. In C#, abstraction is implemented via abstract classes and interfaces. An abstract class is a class that doesn’t and cannot have any physical existence in the form of object. Abstract class is used to provide a blue print for classes that derive it. An abstract can contain abstract as well as non-abstract (concrete) methods. Abstract methods do not have any implementation. A class that implemented or inherits an abstract class provides the implementation for the abstract methods of base abstract class or chooses to stay abstract and let classes inherited from it provide the implementation.
Consider example of a vehicle, a vehicle starts and stops. It has a price, color, weight. However
vehicle itself is nothing. A car can be a vehicle; a motor bike can be a vehicle. But vehicle itself
is nothing. Vehicle exists in the form of car, truck, or cycle etc. Therefore, vehicle is an abstract
entity while car, truck and motor bikes are concrete entities. An abstract class starts with access
modifier, followed by keywords abstract class
and the name of the class. The abstract methods
also contain keyword abstract
.
abstract class Vehicle
{
public int Price;
public string Color;
public abstract void GetInfo();
}
class Car : Vehicle
{
public override void GetInfo()
{
Console.WriteLine("This is a car");
}
}
class Bike : Vehicle
{
public override void GetInfo()
{
Console.WriteLine("This is a Motor Bike");
}
}
In the above code we create an abstract class Vehicle
. This class contains an abstract method
GetInfo
. The Vehicle
class is inherited by two derived classes Car
and Bike
. These classes have to
provide the implementation for the GetInfo
method or be declared as a abstract class, otherwise
the compiler will generate an error. To provide implementation of base class’s abstract methods,
keyword override
is used.
Therefore, Car
and Bike
class override the GetInfo
method and provide their own implementation.
Now, if you try to create object of the Vehicle
class, you will not be able to do so. This is because an
abstract class cannot be instantiated. You can only create objects of child classes. The following
code creates objects of Car
and Bike
class and then calls GetInfo
method on both of them.
Car c = new Car();
Bike b = new Bike();
c.GetInfo();
b.GetInfo();
This is a carThis is a Motor Bike
Polymorphism
Polymorphism refers to multiple forms. In terms of programming, it is defined as single interface providing multiple functionalities. In C# and other object-oriented languages polymorphism is implemented via method overriding.
Method Overriding
Method overriding refers to re-implementing parent class method in child class by providing
different definition in child class. In the Abstraction section we saw how Car
and Bike
classes
provided implementations for parent class’s GetInfo
method.
However, it is not necessary for a parent class and its method to be abstract in order to be
overridden. You can also create virtual
method in the parent class. This method has its own
implementation and it can be also be overridden by child classes. In child class we can have same
method with different implementation. Let’s modify our abstraction example by making Vehicle
class concrete.
class Vehicle
{
public virtual void GetInfo()
{
Console.WriteLine("This is a vehicle");
}
}
class Car : Vehicle
{
public override void GetInfo()
{
Console.WriteLine("This is a car");
}
}
class Bike : Vehicle
{
public override void GetInfo()
{
Console.WriteLine("This is a Motor Bike");
}
}
In the above code, Vehicle
, Car
and Bike
classes have their own implementation of GetInfo
method. The GetInfo
method in parent class is virtual which means that this method can be
overridden by the child classes. Also We can store the object of child class in parent class
variables. Therefore, we shall create three Vehicle
type variables and will store objects of Vehicle
,
Car
and Bike
classes in these variables. We will then call the GetInfo
method on each of these objects.
You will see that though we will call GetInfo
method from same Vehicle type variables,
the output will be different depending upon the object stored in the variable. This is dynamic
polymorphism. Take a look at the following code:
Vehicle v1 = new Vehicle();
Vehicle v2 = new Car();
Vehicle v3 = new Bike();
v1.GetInfo();
v2.GetInfo();
v3.GetInfo();
In the output, you will see that the GetInfo
method of objects stored in Vehicle
variables will be
called.
This is a car
This is a Motor Bike
Generics in C#
Generics allow you to create classes and variables without specifying their types. The type can be specified at runtime. Generic types contain type parameter which is specified by a type parameter. Take a look at a simple example to see generics in action.
class GenericTest<T>
{
T GenericValue;
public GenericTest(T value)
{
this.GenericValue = value;
}
public void DisplayVal()
{
Console.WriteLine(this.GenericValue);
}
}
In the above code we create a generic class GenericTest
with a type parameter T
. The
constructor of the class accepts value of type T
and assigns this value to GenericValue
datamember.
Now, we will create two objects of this class, one using integer type and the other using string type. We shall also pass integer and string type values for the constructor of the class.
GenericTest<int> gen = new GenericTest<int>(10);
gen.DisplayVal();
GenericTest<String> gen2 = new GenericTest<String>("Welcome to C#");
gen2.DisplayVal();
The DisplayVal
method will display the value of GenericValue
variable on the console. The
output will look like this:
Welcome to C#