C# Advanced Features Guide
C# Advanced Features Guide
DELEGATES
Delegates are type-safe classes that defi ne the return types and types of parameters. The
delegate class not only contains a reference to a method, but can hold references to multiple
methods.
Lambda expressions are directly related to delegates. When the parameter is a delegate type,
you can use a lambda expression to implement a method that’s referenced from the delegate.
Delegates exist for situations in which you want to pass methods around to other methods. To
see what that means, consider this line of code: int i = int.Parse("99"); You are so used to
passing data to methods as parameters, as in this example, that you don’t consciously think
about it, so the idea of passing methods around instead of data might sound a little strange.
However, sometimes you have a method that does something, and rather than operate on data,
the method might need to do something that involves invoking another method. To
complicate things further, you do not know at compile time what this second method is. That
information is available only at runtime and hence will need to be passed in as a parameter to
the first method. That might sound confusing, but it should become clearer with a couple of
examples:
➤ Starting threads and tasks — It is possible in C# to tell the computer to start a new
sequence of execution in parallel with what it is currently doing. Such a sequence is known as
a thread, and starting one is done using the Start method on an instance of one of the base
classes, System .Threading.Thread. If you tell the computer to start a new sequence of
execution, you have to tell it where to start that sequence; that is, you have to supply the
details of a method in which execution can start. In other words, the constructor of the Thread
class takes a parameter that defi nes the method to be invoked by the thread.
➤ Generic library classes — Many libraries contain code to perform various standard tasks. It
is usually possible for these libraries to be self-contained, in the sense that you know when
you write to the library exactly how the task must be performed. However, sometimes the
task contains a subtask, which only the individual client code that uses the library knows how
to perform. For example, say that you want to write a class that takes an array of objects and
sorts them in ascending order. Part of the sorting process involves repeatedly taking two of
the objects in the array and comparing them to see which one should come fi rst. If you want
to make the class capable of sorting arrays of any object, there is no way that it can tell in
advance how to do this comparison. The client code that hands your class the array of objects
must also tell your class how to do this comparison for the particular objects it wants sorted.
The client code has to pass your class details of an appropriate method that can be called to
do the comparison.
➤ Events — The general idea here is that often you have code that needs to be informed
when some event takes place. GUI programming is full of situations similar to this. When the
event is raised, the runtime needs to know what method should be executed. This is done by
passing the method that handles the event as a parameter to a delegate.
Declaring Delegates
When you want to use a class in C#, you do so in two stages. First, you need to defi ne the
class — that is, you need to tell the compiler what fi elds and methods make up the class.
Then (unless you are using only static methods), you instantiate an object of that class. With
delegates it is the same process. You start by declaring the delegates you want to use.
Declaring delegates means telling the compiler what kind of method a delegate of that type
will represent. Then, you have to create one or more instances of that delegate. Behind the
scenes, the compiler creates a class that represents the delegate. The syntax for declaring
delegates looks like this:
This declares a delegate called IntMethodInvoker, and indicates that each instance of this
delegate can hold a reference to a method that takes one int parameter and returns void. The
crucial point to understand about delegates is that they are type-safe. When you defi ne the
delegate, you have to provide full details about the signature and the return type of the
method that it represents.
NOTE One good way to understand delegates is to think of a delegate as something that
gives a name to a method signature and the return type. Suppose that you want to defi ne a
delegate called TwoLongsOp that will represent a method that takes two longs as its
parameters and returns a double.
Or, to defi ne a delegate that will represent a method that takes no parameters and returns a
string, you might write this:
delegate string GetAString();
The syntax is similar to that for a method definition, except there is no method body and the
definition is prefixed with the keyword delegate. Because what you are doing here is
basically defining a new class, you can defi ne a delegate in any of the same places that you
would defi ne a class — that is to say, either inside another class, outside of any class, or in a
namespace as a top-level object. Depending on how visible you want your definition to be,
and the scope of the delegate, you can apply any of the normal access modifiers to delegate
definitions — public, private, protected, and so on
Using Delegates
The following code snippet demonstrates the use of a delegate. It is a rather long-winded way
of calling the ToString method on an int (code fi le GetAStringDemo/Program.cs):
Instead of defi ning a new delegate type with every parameter and return type, you can use
the Action and Func delegates. The generic Action delegate is meant to reference a method
with void return. This delegate class exists in different variants so that you can pass up to 16
different parameter types. The Action class without the generic parameter is for calling
methods without parameters. Action is for calling a method with one parameter; Action for a
method with two parameters; and Action for a method with eight parameters.
The Func delegates can be used in a similar manner. Func allows you to invoke methods with
a return type. Similar to Action, Func is defi ned in different variants to pass up to 16
parameter types and a return type. Func is the delegate type to invoke a method with a return
type and without parameters. Func is for a method with one parameter, and Func is for a
method with four parameters.
The example in the preceding section declared a delegate with a double parameter and a
double return type:
lamda:
lambda expressions. Lambda expressions can be used whenever you have a delegate
parameter type. The previous example using anonymous methods is modifi ed here to use a
lambda expression.
With the publisher class CarDealer there’s no need to change anything. It has the same
implementation as before.
GENRICS
There are some significant characteristics of Generic types that make them special to the
conventional non-generics type as follows;
Type Safety
Performance
Type Safety
One of the most significant features of Generics is Type Safety. In the case of the non-generic
ArrayList class, if objects are used, any type can be added to the collections that can
sometimes result in a great disaster. The following example shows adding an integer, string
and object to the collection of an ArrayList type;
Now, if the collection is iterated through the foreach statement using integer elements, the
compiler accepts the code but because all the elements in the collection are not an integer, a
runtime exception occurs;
foreach(int i in obj)
{
Console.WriteLine(i);
}
The rule of thumb in programming is that Errors should be detected as early as possible. With
the generic class Test<T>, the generic type T defines what types are allowed. With the
definition of Test<int>, only an integer type can be added to the collection. The compiler
doesn't compile the code because the Add() method has invalid arguments as follows;
Another feature of Generics is performance. Using value types with non-generic collection
classes result in boxing and unboxing overhead when a value type is converted to reference
type and vice-versa.
In the following example, the ArrayList class stores objects and the Add() method is defined
to store some integer type argument. So an integer type is boxed. When the value from
ArrayList is read using the foreach statement, unboxing occurrs.
}
Note
Instead of using objects, a Generics type of the TestClass<T> class is defined as an int, so an
int type is used inside the class that is generated dynamically from the compiler. Therefore
boxing and unboxing no longer occurs as in the following;
Generics provide a kind of source code protection. A Generic class can be defined once and
can be instantiated with many different types. Generics can be defined in one CLR supported
language and used from another .NET language. The following TestClass<T> is instantiated
with an int and string types:
Generics allow you to define the specification of the data type of programming elements in a
class or a method, until it is actually used in the program. In other words, generics allow you
to write a class or method that can work with any data type.
C# allows you to define generic classes, interfaces, abstract classes, fields, methods, static
methods, properties, events, delegates, and operators using the type parameter and without
the specific data type. A type parameter is a placeholder for a particular type specified
when creating an instance of the generic type.
A generic type is declared by specifying a type parameter in an angle brackets after a type
name, e.g. TypeName<T> where T is a type parameter.
The Generic class can be defined by putting the <T> sign after the class name. It isn't
mandatory to put the "T" word in the Generic type definition. You can use any word in the
TestClass<> class declaration.
Generic Class
Generic classes are defined using a type parameter in an angle brackets after the class
name. The following defines a generic class.
Above, the DataStore is a generic class. T is called type parameter, which can be used as a
type of fields, properties, method parameters, return types, and delegates in
the DataStore class. For example, Data is generic property because we have used a type
parameter T as its type instead of the specific data type.
You can create an instance of generic classes by specifying an actual type in angle
brackets. The following creates an instance of the generic class DataStore.
Above, we specified the string type in the angle brackets while creating an instance.
So, T will be replaced with a string type wherever T is used in the entire class at compile-
time. Therefore, the type of Data property would be a string.
kvp2.Key = "IT";
kvp2.Value = "Information Technology";
Generic Class Characteristics
A generic class increases the reusability. The more type parameters mean more
reusable it becomes. However, too much generalization makes code difficult to
understand and maintain.
A generic class can be a base class to other generic or non-generic classes or abstract
classes.
A generic class can be derived from other generic or non-generic interfaces, classes,
or abstract classes.
namespace GenericApp
{
public class TestClass<T>
{
// define an Array of Generic type with length 5
T[] obj = new T[5];
int count = 0;
}
count++;
}
//indexer for foreach statement iteration
public T this[int index]
{
get { return obj[index]; }
set { obj[index] = value; }
}
}
class Program
{
static void Main(string[] args)
{
//instantiate generic with Integer
TestClass<int> intObj = new TestClass<int>();
//displaying values
for (int i = 0; i < 5; i++)
{
Console.WriteLine(intObj[i]); //No unboxing
}
Console.ReadKey();
}
}
Generic Fields
class DataStore<T>
{
public T data;
}
Generic Methods
A method declared with the type parameters for its return type or parameters is called a
generic method.
Above, the AddorUpdate() and the GetData() methods are generic methods. The actual
data type of the item parameter will be specified at the time of instantiating
the DataStore<T> class, as shown below.
empIds.AddOrUpdate(2, 89);
The generic parameter type can be used with multiple parameters with or without non-
generic parameters and return type. The followings are valid generic method overloading.
A non-generic class can include generic methods by specifying a type parameter in angle
brackets with the method name, as shown below.
using System;
using System.Collections.Generic;
namespace GenericApp
{
class Program
{
//Generic method
static void Swap<T>(ref T a, ref T b)
{
T temp;
temp = a;
a = b;
b = temp;
}
static void Main(string[] args)
{
// Swap of two integers.
int a = 40, b = 60;
Console.WriteLine("Before swap: {0}, {1}", a, b);
Console.ReadLine();
}
}
Advantages of Generics
1. Generics increase the reusability of the code. You don't need to write code to handle
different data types.
2. Generics are type-safe. You get compile-time errors if you try to use a different data
type than the one specified in the definition.
3. Generic has a performance advantage because it removes the possibilities of boxing
and unboxing.
Value Data Types Windows uses a system known as virtual addressing, in which the mapping
from the memory address seen by your program to the actual location in hardware memory is
entirely managed by Windows. As a result, each process on a 32-bit processor sees 4GB of
available memory, regardless of how much hardware memory you actually have in your
computer (on 64-bit processors this number is greater). This memory contains everything that
is part of the program, including the executable code, any DLLs loaded by the code, and the
contents of all variables used when the program runs. This 4GB of memory is known as the
virtual address space or virtual memory. For convenience, this chapter uses the shorthand
memory. Each memory location in the available 4GB is numbered starting from zero. To
access a value stored at a particular location in memory, you need to supply the number that
represents that memory location. In any compiled high-level language, including C#, Visual
Basic, C++, and Java, the compiler converts human-readable variable names into memory
addresses that the processor understands.
Somewhere inside a processor’s virtual memory is an area known as the stack. The stack
stores value data types that are not members of objects. In addition, when you call a method,
the stack is used to hold a copy of any parameters passed to the method. To understand how
the stack works, you need to understand the importance of variable scope in C#. If variable a
goes into scope before variable b, then b will always go out of scope fi rst. Consider the
following code: { int a; // do something { int b; // do something else } } First, a is declared.
Then, inside the inner code block, b is declared. Then the inner code block terminates and b
goes out of scope, then a goes out of scope. Therefore, the lifetime of b is entirely contained
within the lifetime of a. The idea that you always deallocate variables in the reverse order of
how you allocate them is crucial to the way the stack works. Note that b is in a different block
from code (defi ned by a different nesting of curly braces). For this reason, it is contained
within a different scope. This is termed as block scope or structure scope.
Reference Data Types Although the stack provides very high performance, it is not fl exible
enough to be used for all variables. The requirement that the lifetime of a variable must be
nested is too restrictive for many purposes. Often, you need to use a method to allocate
memory for storing data and keeping that data available long after that method has exited.
This possibility exists whenever storage space is requested with the new operator — as is the
case for all reference types. That is where the managed heap comes in.
Garbage Collection:
When the garbage collector runs, it removes all those objects from the heap that are no longer
referenced. Immediately after doing this, the heap will have objects scattered on it, mixed up
with memory that has just been freed (see Figure 14-4). If the managed heap stayed like this,
allocating space for new objects would be an awkward process, with the runtime having to
search through the heap for a block of memory big enough to store each new object.
However, the garbage collector does not leave the heap in this state. As soon as the garbage
collector has freed up all the objects it can, it compacts the heap by moving all the remaining
objects to form one continuous block of memory. This means that the heap can continue
working just like the stack, as far as locating where to store new objects. Of course, when the
objects are moved about, all the references to those objects need to be updated with the
correct new addresses, but the garbage collector handles that too.
Pointers:-
A pointer is simply a variable that stores the address of something else in the same way as a
reference.
REFLECTION
Reflection is a generic term that describes the capability to inspect and manipulate program
elements at runtime. For example, reflection allows you to do the following:
AttributeUsage Attribute:-
The members of the AttributeTargets enumeration are as follows: ➤ All ➤ Assembly ➤ Class
➤ Constructor ➤ Delegate ➤ Enum ➤ Event ➤ Field ➤ GenericParameter (.NET 2.0 and
higher only) ➤ Interface ➤ Method ➤ Module ➤ Parameter ➤ Property ➤ ReturnValue ➤
Struct
C# Exception Handling
In C#, exception is an event or object which is thrown at runtime. All exceptions the derived
from System.Exception class. It is a runtime error which can be handled. If we don't handle
the exception, it prints exception message and terminates the program.
Advantage
It maintains the normal flow of the application. In such case, rest of the code is executed
event after exception.
C# Exception Classes
All the exception classes in C# are derived from System.Exception class. Let's see the list of
C# common exception classes.
Exception Description
Syntax
C# try/catch
Output:
C# try/catch example
1. using System;
2. public class ExExample
3. {
4. public static void Main(string[] args)
5. {
6. try
7. {
8. int a = 10;
9. int b = 0;
10. int x = a / b;
11. }
12. catch (Exception e) { Console.WriteLine(e); }
13.
14. Console.WriteLine("Rest of the code");
15. }
16. }
Output:
C# finally
C# finally block is used to execute important code which is to be executed whether exception
is handled or not. It must be preceded by catch or try block.
1. using System;
2. public class ExExample
3. {
4. public static void Main(string[] args)
5. {
6. try
7. {
8. int a = 10;
9. int b = 0;
10. int x = a / b;
11. }
12. catch (Exception e) { Console.WriteLine(e); }
13. finally { Console.WriteLine("Finally block is executed"); }
14. Console.WriteLine("Rest of the code");
15. }
16. }
Output:
A try block can throw multiple exceptions, which can handle by using multiple catch blocks.
Remember that more specialized catch block should come before a generalized one.
Otherwise the compiler will show a compilation error.
28. }
By providing a catch block without brackets or arguments, we can catch all exceptions
occurred inside a try block. Even we can use a catch block with an Exception type parameter
to catch all exceptions happened inside the try block since in C#, all exceptions are directly or
indirectly inherited from the Exception class.
Throwing an Exception
In C#, it is possible to throw an exception programmatically. The 'throw' keyword is used for
this purpose. The general form of throwing an exception is as follows.
1. throw exception_obj;
Re-throwing an Exception
The exceptions, which we caught inside a catch block, can re-throw to a higher context by
using the keyword throw inside the catch block. The following program shows how to do
this.
User-defined Exceptions
In C#, it is possible to create our own exception class. But Exception must be the ultimate
base class for all exceptions in C#. So the user-defined exception classes must inherit from
either Exception class or one of its standard derived classes.