Chapter 6. Handling Errors and Exceptions

Every application might encounter errors during its execution, even when you spend several nights on testing the application and all the possible execution scenarios. Runtime errors are especially unpredictable because the application execution is conditioned by user actions. Because of this, error handling is a fundamental practice that, as a developer, you need to know in depth. In this chapter you learn how the .NET Framework enables handling errors and how to get information to solve problems deriving such errors. In other words, you learn about .NET exceptions.

Introducing Exceptions

In development environments other than .NET, programming languages can handle errors occurring during the application execution in different ways. For example, the Windows native APIs return a 32-bit HRESULT number in case an error occurs. Visual Basic 6 uses the On Error statements, whereas other languages have their own error-handling infrastructures. As you can imagine, such differences cannot be allowed in the .NET Framework because all languages rely on the Common Language Runtime (CLR), so all of them must intercept and handle errors the same way. With that said, the .NET Framework identifies errors as exceptions. An exception is an instance of the System.Exception class (or of a class derived from it) and provides deep information on the error that occurred. Such an approach provides a unified way for intercepting and handling errors. Exceptions occur at runtime as well as during the application execution. Exceptions can be thrown by the Visual Basic compiler at compile time or by the background compiler when typing code. Errors occurring when designing the user interface of an application or when working within the Visual Studio 2012 IDE are typically called exceptions. This is because such tasks (and most of the Visual Studio IDE) are powered by the .NET Framework. In Chapter 2, “Getting Started with the Visual Studio 2012 IDE,” we introduced the Visual Studio debugger and saw how it can be used for analyzing error messages provided by exceptions (see the “About Runtime Errors” section in Chapter 2). In that case, we did not implement any error-handling routine because we were just introducing the debugging features of Visual Studio during the development process. But what if an error occurs at runtime when the application has been deployed to your customer without implementing appropriate error-handling code? Imagine an application that attempts to read a file that does not exist and in which the developer did not implement error checks. Figure 6.1 shows an example of what could happen and what should never happen in a real application.

Image

Figure 6.1. Without handling exceptions, solving errors is difficult.

As you can see from Figure 6.1, in case of an error the application stops its execution and no resume is possible. Moreover, identifying the type of error occurred can also be difficult. Because of this, as a developer it is your responsibility to implement code for intercepting exceptions and take the best actions possible to solve the problem—while keeping your users’ choices in mind. The best way to understand exceptions is to begin to write some code that causes an error, so this is what we do in the next section.

Handling Exceptions

Visual Basic 2012 enables deep control over exceptions. With regard to this, an important concept is that you not only can check for occurring exceptions, but can also conditionally manage solutions to exceptions and raise exceptions when needed. In this section we discuss all these topics, providing information on how you can intercept and manage exceptions in your application.

Tips for Visual Basic 6 Migration

One of the (very few) commonalities between Visual Basic 6 and Visual Basic .NET and higher is the syntax approach. This should help a little more in migrating from Visual Basic 6 to 2012. Although Visual Basic 2012 (more precisely, VB.NET from 2002 to 2012) still enables the usage of the On Error Goto and On Error Resume statements, when developing .NET applications with Visual Basic, you should never use such statements for two reasons. First, exceptions are the only thing that enables interoperation with other .NET languages such as Visual C# and Visual F#. This is fundamental when developing class libraries or components that could potentially be reused from other languages than Visual Basic. The second reason is that the old-fashioned way for handling errors is not as efficient as handling .NET exceptions. If you decided to migrate, you should completely forget On Error and exclusively use exceptions.

System.Exception, Naming Conventions, and Specialization

System.Exception is the most general exception and can represent all kinds of errors occurring in applications. It is also the base class for derived exceptions, which are specific to situations you encounter. For example, the System.IOException derives from System.Exception, is thrown when the application encounters input/output errors when accessing the disk, and can be handled only for this particular situation. On the other hand, System.Exception can handle not only this situation, but also any other occurring errors. You can think of System.Exception as the root in the exceptions hierarchy. We explain later in code the hierarchy of exception handling. Classes representing exceptions always terminate with the word Exception. You encounter exceptions such as FileNotFoundException, IndexOutOfRangeException, FormatException, and so on. This is not mandatory, but a recommended naming convention. Generally, .NET built-in exceptions inherit from System.Exception, but because they are reference types, you find several exceptions inheriting from a derived exception.

