Home

Exception Handling

 

Fundamentals of Exception Handling

 
 

Introduction

An error is something that is not part of the regular proceeding of a program. It manifests in various ways:

  • An unappropriate value provided where another type of value is expected: An example of a string provided to perform a multiplication. Here is an example:
    let diameter = "25.85" * 2.00
  • Mis-using the resources of the computer while a program is running
  • Not providing a resources where one is expected: For example, imagine you create a program that is supposed to get a picture in a folder on the computer but when the program runs, the picture is nowhere to be found
  • Intensely using resources on the computer but not disposing of those resources when the program ends
  • etc

To help you address such issues, the F# provides a series of mechanisms referred to as exception handling.

Trying an Exception

In all of the programs we have used so far, we assumed that everything would work alright. The reality is not always smooth. In some cases, something would go wrong. In a section of code where something could go wrong, you are asked to try evaluating that section. You start with the try keyword. The part is referred to as the try section and it contains one or more statements:

try
    statement(s)

This is where you process the regular statement.

Handling an Exception

If everything in the try section is alright, the program proceeds regularly. If something goes wrong, you can address the issue in the subsequent section that uses the with keyword. The formula to follow is:

try
    statement(s)
with
    handler

If the statement(s) in  the try section section is(are) alright, there is no error and the program proceeds as intended. If something goes wrong, you use the with section to indicate what to do. The with section uses a matching mechanism that has one or more cases. A case can use the following formula:

| case -> handling

There different ways you can use a case. One of the formulas to use is:

| identifier when condition

An identifier can be any letter or appropriate word. This is followed by the when keyword and a Boolean operation that evalues to true or false. This is followed by -> and what to do. If you don't have any specific way to take care of it, you can just add empty parentheses. Here is an example:

let mutable radius = 0.00
let mutable diameter = 0.00

try
    radius <- 36.84
with
    | wrongValue when (radius < 0.00) -> ()

diameter <- radius * 2.00

printfn "Circle Characteristics"
printfn "----------------------"
printfn "Radius:   %0.02f" radius
printfn "Diameter: %0.02f\n" diameter

As an alternative, you can display a message using the printf() or the printf() function. Here is an example:

let mutable radius = 0.00
let mutable diameter = 0.00

try
    radius <- 36.84
with
    | wrongValue when (radius < 0.00) -> printfn "Something went wrong when setting the value of the radius."

diameter <- radius * 2.00

assert (radius > 0.00)

printfn "Circle Characteristics"
printfn "----------------------"
printfn "Radius:   %0.02f" radius
printfn "Diameter: %0.02f\n" diameterwidth, height

Raising an Exception

Somethere in your program, you can detect where something would go wrong and create an exception. This is referred to as raising an exception. This is done using the raise() function. This function takes one argument that can be a string. To start, somewhere before addressing the exception, create a line of code using the exception keyword and the following formula:

exception function-name of parameter(s)

Start with the exception keyword followed by a name that will be used as a function. The name is followed by the of keyword followed by a type. We will come back to the issue.

In the section of code where a statement is evaluated, create a conditional statement that finds out if there is an error, in which case, call the raise() function to which you pass the function-name with, if necessary, its argument(s).

In the with section, call the  function-name as a case.

Concerning the  function-name:

  • If the function doesn't take an argument, use unit as its parameter. When raising the exception, pass the function with empty parentheses to the raise() function. Also, in the with section, call the  function-name with empty parentheses. Here is an example:
    let mutable radius = -28.15
    let mutable diameter = 0.00
    
    exception Msg of unit
    
    try
        if radius < 0.00 then
            raise (Msg())
    
        diameter <- radius * 2.00
    
        printfn "Circle Characteristics"
        printfn "----------------------"
        printfn "Radius:   %0.02f" radius
        printfn "Diameter: %0.02f\n" diameter
    with
        | Msg() -> printfn "Something went wrong."
  • If the function is taking one argument, specify its type as the parameter. When raising the exception, pass the function with the desired argument in the parentheses of function. Do the same in the with section. Here is an example:
    let mutable radius = -28.15
    let mutable diameter = 0.00
    
    exception Msg of string
    
    try
        if radius < 0.00 then
            raise (Msg("Negative values are not allowed."))
    
        diameter <- radius * 2.00
    
        printfn "Circle Characteristics"
        printfn "----------------------"
        printfn "Radius:   %0.02f" radius
        printfn "Diameter: %0.02f\n" diameter
    with
        | Msg(msg) -> printfn "%s" msg
  • If the function is taking more than one argument, enter their types separated by *. When raising the exception, pass the function to the raise() function. Make sure you pass the appropriate arguments to the function-name. Here is an example:
    let mutable radius = -28.15
    let mutable diameter = 0.00
    
    exception Msg of string * double
    
    try
        if radius < 0.00 then
            raise (Msg("The values you provided is not valid:", radius))
    
        diameter <- radius * 2.00
    
        printfn "Circle Characteristics"
        printfn "----------------------"
        printfn "Radius:   %0.02f" radius
        printfn "Diameter: %0.02f\n" diameter
    with
        | Msg(str, value) -> printfn "%s %f" str value

