If you already know the C++ lanaguage, it wouldn't take you more than an
hour to quickly go through the syntax of C#. C# is a language with the features
of C++, programming style like Java and rapid application model of Basic. So if
you are a little bit familiar with Jave program structure and the concept
of packages, garbage collection of Java, it will definitely help you learn C#
more quickly. So while discussing C# language constructs, i
will presume, you know C++ language. This article dicusses the C# language
constructs and features using code examples in a brief and quick way so just by
having a glance at the code, you can understand the concepts.
Following topics of C# langauge:
-
Program Structure
-
Namespaces
-
Data Types
-
Variables
-
Operators and Expressions
-
Enumerations
-
Statements
-
Classes and Structs
-
Modifiers
-
Properties
-
Interfaces
-
Function Parameters
-
Arrays
-
Indexers
-
Boxing and Unboxing
-
Delegates
-
Inheritance and Polymorphism
Followings are not discussed:
-
Things which are common in C++ and C#.
-
Concepts like garbage collection, threading, file processing etc.
-
Data type conversions
-
Exception Handling
-
.Net library
Note: This article is not for C# gurus. There must be some other beginner's
articles on C#, but this is yet another one.
Program Structure
Like C++, C# is case-sensitive. Semi colon ; is
the statement separater. Unlike C++, there are no separate declaration (header)
and implementation(cpp) files in C#. All code (class declaration and
implementation) is placed in one file with extention cs.
Have a look at this Hello world program in C#.
using System;
namespace MyNameSpace
{
class HelloWorld
{
static void Main(string[] args)
{
Console.WriteLine ("Hello World");
}
}
}
Everything in C# is packed into a class and classes in C# are packed into
namespaces (just like files in a folder). Like C++, a main method is the
entry point of your program. C++'s main function is called "main" whereas C#'s
main function starts with capital M and is named as "Main".
No need to put a semi colon after a class block or struct definition. It was in
C++, C# doesn't require that.
Namespace
Every class is packaged into a namespace.Namespaces are exactly the same concept
as in C++, but in C# we use namespaces more frequently than in C++. You can
access a class in a namespace using dot . qualifier. MyNameSpace is the
namespace in hello world program above.
Now consider you want to access the HelloWorld class from some other class in
some other namespace.
using System;
namespace AnotherNameSpace
{
class AnotherClass
{
public void Func()
{
Console.WriteLine ("Hello World");
}
}
}
Now from your HelloWorld Class you can access it as:
using System;
using AnotherNameSpace; // you will add this using statement
namespace MyNameSpace
{
class HelloWorld
{
static void Main(string[] args)
{
AnotherClass obj = new AnotherClass();
obj.Func();
}
}
}
In .NET library, System is the top level namespace in which other namespaces
exist. By dafault there exists a global namespace, so a class defined outside a
namespaces goes directly into this global namespace and hence you can access
this class without any qualifier.
you can also define nested namespaces.
Using
The #include directive is replaced with "using" keyword, which is followed
by a namespace name. Just as "using System" as above. "System" is the base
level namespace in which all other namespaces and classes are packed. The base
class for all object is Object in the System
namespace.
Variables
Variables in C# are almost the same as in C++ except these differences:
-
Variables in C# (unlike C++), always need to be initialized before you
access them, otherwise you will get compile time error. Hence, it's impossible
to access an uninitialized variable.
-
You can't access a “dangling” pointer in C#.
-
An expression that indexes an array beyond its bounds is also not accessible.
-
There are no global variables or functions in C# and the
behavior of globals is acheived through static functions and static variables.
Data Types
All types of C# are derived from a base class object. There are two
types of data types:
-
Basic/ Built-in types
-
User-defined types
Following is a table which lists built-in C# types:
|
Type
|
Bytes
|
Desc
|
| byte |
1 |
unsigned byte |
| sbyte |
1 |
signed byte |
| short |
2 |
signed short |
| ushort |
2 |
unsigned short |
| int |
4 |
signed integer |
| uint |
4 |
unsigned integer |
| long |
8 |
signed long |
| ulong |
8 |
unsigned long |
| float |
4 |
floating point number |
| double |
8 |
double precision number |
| decimal |
8 |
fixed precision number |
| string |
|
unicode string |
| char |
|
unicode char |
| bool |
true, false |
boolean |
Note: type range in C# and C++ are different, example, long in C++ is 4 bytes,
and in C# it is 8 bytes. Also the bool type and string types are different than
those in C++. bool accepts only true and false and not any integer.
User defined types includes:
-
Classes
-
Structs
-
Interfaces
Memory allocation of the data types divides them into two types:
-
Value Types
-
Reference Types
Value Types
Values types are those data types which are allocated in stack. They include:
-
All basic or built-in types except strings
-
Structs
-
Enum types
Reference Type
Reference types are allocated on heap and are garbage collected when they are no
longer being used. They are created using new operator, and there is
no delete operator for these types unlike C++ where user has to
explicitely delete the types created using delete operator. In C#,
they are automatically collected by garbage collector.
Reference types include:
-
Classes
-
Interfaces
-
Collection types like Arrays
-
String
Enumeration
Enumerations in C# are exactly like C++. Defined through a keyword enum.
Example:
enum Weekdays
{
Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday
}
Classes and Structs
Classes and Structs are same as in C++, except the difference of their memory
allocation. Objects of classes are allocated in heap, and are created using new,
where as structs are allocated in stack. Structs in C# are very light and
fast datatypes. For heavy datatypes, you should create classes.
Examples:
struct Date
{
int day;
int month;
int year;
}
class Date
{
int day;
int month;
int year;
string weekday;
string monthName;
public int GetDay()
{
return day;
}
public int GetMonth()
{
return month;
}
public int GetYear()
{
return year;
}
public void SetDay(int Day)
{
day = Day ;
}
public void SetMonth(int Month)
{
month = Month;
}
public void SetYear(int Year)
{
year = Year;
}
public bool IsLeapYear()
{
return (year/4 == 0);
}
public void SetDate (int day, int month, int year)
{
}
...
}
Properties
If you familiar with object oriented way of C++, you must have an idea of
properties. Properties in above example of Date class are day, month and year
for which in C++, you write Get and Set methods. C# provides a more convinient,
simple and staright forward way of accessing properties.
So above class can be written as:
using System;
class Date
{
public int Day{
get {
return day;
}
set {
day = value;
}
}
int day;
public int Month{
get {
return month;
}
set {
month = value;
}
}
int month;
public int Year{
get {
return year;
}
set {
year = value;
}
}
int year;
public bool IsLeapYear(int year)
{
return year%4== 0 ? true: false;
}
public void SetDate (int day, int month, int year)
{
this.day = day;
this.month = month;
this.year = year;
}
}
Here is the way you will get and set these properties:
class User
{
public static void Main()
{
Date date = new Date();
date.Day = 27;
date.Month = 6;
date.Year = 2003;
Console.WriteLine("Date: {0}/{1}/{2}", date.Day, date.Month, date.Year);
}
}
Modifiers
You must be aware of public, private and protected modifers that are commonly
used in C++. I will here discuss some new modifiers introduced by C#.
readonly
readonly modifier is used only for the class data members. As the name indicates
the readonly data members can only be read once they are written either
directly initializing them or assigning values to them in constructor.
The ifference between the readonly and const data members is that const
requires you to initialize with the declaration, that is directly. See example
code:
class MyClass
{
const int constInt = 100; //directly
readonly int myInt = 5; //directly
readonly int myInt2;
public MyClass()
{
myInt2 = 8; //Indirectly
}
public Func()
{
myInt = 7; //Illegal
Console.WriteLine(myInt2.ToString());
}
}
sealed
sealed modifier with a class don't let you derive any class from it. So you use
this sealed keyword for the classes which you don't want to be inherited from.
sealed class CanNotbeTheParent
{
int a = 5;
}
unsafe
you can define an unsafe context in C# using unsafe modifier. In unsafe context,
you can write an unsafe code, example: C++ pointers etc. See the following
code:
public unsafe MyFunction( int * pInt, double* pDouble)
{
int* pAnotherInt = new int;
*pAnotherInt = 10;
pInt = pAnotherInt;
...
*pDouble = 8.9;
}
Interfaces
If you have an idea of the COM, you will immediately know what i am talking
about. An interface is the abstract base class containing only the function
signatures whose implementaion is provided by the child class. In C#, you
define such classes as interfaces using the interface keyword. .NET is based on
such interfaces. In C#, where you can't use multiple class inheritance, which
was previously allowed in C++, the essence of multiple inheritance is acheived
through interfaces. That's your child class may implement multiple interfaces.
using System;
interface myDrawing
{
int originx
{
get;
set;
}
int originy
{
get;
set;
}
void Draw(object shape);
}
class Shape: myDrawing
{
int OriX;
int OriY;
public int originx
{
get{
return OriX;
}
set{
OriX = value;
}
}
public int originy
{
get{
return OriY;
}
set{
OriY = value;
}
}
public void Draw(object shape)
{
... // do something
}
// class's own method
public void MoveShape(int newX, int newY)
{
.....
}
}
Arrays
Arrays in C# are much better than C++. Arrays are allocated in heap and thus are
reference type. You can't access an out of bound element in an array. So C#
prevents you from that type of bugs. Also some helper functions to iterate
array elements are provided. foreach is the statement
for such iteration. The difference between the syntax of C++ and C# array is:
-
the square brackets are placed after the type and not after the variable name
-
you create element locations using new operator.
C# supports single dimensional, multi dimensional, and jagged array
(array of array).
Examples:
// single-dimensional array of int
int[] array = new int[10];
for (int i = 0; i < array.Length; i++)
array[i] = i;
// 2-dimensional array of int
int[,] array2 = new int[5,10];
array2[1,2] = 5;
// 3-dimensional array of int
int[,,] array3 = new int[5,10,5];
array3[0,2,4] = 9;
// Jagged array - array of array of int
int[][] arrayOfarray = = new int[2];
arrayOfarray[0] = new int[4];
arrayOfarray[0] = new int[] {1,2,15};
Indexers
Indexer is used to write a method to access an element from a collection
using straight way of using [], like an array. All you need is to specify the
index to access an instance or element. Syntax of and Indexer is same as that
of class properties, except they take the input parameter, that is the index of
the element.
Example:
Note: CollectionBase is the library class used for making collections. List is
the protected member of CollectionBase which stores the collection list.
class Shapes: CollectionBase
{
public void add(Shape shp)
{
List.Add(shp);
}
//indexer
public Shape this[int index]
{
get {
return (Shape) List[index];
}
set {
List[index] = value ;
}
}
}
Boxing/Unboxing
The idea of boxing is new in C#. As mentioned above, all data types, built-in or
user defined are derived from a base class object in the System
namespace. So the packing of basic or primitive type into an object is
called boxing, whereas the reverse of this known as unboxing.
Example:
class Test
{
static void Main()
{
int myInt = 12;
object obj = myInt ; // boxing
int myInt2 = (int) obj; // unboxing
}
}
Example shows both boxing and unboxing. An int value can
be converted to object and back again to int. When a variable of a value type
needs to be converted to a reference type, an object box is allocated to hold
the value, and the value is copied into the box. Unboxing is just the opposite.
When an object box is cast back to its original value type, the value is copied
out of the box and into the appropriate storage location.
Function Parameters
Parameters in C# are of three types:
-
By-Value/In parameters
-
By-Reference/In-Out Parameters
-
Out Parameters
If you have an idea of COM interface and it's parameters types, you will easily
understand the C# parameter types.
By-Value/In parameters
The concept of value parameters is same as in C++. The value of the passed value
is copied into a location and is passed to the function.
Example:
SetDay(5);
...
void SetDay(int day)
{
....
}
By-Reference/In-Out Parameters
The reference parameters in C++ are passed either through pointers or reference
operator &. In C# reference parameters are less error prone. Reference
parameters are also called In-Out parameters because you pass a reference
address of the location, so you pass an input value and get an output value
from that function.
You can not pass an uninitialized reference parameter into a function. C# uses a
keyword ref for the reference parameters.
Example:
int a= 5;
FunctionA(a);
Console.WriteLine(a); // prints 20
void FunctionA(ref int Val)
{
int x= Val;
Val = x* 4;
}
Out Parameter
Out parameter is the parameter which only returns value from the function. The
input value is not required. C# uses a keyword out for the out
parameters
Example:
int Val;
GetNodeValue(Val);
bool GetNodeValue(out int Val)
{
Val = value;
return true;
}
Variable number of parameters and Arrays
Arrays in C# when are passed through a keyword params. An array type
parameter should always be the right most argument of the function. Only one
parameter can be of array type. You can pass any number of elements as an
argument of type of that array. You can better understand it from example
Example:
void Func(params int[] array)
{
Console.WriteLine("number of elements {0}", array.Length);
}
Func(); // prints 0
Func(5); // prints 1
Func(7,9); // prints 2
Func(new int[] {3,8,10}); // prints 3
int[] array = new int[8] {1,3,4,5,5,6,7,5};
Func(array); // prints 8
Operators and Expressions
Operators are exactly the same as of C++ and thus the expression also. However
some new and useful operators are also added. Some of them are discussed here.
is operator
is operator is used to check whether the operand types are equal or convertable.
The is operator is aprticularly useful in the polymorphism scenarios. is
operator takes two operands and the result is a boolean. See the example:
function(object param)
{
if(param is ClassA)
//do something
else if(param is MyStruct)
//do something
}
}
as operator
as operator checks if the type of the operands are convertable or
equal (as is done by is operator) and if it is, the result is a converted or
boxed object (if the operand can be boxed into the target type, see
boxing/unboxing). If the objects are not convertable or boxable, the
return is a null. Have a look at the example below to better understand
the concept.
Shape shp = new Shape();
Vehicle veh = shp as Vehicle; // result is null, types are not convertable
Circle cir = new Circle();
Shape shp = cir;
Circle cir2 = shp as Circle; //will be converted
object[] objects = new object[2];
objects[0] = "Aisha";
object[1] = new Shape();
string str;
for(int i=0; i&< objects.Length; i++)
{
str = objects[i] as string;
if(str == null)
Console.WriteLine("can not be converted");
else
Console.WriteLine("{0}",str);
}
Output:
Aisha
can not be converted
Statements
Statements in C# are just like in C++ except some additions of new
statements and modifications in some statements.
Followings are new statements:
foreach
For iteration of collections like arrays etc.
Example:
foreach (string s in array)
Console.WriteLine(s);
lock
Used in threads for locking a block of code making it a critical section.
checked/unchecked
The statements are for overflow checking in numeric operations.
Example:
int x = Int32.MaxValue;
x++; // Overflow
checked
{
x++; // Exception
}
unchecked
{
x++; // Overflow}
}
Following statements are modified:
Switch
Switch statement is modified in C#.
-
Now after executing a case statement, program flow can not jump to next case
which was previously allowed in C++. Example:
int var = 100;
switch (var)
{
case 100: Console.WriteLine("<Value is 100>"); // No break here
case 200: Console.WriteLine("<Value is 200>"); break;
}
Output in C++:
<Value is 100><Value is 200>
In C# you get compile time error:
error CS0163: Control cannot fall through from one case
label ('case 100:') to another
However you can do this similarly you do it in C++:
switch (var)
{
case 100:
case 200: Console.WriteLine("100 or 200");
break;
}
You can also use constant variables for case values:
Example:
const string WeekEnd = "Sunday";
const string WeekDay1 = "Monday";
....
string WeekDay = Console.ReadLine();
switch (WeekDay )
{
case WeekEnd: Console.WriteLine("It's weekend!!"); break;
case WeekDay1: Console.WriteLine("It's Monday"); break;
}
Delegates
Delegate let us store function references into a variable. In C++, this is like
using and storing function pointer for which we usually use typedef.
Delegates are declared using a keyword delegate. Have
a look at this example, and you will understand what delegates are:
Example:
delegate int Operation(int val1, int val2);
public int Add(int val1, int val2)
{
return val1 + val2;
}
public int Subtract (int val1, int val2)
{
return val1- val2;
}
public void Perform()
{
Operation Oper;
Console.WriteLine("Enter + or - ");
string optor = Console.ReadLine();
Console.WriteLine("Enter 2 operands");
string opnd1 = Console.ReadLine();
string opnd2 = Console.ReadLine();
int val1 = Convert.ToInt32 (opnd1);
int val2 = Convert.ToInt32 (opnd2);
if (optor == "+")
Oper = new Operation(Add);
else
Oper = new Operation(Subtract);
Console.WriteLine(" Result = {0}", Oper(val1, val2));
}
Inheritance and Polymorphism
Only single inheritance is allowed in C#. Mutiple inheritance can be
acheived using interfaces.
Example:
class Parent{
}
class Child : Parent
Virtual Functions
Virtual functions to implement the concept of polymorphism are same in
C#, except you use the override keyword with the
virtual function implementaion in the child class. The parent class uses the
same virtual keyword. Every class which overrides the
virtual method will use override keyword.
class Shape
{
public virtual void Draw()
{
Console.WriteLine("Shape.Draw") ;
}
}
class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Rectangle.Draw");
}
}
class Square : Rectangle
{
public override void Draw()
{
Console.WriteLine("Square.Draw");
}
}
class MainClass
{
static void Main(string[] args)
{
Shape[] shp = new Shape[3];
Rectangle rect = new Rectangle();
shp[0] = new Shape();
shp[1] = rect;
shp[2] = new Square();
shp[0].Draw();
shp[1].Draw();
shp[2].Draw();
}
}
Output:
Shape.Draw
Rectangle.Draw
Square.Draw
Hiding parent functions using "new"
You can define in a child class a new version of a function, hiding the one
which is in base class. A keyword new is used to
define a new version. Consider the example below, which is a modified version
of above example and note the output this time, when i replace the
keyword override with a keyword new in Rectangle class.
class Shape
{
public virtual void Draw()
{
Console.WriteLine("Shape.Draw") ;
}
}
class Rectangle : Shape
{
public new void Draw()
{
Console.WriteLine("Rectangle.Draw");
}
}
class Square : Rectangle
{
//wouldn't let u override it here
public new void Draw()
{
Console.WriteLine("Square.Draw");
}
}
class MainClass
{
static void Main(string[] args)
{
Console.WriteLine("Using Polymorphism:");
Shape[] shp = new Shape[3];
Rectangle rect = new Rectangle();
shp[0] = new Shape();
shp[1] = rect;
shp[2] = new Square();
shp[0].Draw();
shp[1].Draw();
shp[2].Draw();
Console.WriteLine("Using without Polymorphism:");
rect.Draw();
Square sqr = new Square();
sqr.Draw();
}
}
Output:
Using Polymorphism
Shape.Draw
Shape.Draw
Shape.Draw
Using without Polymorphism:
Rectangle.Draw
Square.Draw
See when the polymorphism doesn't take the Rectangle class's Draw method as a
polymorphic form of the Shape's Draw method, instead it considers it a
different method. So in order to avoid the naming conflict between parent and
child, we have used new modifier.
Note: you can not use in the same class the two version of a method, one with
new modifier and other with override or virtual. Like in above example i can
not add another method named Draw in Rectangle class which is a virtual or
override method. Also in the Square class i can't override the virtual Draw
method of Shape class.
Calling base class members
If the child class has the data members with same name as that of base class, in
order to avoid naming conflicts, base class data members and functions are
accessed using a keyword base. See in examples how
the base class constructors are called and how the data members are used.
public Child(int val) :base(val)
{
myVar = 5;
base.myVar;
}
OR
public Child(int val)
{
base(val);
myVar = 5 ;
base.myVar;
}
Future Additions:
This article is just a quick overwiew of the C# language so that you can just
become familiar with the langauge features.
Although I have tried to discuss almost all the major concepts in C# in a brief
and comprehensive way with code examples yet I i think there is lot much to be
added and discussed. In future, I would like to add more commands and concepts
not yet discussed including events etc.
I would also like to write for beginners the Windows programming using C#.
References:
-
Our most commonly known MSDN
-
Inside C# by Tom Archer
-
A Programmer's Introduction to C# by Eric Gunnerson
-
Beginning C# by Karli Watson
-
Programming C# (O'Reilly)