Handling Exceptions with Try..Catch..Finally Blocks

You perform exception handling by writing a Try..Catch..Finally code block. The logic is that you say to the compiler, “Try to execute the code; if you encounter an exception, take the specified actions; whenever the code execution succeeds or it fails due to an exception, execute the final code.” The most basic code for controlling the execution flow regarding exceptions is the following:

Try
    'Code to be executed
Catch ex As Exception
    'Code to handle the exception
End Try

IntelliSense does a great job here. When you type the Try keyword and then press Enter, it automatically adds the Catch statement and the End Try terminator. The ex variable gets the instance of the System.Exception that is caught, and that provides important information so that you can best handle the exception. To see what happens, consider the following code snippet:

Try
    Dim myArray() As String = {"1", "2", "3", "4"}

    Console.WriteLine(myArray(4))
Catch ex As Exception
    Console.WriteLine(ex.Message)
End Try

Here we have an array of strings in which the upper range of the array is 3. The Try block tries to execute code that attempts writing the content of the fourth index to the Console window. Unfortunately, such an index does not exist, but because the code is formally legal, it will be correctly compiled. When the application runs and the runtime encounters this situation, it throws an exception to communicate the error occurrence. So the Catch statement intercepts the exception and enables deciding which actions must be taken. In our example, the action to handle the exception is to write the complete error message of the exception. If the code within Try succeeds, the execution passes to the first code after the End Try terminator. In our example, the control transfers to the Catch block that contains code that writes to the Console window the actual error message that looks like the following:

Index was outside the bounds of the array

The runtime never throws a generic System.Exception exception. There are specific exceptions for the most common scenarios (and it is worth mentioning that you can create custom exceptions as discussed in Chapter 12, “Inheritance”) that are helpful to identify what happened instead of inspecting a generic exception. Continuing our example, the runtime throws an IndexOutOfRangeException that means the code attempted to access and index greater or smaller than allowed. Based on these considerations, the code could be rewritten as follows:

Try
    Dim myArray() As String = {"1", "2", "3", "4"}

    Console.WriteLine(myArray(4))

Catch ex As IndexOutOfRangeException
    Console.WriteLine("There is a problem: probably you are "
            & Environment.NewLine &
            " attempting to access an index that does not exist")
Catch ex As Exception
    Console.WriteLine(ex.Message)

End Try

As you can see, the most specific exception needs to be caught before the most generic one. This is quite obvious because, if you first catch the System.Exception, all other exceptions will be ignored. Intercepting specific exceptions can also be useful because you can both communicate the user detailed information and decide which actions must be taken to solve the problem. Anyway, always adding a Catch block for a generic System.Exception is a best practice. This enables you to provide a general error-handling code in case exceptions you do not specifically intercept will occur. You could also need to perform some actions independently from the result of your code execution. The Finally statement enables executing some code either if the Try succeeds or if it fails, passing control to Catch. For example, you might want to clean up resources used by the array:

Dim myArray() As String = {"1", "2", "3", "4"}

Try
    Console.WriteLine(myArray(4))

Catch ex As IndexOutOfRangeException
    Console.WriteLine("There is a problem: probably you are "
            & Environment.NewLine &
            " attempting to access an index that does not exists")
Catch ex As Exception
    Console.WriteLine(ex.Message)
Finally
    myArray = Nothing
End Try

Notice how objects referred within the Finally block must be declared outside the Try..End Try block because of visibility. The code within Finally will be executed no matter what the result of the Try block will be. This is important; for example, think about files. You might open a file and then try to perform some actions on the file that for any reason can fail. In this situation you would need to close the file, and Finally ensures you can do that if the file access is successful and even if it fails (throwing an exception). An example of this scenario is represented in Listing 6.1.

Listing 6.1. Use Finally to Ensure Resources Are Freed Up and Unlocked


Imports System.IO

Module Module1

    Sub Main()

        Console.WriteLine("Specify a file name:")
        Dim fileName As String = Console.ReadLine

        Dim myFile As FileStream = Nothing

        Try
            myFile = New FileStream(fileName, FileMode.Open)
            'Seek a specific position in the file.
            'Just for example
            myFile.Seek(5, SeekOrigin.Begin)
        Catch ex As FileNotFoundException
            Console.WriteLine("File not found.")
        Catch ex As Exception
            Console.WriteLine("An unidentified error occurred.")
        Finally
            If myFile IsNot Nothing Then
                myFile.Close()
            End If
        End Try

        Console.ReadLine()
    End Sub