Raising Many Exceptions

In the try section, you can raise as many exceptions as you want. One option is to use various conditional statements to raise each exception. In this case, in the with section, create a match for each case. To start, you can create the appropriate exception function for each case. Here is one example:

let mutable radius = 0.00
let mutable strRadius = ""
let mutable diameter = 0.00

exception GetErrorType of unit
exception ShowMessage of string
exception IdentifyValue of string * double

try
    if strRadius = "" then
        raise (GetErrorType())
    elif strRadius = "0.00" then
        raise (ShowMessage("Null values are not allowed."))

    radius <- (double strRadius);

    if radius < 0.00 then
        raise (IdentifyValue("The values you provided is not valid:", radius))
    
    diameter <- radius * 2.00

    printfn "Circle Characteristics"
    printfn "----------------------"
    printfn "Radius:   %0.02f" radius
    printfn "Diameter: %0.02f\n" diameter
with
    | GetErrorType() -> printfn "The value you provided could not be evaluated."
    | ShowMessage(msg) -> printfn "%s" msg
    | IdentifyValue(str, value) -> printfn "%s %f" str value
    | _ -> ()

This would produce:

The value you provided could not be evaluated.
Press any key to continue . . .

Here is another version of the program:

let mutable radius = 0.00
let mutable strRadius = "0.00"
let mutable diameter = 0.00

exception GetErrorType of unit
exception ShowMessage of string
exception IdentifyValue of string * double

try
    if strRadius = "" then
        raise (GetErrorType())
    elif strRadius = "0.00" then
        raise (ShowMessage("Null values are not allowed."))

    radius <- (double strRadius);

    if radius < 0.00 then
        raise (IdentifyValue("The values you provided is not valid:", radius))
    
    diameter <- radius * 2.00

    printfn "Circle Characteristics"
    printfn "----------------------"
    printfn "Radius:   %0.02f" radius
    printfn "Diameter: %0.02f\n" diameter
with
    | GetErrorType() -> printfn "The value you provided could not be evaluated."
    | ShowMessage(msg) -> printfn "%s" msg
    | IdentifyValue(str, value) -> printfn "%s %f" str value
    | _ -> ()

This would produce:

Null values are not allowed.
Press any key to continue . . .

Here is another version of the program:

let mutable radius = 0.00
let mutable strRadius = "-35.85"
let mutable diameter = 0.00

exception GetErrorType of unit
exception ShowMessage of string
exception IdentifyValue of string * double

try
    if strRadius = "" then
        raise (GetErrorType())
    elif strRadius = "0.00" then
        raise (ShowMessage("Null values are not allowed."))

    radius <- (double strRadius);

    if radius < 0.00 then
        raise (IdentifyValue("The values you provided is not valid:", radius))
    
    diameter <- radius * 2.00

    printfn "Circle Characteristics"
    printfn "----------------------"
    printfn "Radius:   %0.02f" radius
    printfn "Diameter: %0.02f\n" diameter
with
    | GetErrorType() -> printfn "The value you provided could not be evaluated."
    | ShowMessage(msg) -> printfn "%s" msg
    | IdentifyValue(str, value) -> printfn "%s %f" str value
    | _ -> ()

This would produce:

The values you provided is not valid: -35.850000
Press any key to continue . . .

Here is one more version of the program:

let mutable radius = 0.00
let mutable strRadius = "35.85"
let mutable diameter = 0.00

exception GetErrorType of unit
exception ShowMessage of string
exception IdentifyValue of string * double

try
    if strRadius = "" then
        raise (GetErrorType())
    elif strRadius = "0.00" then
        raise (ShowMessage("Null values are not allowed."))

    radius <- (double strRadius);

    if radius < 0.00 then
        raise (IdentifyValue("The values you provided is not valid:", radius))
    
    diameter <- radius * 2.00

    printfn "Circle Characteristics"
    printfn "----------------------"
    printfn "Radius:   %0.02f" radius
    printfn "Diameter: %0.02f\n" diameter
with
    | GetErrorType() -> printfn "The value you provided could not be evaluated."
    | ShowMessage(msg) -> printfn "%s" msg
    | IdentifyValue(str, value) -> printfn "%s %f" str value
    | _ -> ()
type

This would produce:

