You are on page 1of 20

EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

Unit 4: Exception Handling, Events and Delegates

Exception Handling and Debugging

No matter how good a programmer is, he will always encounter errors or bugs. Testing for errors
eats up a large percentage of program development. It would be better if there are ways to
prevent this errors or at least "degrade gracefully", that is, inform the user about certain errors
instead of the program suddenly crashing and showing up cryptic error messages the user can't
understand.

Thankfully, C# has ways to do just that. .NET Framework offers a large collection of Exception
classes for specific errors. Exceptions are .NET's way of saying an error was found while the
program is running. The .NET Framework contains a large collection of exception classes that
you can use to handle different errors on different situations. You can even create your own
exception class! Exceptions are "thrown" by the program, and you need to "catch" these
exceptions and act accordingly.

For example, in the computer world, an integer can never be divided by 0. Try that on a
calculator and you will receive an error. If a C# program encounters such an error, it will throw a
DivideByZeroException which is an exception that is thrown when you divide a number by zero.

Bugs are another term for errors or code that produce unwanted behavior to a program.
Debugging is the process of removing those bugs, which means, removing the errors and
misbehaviors. Visual Studio has tools for debugging, which finds errors and allows you to fix
them. You will learn how to effectively use these tools to remove bugs(errors) from your
program. Excessive testing and debugging of programs are necessary before you deploy the final
version of your program.

1
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

Unhandled Exceptions

Unhandled exceptions are exceptions that were not properly handled by the program causing
the program to terminate. We will demonstrate here what will happen if a program detects an
exception during runtime and there is no exception handling used. You will see how an
exception can potentially destroy the flow or execution of your application. We are running our
examples from the beginning up until now using Non-Debug mode. Running a program that
contains no errors using Non-Debug or Debug modes have a minimal difference. Here, we will
clearly see the difference of each mode.

Let's create our test program. Create a new Console Application and name it ExceptionTest. Use
the following code.

using System;

namespace UnhandledExceptionDemo
{
public class Program
{
public static void Main()
{
int five = 5;
int zero = 0;

//Generate an exception by dividing 5 by 0


int result = five / zero;
}
}
}

Example 1 - A Program without Exception Handling

Dividing an integer by 0 is illegal and will cause a System.DivideByZeroException.

No Exception Handling (Non-Debug Mode)

Run the program in Non-Debug mode by hitting Ctrl+F5 shortcut. The program will successfully
compile, but you will get the following error message.

Unhandled Exception: System.DivideByZeroException: Attempted to divide by


zero.
at ExceptionTest.Program.Main() in C:\Users\TheUser\AppData\Local\Temporary
Projects\ExceptionTest\Program.cs:line 9

Then a window will prompt showing that the program was terminated.

2
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

Figure 1 - Unhandled Exception Window

As you can see the label "Unhandled Exception". This is because the error was not handled
properly because we did not use any exception handling techniques. These details are usually not
needed by the one who will use your program.

No Exception Handling (Debug Mode)

There is a better way to see information about an unhandled exception. And that is by running
the program in debug mode. You can go to Debug > Start Debugging to start Debug Mode.
You can also hit the F5 key, or click the green play button located at the toolbar. It will now start
debugging the program. During Debug Mode, Visual C# Express will stop executing and bring
you to you code. The statement that produced the error will be highlighted in yellow and the
Exception Assitant will show up.

3
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

Figure 2 - The Exception Assistant

The Exception Assistant is a helpful window that describes the exception that was unhandled
and tries to give some tips on how you can troubleshoot or fix them. If the Exception Assistant is
hidden, simply clicked again on the statement that threw the exception.

try and catch Statements

You can handle errors by using a try...catch statement. You wrap the code that you suspect to
trigger an error inside a try block. The catch block contains the codes to be executed when an
error is encountered. The following program demonstrates using try...catch statements.

4
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