End Module


The code in Listing 6.1 is quite simple. First, it asks the user to specify a filename to be accessed. Accessing files is accomplished with a FileStream object. Notice that the myFile object is declared outside the Try..End Try block so that it can be visible within Finally. Moreover, its value is set to Nothing so that it has a default value, although it’s null. If we did not assign this default value, myFile would just be declared but not yet assigned, so the Visual Basic compiler would throw a warning message. By the way, setting the default value to Nothing will not prevent a NullReferenceException at runtime unless the variable gets a value. The Try block attempts accessing the specified file. The FileStream.Seek method here is just used as an example needed to perform an operation on the file. When accessing files, there could be different problems, resulting in various kinds of exceptions. In our example, if the specified file does not exist, a FileNotFoundException is thrown by the runtime and the Catch block takes control over the execution. Within the block, we just communicate that the specified file was not found. If, instead, the file exists, the code performs a search. In both cases, the Finally block ensures that the file gets closed, independently on what happened before. This is fundamental because if you leave a file open, other problems would occur.

Exceptions Hierarchy

In Listing 6.1, we saw how we can catch a specific exception, such as FileNotFoundException, and then the general System.Exception. By the way, FileNotFoundException does not directly derive from System.Exception; instead, it derives from System.IO.IOException, which is related to general input/output problems. Although you are not obliged to also catch an IOException when working with files, adding it could be a good practice because you can separate error handling for disk input/output errors from other errors. In such situations, the rule is that you have to catch exceptions from the most specific to the most general. Continuing the previous example, Listing 6.2 shows how you can implement exceptions hierarchy.

Listing 6.2. Understanding Exceptions Hierarchy


Imports System.IO

Module Module1

    Sub Main()

        Console.WriteLine("Specify a file name:")
        Dim fileName As String = Console.ReadLine

        Dim myFile As FileStream = Nothing

        Try
            myFile = New FileStream(fileName, FileMode.Open)
            'Seek a specific position in the file.
            'Just for example
            myFile.Seek(5, SeekOrigin.Begin)
        Catch ex As FileNotFoundException
            Console.WriteLine("File not found.")
        Catch ex As IOException
            Console.WriteLine("A general input/output error occurred")
        Catch ex As Exception
            Console.WriteLine("An unidentified error occurred.")
        Finally
            If myFile IsNot Nothing Then
                myFile.Close()
            End If
        End Try

        Console.ReadLine()
    End Sub
End Module


FileNotFoundException is the most specific exception, so it must be caught first. It derives from IOException, which is intercepted second. System.Exception is instead the base class for all exceptions and therefore must be caught last.

System.Exception Properties

The System.Exception class exposes some properties that are useful for investigating exceptions and then understanding what the real problem is. Particularly when you catch specialized exceptions, it could happen that such exception is just the last ring of a chain and that the problem causing the exception itself derives from other problems. System.Exception’s properties enable a better navigation of exceptions. Table 6.1 lists the available properties.

Table 6.1. System.Exception’s Properties

Image

Listing 6.3 shows how you can retrieve deep information on the exception. In the example, information is retrieved for the System.IO.FileNotFoundException, but you can use these properties for any exception you like.

Listing 6.3. Investigating Exceptions Properties


Imports System.IO

Module Module1

    Sub Main()
        Console.WriteLine("Specify a file name:")
        Dim fileName As String = Console.ReadLine

        Dim myFile As FileStream = Nothing

        Try
            myFile = New FileStream(fileName, FileMode.Open)
            'Seek a specific position in the file.
            'Just for example
            myFile.Seek(5, SeekOrigin.Begin)
        Catch ex As FileNotFoundException
            Console.WriteLine()
            Console.WriteLine("Error message: " & ex.Message & Environment.NewLine)
            Console.WriteLine("Object causing the exception: "
                              & ex.Source & Environment.NewLine)
            Console.WriteLine("Method where the exception is thrown; "
                              & ex.TargetSite.ToString & Environment.NewLine)
            Console.WriteLine("Call stack:" & ex.StackTrace & Environment.NewLine)

            Console.WriteLine("Other useful info:")
            For Each k As KeyValuePair(Of String, String) In ex.Data
                Console.WriteLine(k.Key & " " & k.Value)
            Next

        Catch ex As IOException
            Console.WriteLine("A general input/output error occurred")
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        Finally
            If myFile IsNot Nothing Then
                myFile.Close()
            End If
        End Try

        Console.ReadLine()
    End Sub