Circle Characteristics
----------------------
Radius:   35.85
Diameter: 71.70

Press any key to continue . . .
 
   
 

Introduction to Exception Handling.NET

 

Introduction

Before the creating of the .NET Framework, many computer languages supported error or exception handling. These included C++ (with its try . . . catch(...) mechanism Visual Basic (with its On Error GoTo ... mechanism). This means that different languages used different techniques to address the issues related to errors. As those languages were imported to the .NET Framework, a unified technique was created. As a result, all languages of the .NET Framework must support exception handling and they practically use the same technique. To start, the .NET Framework includes various classes purposely made for exception handling.

The Exception Class

The most fundamental class that supports exception handling in the .NET Framework is called Exception. The class starts as follows:

type Exception =  
    class
    	abstract Message : string with get 
	override Message : string with get
	abstract Source : string with get, set 
	override Source : string with get, set
	abstract Data : IDictionary with get 
	override Data : IDictionary with get

	. . .

        interface ISerializable 
        interface _Exception 
    end

The Source property indicates what caused the error. The source can be anything. It depends on the type of error and what was going on. One of the most important characteristics of the Exception class is a property named Message. Like the name indicates, this property holds a description of the error that was produced from the action. In reality, both the source and the message from the Exception class are vague and could mean anything in different circumstances. In reality, when handling an exception in your program, you are free to indicate the source of the problem and to provide a message to display. To let you get more information about the error, the Exception class is equipped with a property named Data.

As mentioned above, the Exception class is vague, In mostly serves as a foundation for other classes. In fact, if you want to create a customized way to handle exceptions in your program, you can create a class that derives from Exeption.

Other Exception Classes

Instead of creating your own classes to handle exceptions, the .NET Framework provides many good classes from various situations. There are classes to address issues related to numbers, files, pictures, music, databases, etc. Some of the most regularly used classes are:

  • FormatException: This class is mostly used to validate numeric values that are provided in a program. If a value is valid, there is no exception. If there is a problem, such as if a string is provided where a number is expected, a FormatException is thrown
  • OverflowException: This class is used when the value provided to (or sorted in) a variable is larger than the memory area reserved for that value. An example is when a variable made to hold values from 0 to 255 is asked to store a value higher than that
  • ArgumentOutOfRangeException: This class is used if the compiler cannot convert a value from one type into another; that is, if the conversion fails, for any reason
  • DivideByZeroException: This class is used when there is an attempt to divide a number by 0

Resources Cleaning Up

 

Introduction

Some applications or parts of an application use a lot of resources. Examples of resources are hard drives, servers, communication media, etc. For example, an application may be asked to locate and use files on a computer. Another application may have to create a connection to a database on a remote computer. Sometimes, these types of operations require that an application monopolyze a resource and other applications must wait. For example, while one application opens and is using a file, other applications may be prevented from using or accessing that same file. When the first application has finished using the resource, it must stop using the resource so that other applications that need the same resource can use it. To help you take care of this, the F# language provides the finally keyword. The formula to use it is:

try
    statement(s)
finally
    body

You start with a try section exactly as we reviewed for the regular exception handling. In the finally section, create the code that would free the resources. Normally, the finally section is not used for regular exception handling; it is used to free resources that were used in a program. If you want to handle an exception, create the appropriate section in the try block. The formula to follow is:

try
    try
        statement(s)
    with
        exception-handler
finally
    body

Cleaning-Up After Using a Resource

The finally option requires a few steps that include an explicit free-up of resources after using them. The .NET Framework provides a better mechanism through an interface named IDispose. This interface has only one method named Dispose. This method takes no argument and returns nothing. The actual role of this method is to clean-up resources that an application was using. To use it, you must use a class that implements the IDispose interface. Of course, you can create your own class that implements this interface. In reality, all of the classes used in scenarios that consume resources, those classes implement the IDispose interface. When using one of those classes, to assist you with freeing the resources, the F# language provides the use keyword that is used as an operator. The formula to use it is as follows:

use variable = method

The use keyword is followed by a variable of of the class that will be used to consume resources. On the right side of =, call a method that will be used to actually consume the resources. When the method has finished doing its work, the compiler calls the IDispose.Dispose() method to free the resources that were used. An alternative is the one used by other languages. This technique uses the using() function. The formula to use it is:

using(Parameter) body

The using() function takes one argument that specicies what method will consume resources. The body of this function follows the = sign. If the body is short enough, it can be written on the same line that calls the using() function. If the body involves many lines, it should be created on the next line. In this case, the body should be indented. At the end of the body of the function, the compiler calls the IDispose.Dispose() method to free the resources that were used.

 
 
   
 

Previous Copyright © 2014-2015 FunctionX Next