using System;
namespace TryCatchDemo
{
public class Program
{
public static void Main()
{
int result;
int x = 5;
int y = 0;

try
{
result = x / y; // Exception will be thrown
}
catch
{
Console.WriteLine("An attempt to divide by 0 was detected.");
}

}
}
}

Example 1 - The try...catch Statements

An attempt to divide by 0 was detected.

Inside the try block, we divided x which has a value of 5 by y which contains 0. The calculation
will cause a DivideByZeroException. Note in C#, saying an exception was thrown simply
means that a particular error was encountered by the program. That's why the catch block was
named like that because it "catches" the exceptions that were thrown. Since an exception was
thrown, the codes inside the catch block will be executed. If the line of code causes an error, it
will jump immediately to the catch block skipping the following lines. Therefore:

try
{
result = x / y; //Error: Jump to catch block
Console.WriteLine("This line will not be executed.");
}
catch
{
Console.WriteLine("An attempt to divide by 0 was detected.");
}

You can indicate a specific type of exception that you want to handle by indicating it on the
catch block like this:

try
{
result = x / y; //ERROR
}
catch (DivideByZeroException)

5
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

{
Console.WriteLine("An attempt to divide by 0 was detected.");
}

Additionally you can provide a variable that will hold the values for the exception.

try
{
result = x / y; //ERROR
}
catch (DivideByZeroException error)
{
Console.WriteLine(error.Message);
}
Attempted to divide by zero.

The variable will hold useful informations about the exception that was thrown. We used the
Message property to show the description of the message. Every exception class has descriptions
about the errors. You will learn more properties of Exception in later lessons.

If you are expecting more than one error to occur within the try block, you can provide multiple
catch blocks but you should specify the type of exception for each of them.

int result;
int x = 5;
int y;

try
{
y = Int32.Parse(Console.ReadLine());
result = x / y;
}
catch (DivideByZeroException error)
{
Console.WriteLine(error.Message);
}
catch (FormatException error)
{
Console.WriteLine(error.Message);
}

Since the value of y is determined from the input of the user, the value can be a non-zero integer.
But there is a problem. What if the user types a non-numerical value such as a letter. They cannot
be properly converted to an int datatype so a FormatException will be thrown. When this
exception is thrown, the catch block for the FormatException will be executed. The calculation
for the quotient of the x and y will be skipped.

What if you want to catch all the possible errors inside the try block? Simply use the generic
Exception class. Every exception in .NET inherits from this class therefore, you can store any
type of exception inside an object of the Exception class.

6
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

try
{
//Put your codes to test here
}
catch (Exception error)
{
Console.WriteLine(error.Message);
}

Now, you don't need to worry that an error may get away. The catch block will handle every
error that is detected within the try block and then show the appropriate message for that
particular exception.

Note that if you will use the Exception base class in a catch block together with other derived
Exception classes, then the base Exception class must be placed at the final catch block.

try
{
//Put your codes to test here
}
catch (DivideByZeroException)
{
Console.WriteLine("Division by zero is not allowed.");
}
catch (FormatException)
{
Console.WriteLine("Error on converting the data to proper type.");
}
catch (Exception)
{
Console.WriteLine("An error occured.");
}

This makes sense because if you place it in the first catch block, then it will always be match
first and the other catch blocks after it will never be executed even if they have the specific
exception that matches the one thrown by the program.

You can also use the is operator or check the type of the exception to assign specific behaviors
depending on the errors caught.

try
{

}
catch(Exception error)
{
if (error is DivideByZeroException)
{
Console.WriteLine("Cannot divide by zero!");
}
if (error is FormatException)
{

7
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

Console.WriteLine("Format cannot be accepted!");


}
}

The catch block uses the Exception class to catch all the exceptions thrown by the program.
Inside the catch block, you can then test what kind of Exception was thrown using an if
statement and the is keyword.

Using finally Blocks