End Module


If you run this code and specify a filename that does not exist, you can retrieve a lot of useful information. Figure 6.2 shows the result of the code.

Image

Figure 6.2. Getting information on the exception.

As you can see from Figure 6.2, the Message property contains the full error message. In production environments, this can be a useful way to provide customers a user-friendly error message. The Source property shows the application or object causing the exception. In our example, it retrieves Mscorlib, meaning that the exception was thrown by the CLR. The Target property retrieves the method in which the exception was thrown. It is worth mentioning that this property retrieves the native method that caused the error, meaning that the method was invoked by the CLR. This is clearer if we take a look at the content of the Stack property. We can see the hierarchy of method calls in descending order: the .ctor method is the constructor of the FileStream class, invoked within the Main method; the next method, named Init, attempts to initialize a FileStream and is invoked behind the scenes by the CLR. Init then invokes the native WinIOError function because accessing the file was unsuccessful. Analyzing such properties can be useful to understand what happened. Because there is no other useful information, iterating the Data property produced no result. By the way, if you just need to report a detailed message about the exception, you can collect most of the properties’ content by invoking the ToString method of the Exception class. For example, you could replace the entire Catch ex as FileNotFoundException block as follows:

Catch ex As FileNotFoundException
    Console.WriteLine(ex.ToString)

This edit produces the result shown in Figure 6.3. As you can see, the result is a little different from the previous one. We can find a lot of useful information, such as the name of the exception, complete error message, filename, Call Stack hierarchy, and line of code that caused the exception.

Image

Figure 6.3. Invoking the Exception.ToString method offers detailed information.

Typically, you use ToString just to show information, whereas you use properties to analyze exception information.


Note About Performance

Catching exceptions is necessary but is also performance consuming. Sometimes you could simply check the value of an object instead of catching exceptions. For example, you could check with an If..Then statement whether an object is null instead of catching a NullReference exception (when possible, of course). Exceptions are best left to handling “exceptional” situations that occur in code, so you should limit the use of them when possible.


F Catching Exception

You can also ignore exceptions by simply not writing anything inside a Catch block. For instance, the following code catches a FileNotFoundException and prevents an undesired stop in the application execution but takes no action:

Try
    testFile = New FileStream(FileName, FileMode.Open)

Catch ex As FileNotFoundException
End Try

Nested Try..Catch..Finally Blocks

You also have the ability to nest Try..Catch..Finally code blocks. Nested blocks are useful when you have to try the execution of code onto the result of another Try..Catch block. Consider the following code that shows the creation time of all files in a given directory:

Try
    Dim allFiles As String() =
        Directory.GetFiles("C:TestDirectory")

    Try
        For Each f As String In allFiles
            Console.WriteLine(File.GetCreationTime(f).ToString())
        Next

    Catch ex As IOException

    Catch ex As Exception

    End Try

Catch ex As DirectoryNotFoundException

Catch ex As Exception
End Try

The first Try..Catch attempts reading the list of files from a specified directory. Because you can encounter directory errors, a DirectoryNotFoundException is caught. The result (being an array of String) is then iterated within a nested Try..Catch block. This is because the code is now working on files and then specific errors might be encountered.

Exit Try Statements

You can exit from within a Try..Catch..Finally block at any moment using an Exit Try statement. If a Finally block exists, Exit Try pulls the execution into Finally that otherwise resumes the execution at the first line of code after End Try. The following code snippet shows an example:

Try
    'Your code goes here
    Exit Try
    'The following line will not be considered
    Console.WriteLine("End of Try block")

Catch ex As Exception

End Try
'Resume the execution here

The Throw Keyword

In some situations you need to programmatically throw exceptions or you catch exceptions but do not want to handle them in the Catch block that intercepted exceptions. Programmatically throwing exceptions can be accomplished via the Throw keyword. For instance, the following line of code throws an ArgumentNullException that usually occurs when a method receives a null argument:

Throw New ArgumentNullException

