Home > Articles

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Basic Error Handling with Exceptions

This section examines how to handle error reporting via a mechanism known as exception handling.

With exception handling, a method is able to pass information about an error to a calling method without using a return value or explicitly providing any parameters to do so. Listing 5.22 contains a slight modification to Listing 1.16, the HeyYou program from Chapter 1. Instead of requesting the last name of the user, it prompts for the user’s age.

Listing 5.22: Converting a string to an int

using System;

class ExceptionHandling
{
  static void Main()
  {
      string firstName;
      string ageText;
      int age;

      Console.WriteLine("Hey you!");

      Console.Write("Enter your first name: ");
      firstName = System.Console.ReadLine();

      Console.Write("Enter your age: ");                             
      ageText = Console.ReadLine();                                  
      age = int.Parse(ageText);                                      
                                                                     
      Console.WriteLine(                                             
          $"Hi { firstName }!  You are { age*12 } months old.");     
  }
}

Output 5.11 shows the results of Listing 5.22.

Output 5.11

Hey you!
Enter your first name: Inigo
Enter your age: 42
Hi Inigo!  You are 504 months old.

The return value from System.Console.ReadLine() is stored in a variable called ageText and is then passed to a method with the int data type, called Parse(). This method is responsible for taking a string value that represents a number and converting it to an int type.

Given the converted string, the final System.Console.WriteLine() statement will print the age in months by multiplying the age value by 12.

But what happens if the user does not enter a valid integer string? For example, what happens if the user enters “forty-two”? The Parse() method cannot handle such a conversion. It expects the user to enter a string that contains only digits. If the Parse() method is sent an invalid value, it needs some way to report this fact back to the caller.

Trapping Errors

To indicate to the calling method that the parameter is invalid, int.Parse() will throw an exception. Throwing an exception halts further execution in the current control flow and jumps into the first code block within the call stack that handles the exception.

Since you have not yet provided any such handling, the program reports the exception to the user as an unhandled exception. Assuming there is no registered debugger on the system, the error will appear on the console with a message such as that shown in Output 5.12.

Output 5.12

Hey you!
Enter your first name: Inigo
Enter your age: forty-two

Unhandled Exception: System.FormatException: Input string was
        not in a correct format.
    at System.Number.ParseInt32(String s, NumberStyles style,
        NumberFormatInfo info)
    at ExceptionHandling.Main()

Obviously, such an error is not particularly helpful. To fix this, it is necessary to provide a mechanism that handles the error, perhaps reporting a more meaningful error message back to the user.

This process is known as catching an exception. The syntax is demonstrated in Listing 5.23, and the output appears in Output 5.13.

Listing 5.23: Catching an Exception

using System;

class ExceptionHandling
{
  static int Main()
  {
      string firstName;
      string ageText;
      int age;
      int result = 0;

      Console.Write("Enter your first name: ");
      firstName = Console.ReadLine();

      Console.Write("Enter your age: ");
      ageText = Console.ReadLine();

      try
      {
          age = int.Parse(ageText);
          Console.WriteLine(
              $"Hi { firstName }! You are { age*12 } months old.");
      }
      catch (FormatException )
      {
          Console.WriteLine(
              $"The age entered, { ageText }, is not valid.");
          result = 1;
      }
      catch(Exception exception)
      {
          Console.WriteLine(
              $"Unexpected error:  { exception.Message }");
          result = 1;
      }
      finally
      {
          Console.WriteLine($"Goodbye { firstName }");
      }

      return result;
  }
}

Output 5.13

Enter your first name: Inigo
Enter your age: forty-two
The age entered, forty-two, is not valid.
Goodbye Inigo

To begin, surround the code that could potentially throw an exception (age = int.Parse()) with a try block. This block begins with the try keyword. It indicates to the compiler that the developer is aware of the possibility that the code within the block might throw an exception, and if it does, one of the catch blocks will attempt to handle the exception.