Using finally blocks, you can provide a code that will always execute even if exceptions are
thrown. We learned that if an exception was thrown inside a try block, all the following codes in
the try block will be skipped because the execution will immediately jump to the catch block.
Those skipped codes could have a vital role in the program. That's the purpose of the finally
block. You place the codes that you think is essential or needs to be executed inside the finally
block. The following program shows an example of using a finally block.

using System;

namespace FinallyBlockDemo
{
public class Program
{
public static void Main()
{
int result;
int x = 5;
int y = 0;

try
{
result = x / y;
}
catch (DivideByZeroException error)
{
Console.WriteLine(error.Message);
}
finally
{
Console.WriteLine("finally blocked was reached.");
}
}
}
}

Example 1 - Using the finally Block

Attempted to divide by zero.


finally blocked was reached.

8
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

The finally block comes right after catch block. If there are multiple catch blocks, the finally
block must be placed after them. Note that if you will use a finally block, you are allowed to not
define a catch block such as the following:

try
{
//some code
}
finally
{
//some code
}

finally blocks are often used when you want to dispose of an object or close a database
connection or file stream.

Throwing Exceptions

You can throw exceptions anywhere in the program to artificially create an occurrence of an
error. Additionally, you can create custom messages if you don't like the default message of an
exception. The following program shows you an example.

using System;
namespace ThrowingExceptions
{
class Program
{
public static void Main()
{
int firstNumber, secondNumber, result;

Console.Write("Enter the first number: ");


firstNumber = Int32.Parse(Console.ReadLine());

Console.Write("Enter the second number: ");


secondNumber = Int32.Parse(Console.ReadLine());

try
{
if (secondNumber == 0)
{
throw new DivideByZeroException();
}
else
{
result = firstNumber / secondNumber;
}

9
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

}
catch (DivideByZeroException error)
{
Console.WriteLine(error.Message);
}
}
}
}

Example 1 - Manually Throwing Exceptions

Enter the first number: 10


Enter the second number: 0
Attempted to divide by zero.

In line 19, we used the throw keyword followed by an instance of an exception class. We have
just gone straight to creating the instance and throwing it in just one line. For example, you can
create an object first and then throw it.

DivideByZeroException error = new DivideByZeroException();

throw error;

You can also give your own error message by using an overloaded constructor of the Exception
class which accepts a string as the error message.

throw new DivideByZeroException("Cannot divide by zero!");

This will modify the default error message stored in the Message property. Throwing exceptions
are mostly used when a certain code will not naturally yield an error, but you want to consider it
an error anyways. The next lesson will show you something like that.

Exception Properties

The System.Exception base class is the class that is inherited by all exception classes in the
.NET Framework. Therefore, the properties of the Exception class is available to other
exception classes. The following table shows some notable properties of the System.Exception
class which is shared by all of the other exception classes.

Property Description

InnerException Specifies the Exception instance that caused the current exception.

Message Specifies the message that describes the exception.

10
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

Property Description

StackTrace Returns a string representation of the method call stack.

Figure 1 - System.Exception Properties

The Message Property

We have seen the Message property several times. It allows you to specify an error message
which describes the Exception that was thrown. For example, consider the following code:

int x = 1;
int y = 0;
int z;