You often need to manually throw exceptions when they should be handled by another portion of code, also in another application. A typical example is when you develop class libraries. A class library must be as abstract as possible, so you cannot decide which actions to take when an exception is caught; this is the responsibility of the developer who creates the application that references your class library. If both developers are the same person, this remains a best practice because you should always separate the logic of class implementations from the client logic (such as the user interface). To provide a clearer example, a class library cannot show a graphical message box or a text message into the Console window. It instead needs to send the code the exception caught to the caller, and this is accomplished via the Throw keyword. We can provide a code example. Visual Studio 2012 creates a new blank solution and then adds a new Class Library project that you could name, for example, TestThrow. Listing 6.4 shows the content of the class.

Listing 6.4. Throwing Back Exceptions to Caller Code


Imports System.IO

Public Class TestThrow

    Public Sub TestAccessFile(ByVal FileName As String)
        If String.IsNullOrEmpty(FileName) Then
            Throw New ArgumentNullException("FileName",
                      "You passed an invalid file name")
        End If
        Dim testFile As FileStream = Nothing

        Try
            testFile = New FileStream(FileName, FileMode.Open)

        Catch ex As FileNotFoundException
            Throw New FileNotFoundException("The supplied file name was not found")
        Catch ex As Exception
            Throw
        Finally
            If testFile IsNot Nothing Then
                testFile.Close()
            End If
        End Try
    End Function
End Class


The TestThrow class’s purpose is just attempting to access a file. This is accomplished by invoking the TestAccess method that receives a FileName argument of type String. The first check is on the argument: If it is null, the code throws back to the caller an ArgumentNullException that provides the argument name and a description. In this scenario, the method catches but does not handle the exception. This is thrown back to the caller code, which is responsible to handle the exception (for example, asking the user to specify a valid filename). The second check is on the file access. The Try..Catch..Finally block implements code that tries to access the specified file and, if the file is not found, it throws back the FileNotFoundException describing what happened. In this way the caller code is responsible for handling the exception; for example, asking the user to specify another filename. Also notice how a generic System.Exception is caught and thrown back to the caller by simply invoking the Throw statement without arguments. This enables the method to throw back to the caller the complete exception information.


Rethrowing Exceptions

Throwing back an exception to the caller code is also known as the rethrow technique.


Now we can create an application that can reference the TestThrow class library and handle exceptions on the client side. Add to the solution a new Visual Basic project for the Console and add a reference to the TestThrow class library by selecting Project, Add Reference. Adding a reference to another assembly enables you to use types exposed publicly from such an assembly. Finally, write the code shown in Listing 6.5.

Listing 6.5. Handling Exceptions on the Client Side


Imports System.IO

Module Module1

    Sub Main()

        Console.WriteLine("Specify the file name:")
        Dim name As String = Console.ReadLine
        Dim throwTest As New TestThrow.TestThrow

        Try
            throwTest.TestAccessFile(name)
        Catch ex As ArgumentNullException
            Console.WriteLine(ex.ToString & Environment.NewLine &
                   "You passed an invalid argument")
        Catch ex As FileNotFoundException
            Console.WriteLine(ex.Message)
        Catch ex As Exception
            Console.WriteLine(ex.ToString)
        Finally
            Console.ReadLine()
        End Try
    End Sub
End Module


The code in Listing 6.5 first asks the user to specify a filename to access. If you press Enter without specifying any filename, the TestAccessFile method is invoked passing an empty string, so the method throws an ArgumentNullException, as shown in Figure 6.4.

Image

Figure 6.4. Passing an empty string as an argument causes an ArgumentNullException.

If this situation happened inside a Windows application, you could provide a MessageBox showing the error message. With this approach, you maintain logics separately. If you instead specify a filename that does not exist, the caller code needs to handle the FileNotFoundException; the result is shown in Figure 6.5.

Image

Figure 6.5. The caller code handles the FileNotFoundException and shows a user-friendly message.

The caller code writes the content of the ex.Message property to the Console window, which is populated with the error message provided by the Thrown statement from the class library. If any other kinds of exceptions occur, a generic System.Exception is handled and its content is shown to the user.


Catching Task-Specific Exceptions

Depending on which tasks you perform within a Try..Catch block, always catch exceptions specific for those tasks. For example, if your code works with files, don’t limit it to catch a FileNotFoundException. Consider file-related and disk-related exceptions, too. To get a listing of the exceptions that a class was designed to throw, you can read the MSDN documentation related to that class. The documentation describes in detail which exceptions are related to the desired object.