One or more catch blocks (or the finally block) must appear immediately following a try block. The catch block header (see the Advanced Topic titled “General Catch” later in this chapter) optionally allows you to specify the data type of the exception, and as long as the data type matches the exception type, the catch block will execute. If, however, there is no appropriate catch block, the exception will fall through and go unhandled as though there were no exception handling. The resultant control flow appears in Figure 5.1.

FIGURE 5.1

Figure 5.1: Exception-Handling Control Flow

For example, assume the user enters “forty-two” for the age in the previous example. In this case, int.Parse() will throw an exception of type System.FormatException, and control will jump to the set of catch blocks. (System.FormatException indicates that the string was not of the correct format to be parsed appropriately.) Since the first catch block matches the type of exception that int.Parse() threw, the code inside this block will execute. If a statement within the try block threw a different exception, the second catch block would execute because all exceptions are of type System.Exception.

If there were no System.FormatException catch block, the System.Exception catch block would execute even though int.Parse throws a System.FormatException. This is because a System.FormatException is also of type System.Exception. (System.FormatException is a more specific implementation of the generic exception, System.Exception.)

The order in which you handle exceptions is significant. Catch blocks must appear from most specific to least specific. The System.Exception data type is least specific, so it appears last. System.FormatException appears first because it is the most specific exception that Listing 5.23 handles.

Regardless of whether control leaves the try block normally or because the code in the try block throws an exception, the finally block of code will execute after control leaves the try-protected region. The purpose of the finally block is to provide a location to place code that will execute regardless of how the try/catch blocks exit—with or without an exception. Finally blocks are useful for cleaning up resources regardless of whether an exception is thrown. In fact, it is possible to have a try block with a finally block and no catch block. The finally block executes regardless of whether the try block throws an exception or whether a catch block is even written to handle the exception. Listing 5.24 demonstrates the try/finally block, and Output 5.14 shows the results.

Listing 5.24: Finally Block without a Catch Block

using System;

class ExceptionHandling
{
  static int Main()
  {
      string firstName;
      string ageText;
      int age;
      int result = 0;

      Console.Write("Enter your first name: ");
      firstName = Console.ReadLine();

      Console.Write("Enter your age: ");
      ageText = Console.ReadLine();

      try
      {
          age = int.Parse(ageText);
          Console.WriteLine(
              $"Hi { firstName }! You are { age*12 } months old.");
      }
      finally
      {
          Console.WriteLine($"Goodbye { firstName }");
      }

      return result;
  }
}

Output 5.14

Enter your first name: Inigo
Enter your age: forty-two

Unhandled Exception: System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
   at ExceptionHandling.Main()
Goodbye Inigo

The attentive reader will have noticed something interesting here: The runtime first reported the unhandled exception and then ran the finally block. What explains this unusual behavior?

First, the behavior is legal because when an exception is unhandled, the behavior of the runtime is implementation defined; any behavior is legal! The runtime chooses this particular behavior because it knows before it chooses to run the finally block that the exception will be unhandled; the runtime has already examined all of the activation frames on the call stack and determined that none of them is associated with a catch block that matches the thrown exception.

As soon as the runtime determines that the exception will be unhandled, it checks whether a debugger is installed on the machine, because you might be the software developer who is analyzing this failure. If a debugger is present, it offers the user the chance to attach the debugger to the process before the finally block runs. If there is no debugger installed or if the user declines to debug the problem, the default behavior is to print the unhandled exception to the console and then see if there are any finally blocks that could run. Due to the “implementation-defined” nature of the situation, the runtime is not required to run finally blocks in this situation; an implementation may choose to do so or not.

A method could throw many exception types. Table 5.2 lists some of the more common ones within the framework.

Table 5.2: Common Exception Types

Exception Type

Description

System.Exception

The “base” exception from which all other exceptions derive.

System.ArgumentException

Indicates that one of the arguments passed into the method is invalid.

System.ArgumentNullException

Indicates that a particular argument is null and that this is not a valid value for that parameter.

System.ApplicationException

To be avoided. The original idea was that you might want to have one kind of handling for system exceptions and another for application exceptions, which, although plausible, doesn’t actually work well in the real world.

System.FormatException