try
{
z = x / y;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Attempted to divide by zero.

We intentionally try to divide by zero so the catch block will be executed. Note that we provide
an instance of the Exception class so we can use the Message property. We then print the content
of the Message property. When you run the program, an error message will show up. Every
predefined Exception class in the .NET framework has their corresponding error message stored
in the Message property.

If you are throwing exceptions, then you can use the overloaded constructor of the Exception
class which accepts a string argument that is the error message.

try
{
if (y == 0)
{
throw new DivideByZeroException("You cannot divide by zero. Sorry my
friend.");
}
else
{
z = x / y;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}

11
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

You cannot divide by zero. Sorry my friend.

The InnerException Property

The InnerException property of the System.Exception class is used to determine the Exception
that caused the current exception. For example, suppose a programmer wants to detect if the
format of an account number is correct. The first thing to do is to convert it from string to int. If
the account number cannot be converted properly to a numerical representation, then a
FormatException will be thrown. Inside the catch block for the FormatException, you can then
do some more processing and then throw another customized exception that describes the error
more clearly. The following program shows you this example.

using System;
namespace InnerExceptionDemo
{
class Program
{
static int ProcessAccountNumber(string accountNumber)
{
try
{
return Convert.ToInt32(accountNumber);
}
catch (FormatException formatException)
{
throw new Exception("Invalid Account Number.",
formatException);
}
}
static void Main(string[] args)
{
Console.Write("Enter an account number: ");
string input = Console.ReadLine();

try
{
int accountNumber = ProcessAccountNumber(input);
}
catch (Exception ex)
{
Console.WriteLine("Current exception's message: {0}", ex.Message);
Console.WriteLine("Inner exception's message: {0}",
ex.InnerException.Message);
}
}
}
}

Example 1 – Inner Exception Demo

Enter an account number: abcde


Current exception's message: Invalid Account Number.
Inner exception's message: Input string was not in a correct format.

In lines 7-17, we defined a method named ProcessAccountNumber. This method accepts a string
argument which is the string input given by the user. As you can see in line 25, this method is

12
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

called and the input given by the user is passed as an argument. It is also enclosed in a try block
because it might cause an exception. After the method is called the excecution will transfer to
the first line of the ProcessAccountNumber() method.

Inside that method is yet another try...catch statement. Inside the try block, we try to convert the
string argument into an integer and return the value if successful. If the string argument cannot
be converted, then a FormatException will be thrown and the succeeding catch block will handle
it. Inside the catch block, we throw a new instance of the Exception class and provide a more
descriptive message as the first argument, and the FormatException instance as the second
argument which indicates the inner exception. The catch block inside the calling method
(Main()) will then be executed (line 27) and there, we print the message of the new Exception
and the message of the original inner exception that caused it by using the InnerException
property and its Message property.

The StackTrace Property

The StackTrace property of the System.Exception class allows you to inspect which method in
the method-call stack produced the exception. This is a useful method when debugging since a
method might also call other methods which may result into errors. You might mistaken that
method being called as the culprit, but it might actually be the other methods it calls. The
StackTrace shows the method-call stack as a string representation. To demonstrate this, the
following code creates a deep method-call stack where the final method throws an exception.

using System;

namespace StackTraceDemo
{
class Program
{
static void Method1()
{
Method2();
}

static void Method2()


{
Method3();
}

static void Method3()


{
throw new Exception("Exception at Method3()");
}

static void Main(string[] args)


{
try
{
Method1();
}
catch (Exception ex)

13
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

{
Console.WriteLine(ex.StackTrace);
Console.ReadKey();
}
}
}
}

Example 2 - StackTrace Demo

at StackTraceDemo.Program.Method3() in C:\StackTraceDemo\Program.cs:line 19
at StackTraceDemo.Program.Method2() in C:\StackTraceDemo\Program.cs:line 14
at StackTraceDemo.Program.Method1() in C:\StackTraceDemo\Program.cs:line 9
at StackTraceDemo.Program.Main(String[] args) in
C:\StackTraceDemo\Program.cs:line 26

Each line shows the method, file, and the line number where the exception was thrown. The first
entry which is Method3() is the main source of the exception. The stack continues up to the
Main() method which is the top of the method-call stack. Please note that you must run the
program in debug mode to see the complete method-call stack.

User-Defined Exceptions

You can create your own exceptions. User-defined exceptions must inherit from the Exception
base class. We will create another separate class that inherits from the Exception base class.
Create a new Console Application and name it UserDefinedExceptions. After the project is
created, click the Add New Item button in the toolbar and browse for the Class template. Name
the file NegativeNumberException.

using System;

namespace UserDefinedExceptions
{
class NegativeNumberException : Exception
{
public NegativeNumberException()
: base("The operation will result to a negative number.")
{
}

public NegativeNumberException(string message)


: base(message)
{
}

public NegativeNumberException(string message, Exception inner)

14
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

: base(message, inner)
{
}
}
}

Example 1 - NegativeNumberException Class

You can see in line 5 that our custom class inherits from the Exception class. As a convention,
user-defined exception class names must be appended with the word "Exception" and it should
define 3 constructors. The first one is the parameterless constructor, the second constructor
accepts a string argument which defines the error message, and the third constructor accepts the
error message, and the inner exception the caused the current exception.

Let's try to use our very own exception class. In the Program.cs file, use the following codes.

using System;

namespace UserDefinedExceptions
{
class Program
{
public static void Main()
{
int firstNumber, secondNumber, difference;

Console.Write("Enter the first number: ");


firstNumber = Int32.Parse(Console.ReadLine());

Console.Write("Enter the second number: ");


secondNumber = Int32.Parse(Console.ReadLine());

difference = firstNumber - secondNumber;

try
{
if (difference < 0)
{
throw new NegativeNumberException();
}
}
catch (NegativeNumberException error)
{
Console.WriteLine(error.Message);
}
}
}
}

Example 2 - Using NegativeNumberException

Enter the first number: 10


Enter the second number: 11

15
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

The operation will result to a negative number.

Since yielding a negative number as a result will not issue any kind of exception, we need to
manually throw it ourselves. We ask two values from the user (line 11-15). We then calculated
the difference of the two (line 17). Inside the try block, we tested if the result of the operation is a
negative number (line 21). If it is, then we throw a new instance of the
NegativeNumberException class (line 23). This will then be caught by the catch block and show
the error message (line 24-26).

Delegates

Delegates are types that hold references to methods instead of variables. A delegate can copy the
behavior of any method. To declare a delegate, you use the delegate keyword. Declaring a
delegate is similar to declaring a method except that it has no body. It has a return type and a set
of parameters just like a method. These tells the type of method it can hold. Below shows the
syntax of declaring a delegate.

delegate returnType DelegateName(dt param1, dt param2, ... dt paramN);

The following example program shows how to use and the benefits of using a delegate.

using System;

namespace DelegatesDemo
{
public class Program
{
delegate void ArithmeticDelegate(int num1, int num2);

static void Add(int x, int y)


{
Console.WriteLine("Sum is {0}.", x + y);
}

static void Subtract(int x, int y)


{
Console.WriteLine("Difference is {0}.", x - y);
}

static void Main()


{
ArithmeticDelegate Operation;

int num1, num2;

Console.Write("Enter first number: ");


num1 = Convert.ToInt32(Console.ReadLine());

Console.Write("Enter second number: ");

16
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

num2 = Convert.ToInt32(Console.ReadLine());

if (num1 < num2)


{
Operation = new ArithmeticDelegate(Add);
}
else
{
Operation = new ArithmeticDelegate(Subtract);
}

Operation(num1, num2);
}
}
}

Figure 1

Enter first number: 3


Enter second number: 5
Sum is 8
Enter first number: 5
variable = new DelegateName(MethodName);
Enter second number: 3 Difference is 2

Line 7 is the declaration of our delegate. We used the delegate keyword to indicate that it is a
delegate. The following is the return type of the method it will accept. The naming practice for a
delegate is the same with the method, we used Pascal Casing. We also append the word
"Delegate" for better recognition. We defined the parameters of the delegate which will also be
the same with the parameters of the method it will hold. The delegate that was declared in line 7
can only accept references to methods that has a void return type (no return type) and has two int
parameters.

After defining the delegate, we defined two methods with exactly the same signature as the
delegate because they are the methods the delegate will use later. Both methods don't return a
data and both accepts 2 int arguments. Inside the Main method, we declared a variable with the
type of the delegate we defined (line 21). This variable will hold a reference to a method that
matches the delegate's signature.The program asked for two values from the user. We entered an
if statement in line 31 wherein, if the value of the first number is less than the second number,
then we add them. If the value of the first number is greater than or equal to the number, we
subtract them. To assign a method to the delegate, we follow this syntax:

When assigning a delegate with a reference of the method, we use the new keyword followed by
the name of the delegate. Inside the parentheses, we indicate the name of the method the delegate
will refer to. A much simpler way is you can just assign the name of the method to the delegate
variable.

Operation = Add;
Operation = Subtract;

17
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

Back to our if statement, when the condition was true, we assigned the delegate the method
Add() and when the value was false, we assigned it with the Subtract() method. Line 40 executes
our delegate containing the reference of the assigned method. Executing the delegate also
executes the method it refers.

Events
Events are behaviors or happenings that occur when the program is running. Events are often
used in visual programming like windows and web forms. Some examples of events are clicking
a mouse, typing a text in a text box, changing the selected item in a list and many more. In
console applications, we can manually trigger events. You must subscribe to an event which
means adding event handlers or code blocks that will be executed when a particular event
happens. Multiple event handlers can subscribe to an event. When an event triggers, all the event
handlers will execute. The following example shows a simple usage of events.

using System;

namespace EventsDemo
{
public delegate void MessageHandler(string message);

public class Message


{
public event MessageHandler ShowMessage;

public void DisplayMessage(string message)


{
Console.WriteLine(message);
}

public void ExecuteEvent()


{
ShowMessage("Hello World!");
}
}

public class Program


{
public static void Main()
{
Message myMessage = new Message();
myMessage.ShowMessage += new
MessageHandler(myMessage.DisplayMessage);

myMessage.ExecuteEvent();
}
}
}

Example 1 - Events Demo

Hello World!

18
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

An event handler is a method that matches the signature of a delegate. For example, we defined a
delegate that has a void return type and one string parameter. This will be the signature of the
event handler method. After defining the delegate, we created a class that contains an event. To
define an event, the following syntax is used.

accessSpecifier event delegateType name;

The delegate type is the type of delegate to be used. The delegate is used to determine the
signature of the event handler that can subscribe to it. We created an event handler
DisplayMessage that has the same signature as the delegate. We also created another method that
will trigger the event manually. Events cannot be triggered outside the class the contains it.
Therefore, we will use this method to indirectly execute it. Inside our Main method, we created a
new instance of the Message class. On the next line, an event handler subscribed to our event.
Notice we used the += operator which means, we are adding the event handler to the list of event
handlers for an event. We created a new instance of MessageHandler delegate and inside it, we
passed the name of the event handler. We then call the ExecuteEvent method to trigger the event
and show the message. You will see the usefulness of events when you work with windows
forms or web forms in ASP.NET.

Anonymous Methods
Anonymous methods are methods that are not actually defined, therefore, only suited for one-
time use. Anonymous methods are targeted for delegates. The following shows the syntax of an
anonymous method.

delegate (parameters)
{
//Code for the anonymous method
};

For example, we can define a delegate, create an object of that delegate, and assign the
anonymous method to it.

using System;

namespace AnonymousMethodsDemo
{
public delegate void MessageDelegate(string message);

public class Program


{
public static void Main()
{
MessageDelegate ShowMessage = new MessageDelegate(
delegate(string message)
{
Console.WriteLine(message);
}
);

19
EXCEPTION HANDLING, EVENTS AND DELEGATES SKNSCOEK

ShowMessage("Hello World!");
}
}
}

Example 1 - Anonymous Method Demo

Hello World

Here, the delegate has a return type of void, and one string parameter. When we created our
delegate object, we passed an anonymous method to it. Notice that the anonymous method has
also a string parameter to match its delegate. The return type is automatically detected. If no
return statement was found, then it will have a return type of void. If your delegate has a return
type, then your anonymous method should have a return statement returning the proper type of
value. You can even simplify the code above like this:

MessageDelegate ShowMessage = delegate (string message)


{
Console.WriteLine (message);
};

Anonymous methods can also be used when subscribing to events.

myClass.myEvent += delegate (string message)


{
Console.WriteLine (message);
};

20

You might also like