Performance Overhead

Invoking the Throw keyword causes the runtime to search through all the code hierarchy until it finds the caller code that can handle the exception. Continuing our previous example, the Console application is not necessarily the first caller code. There could be another class library that could rethrow the exception to another class that could then rethrow the exception to the main caller code. Obviously, going through the callers’ hierarchy is a task that could cause performance overhead, so you should take care about how many callers are in your code to reduce the overhead. In the end, if the caller code is missing a Catch block, the result will be the same as what is shown in Figure 6.1, which should always be avoided.

The When Keyword

Sometimes you might need to catch an exception only when a particular condition exists. You can conditionally control the exception handling using the When keyword, which enables taking specific actions when a particular condition is evaluated as True. Continuing the example of the previous paragraph, the TestThrow class throws an ArgumentNullException in two different situations (although similar). The first one is if the string passed to the TestAccessFile method is empty; the second one is if the string passed to the method is a null value (Nothing). So it could be useful to decide which actions to take depending on what actually caused the exception. According to this, we could rewrite the code shown in Listing 6.5 as what is shown in Listing 6.6.

Listing 6.6. Conditional Exception Handling with the When Keyword


Imports System.IO

Module Module1

    Sub Main()

        Console.WriteLine("Specify the file name:")
        Dim name As String = Console.ReadLine

        Dim throwTest As New TestThrow.TestThrow

        Try
            throwTest.TestAccessFile(name)

        Catch ex As ArgumentNullException When name Is Nothing
            Console.WriteLine("You provided a null parameter")
        Catch ex As ArgumentNullException When name Is String.Empty
            Console.WriteLine("You provided an empty string")
        Catch ex As FileNotFoundException
            Console.WriteLine(ex.Message)
        Catch ex As Exception
            Console.WriteLine(ex.ToString)
        Finally
            Console.ReadLine()
        End Try
    End Sub
End Module


As you can see from Listing 6.6, by using the When keyword you can conditionally handle exceptions depending if the expression on the right is evaluated as True. In this case, when handling the ArgumentNullException, the condition is evaluating the name variable. If name is equal to Nothing, the exception is handled showing a message saying that the string is null. If name is instead an empty string (which is different from a null string), another kind of message is shown. When can be applied only to Catch statements and works only with expressions. Of course, you can use the When keyword also with value types; for example, you might have a counter that you increment during the execution of your code and, in case of exceptions, you might decide which actions to take based on the value of your counter. The following code demonstrates:

Dim oneValue As Integer = 0
Try
    'perform some operations
    'on oneValue
Catch ex As Exception When oneValue = 1

Catch ex As Exception When oneValue = 2

Catch ex As Exception When oneValue = 3

End Try

Catching Exceptions Without a Variable

You do not always need to specify a variable in the Catch block. This can be the case in which you want to take the same action independently from the exception that occurred. For example, consider the following code:

Try
    Dim result As String =
        My.Computer.FileSystem.ReadAllText("C:MyFile.txt")

Catch ex As Exception
    Console.WriteLine("A general error occurred")
End Try

The ex variable is not being used and no specific exceptions are handled. So the preceding code can be rewritten as follows, without the ex variable:

Try
    Dim result As String =
        My.Computer.FileSystem.ReadAllText("C:MyFile.txt")

Catch
    Console.WriteLine("A general error occurred")
End Try

Whichever exception occurs, the code shows the specified message. This also works with regard to the rethrow technique. The following code simply rethrows the proper exception to the caller:

Try
    Dim result As String =
        My.Computer.FileSystem.ReadAllText("C:MyFile.txt")

Catch
    Throw
End Try

Summary

Managing errors is something that every developer needs to take into consideration. The .NET Framework provides a unified system for managing errors, which is the exception handling. System.Exception is the root class in the exceptions hierarchy, and exceptions are handled using a Try..Catch..Finally block. You can create nested Try..Catch..Finally blocks and check exceptions conditionally using the When keyword. In the end, you can programmatically generate exceptions using the Throw keyword that is particularly useful in class libraries development. This chapter provided a high-level overview of exceptions; now you can decide which kinds of exceptions you need to handle for your code and how to take actions to solve errors.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.88.62