Indicates that the string format is not valid for conversion.

System.IndexOutOfRangeException

Indicates that an attempt was made to access an array or other collection element that does not exist.

System.InvalidCastException

Indicates that an attempt to convert from one data type to another was not a valid conversion.

System.InvalidOperationException

Indicates that an unexpected scenario has occurred such that the application is no longer in a valid state of operation.

System.NotImplementedException

Indicates that although the method signature exists, it has not been fully implemented.

System.NullReferenceException

Thrown when code tries to find the object referred to by a reference that is null.

System.ArithmeticException

Indicates an invalid math operation, not including divide by zero.

System.ArrayTypeMismatchException

Occurs when attempting to store an element of the wrong type into an array.

System.StackOverflowException

Indicates an unexpectedly deep recursion.

Reporting Errors Using a throw Statement

C# allows developers to throw exceptions from their code, as demonstrated in Listing 5.26 and Output 5.15.

Listing 5.26: Throwing an Exception

using System;
public class ThrowingExceptions
{
  public static void Main()
  {
      try
      {
          Console.WriteLine("Begin executing");
          Console.WriteLine("Throw exception");
             throw new Exception("Arbitrary exception");
          Console.WriteLine("End executing");
      }
      catch(FormatException exception)
      {
          Console.WriteLine(
              "A FormateException was thrown");
      }
      catch(Exception exception)
      {
          Console.WriteLine(
              $"Unexpected error: { exception.Message }");
      }
      catch
      {
          Console.WriteLine("Unexpected error!");
      }

      Console.WriteLine(
          "Shutting down...");
  }
}

Output 5.15

Begin executing
Throw exception...
Unexpected error:  Arbitrary exception
Shutting down...

As the arrows in Listing 5.26 depict, throwing an exception causes execution to jump from where the exception is thrown into the first catch block within the stack that is compatible with the thrown exception type.6 In this case, the second catch block handles the exception and writes out an error message. In Listing 5.26, there is no finally block, so execution falls through to the System.Console.WriteLine() statement following the try/catch block.

To throw an exception, it is necessary to have an instance of an exception. Listing 5.26 creates an instance using the keyword new followed by the type of the exception. Most exception types allow a message to be generated as part of throwing the exception, so that when the exception occurs, the message can be retrieved.

Sometimes a catch block will trap an exception but be unable to handle it appropriately or fully. In these circumstances, a catch block can rethrow the exception using the throw statement without specifying any exception, as shown in Listing 5.27.

Listing 5.27: Rethrowing an Exception

...
      catch(Exception exception)
      {
          Console.WriteLine(
              $@"Rethrowing unexpected error:  {
                  exception.Message }");
          throw;
      }
...

In Listing 5.27, the throw statement is “empty” rather than specifying that the exception referred to by the exception variable is to be thrown. This illustrates a subtle difference: throw; preserves the call stack information in the exception, whereas throw exception; replaces that information with the current call stack information. For debugging purposes, it is usually better to know the original call stack.

Avoid Using Exception Handling to Deal with Expected Situations

Developers should make an effort to avoid throwing exceptions for expected conditions or normal control flow. For example, developers should not expect users to enter valid text when specifying their age.7 Therefore, instead of relying on an exception to validate data entered by the user, developers should provide a means of checking the data before attempting the conversion. (Better yet, they should prevent the user from entering invalid data in the first place.) Exceptions are designed specifically for tracking exceptional, unexpected, and potentially fatal situations. Using them for an unintended purpose such as expected situations will cause your code to be hard to read, understand, and maintain.

Additionally, like most languages, C# incurs a slight performance hit when throwing an exception—taking microseconds compared to the nanoseconds most operations take. This delay is generally not noticeable in human time—except when the exception goes unhandled. For example, when Listing 5.22 is executed and the user enters an invalid age, the exception is unhandled and there is a noticeable delay while the runtime searches the environment to see whether there is a debugger to load. Fortunately, slow performance when a program is shutting down isn’t generally a factor to be concerned with.

End 2.0

  • + Share This
  • 🔖 Save To Your Account

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020