|
Practical Learning: Using Exceptions
|
|
- Start Microsoft Visual C# and create a new
Console Application named RealEstate3
- To create a new class, in the Class View, right-click the name of
the project, position the mouse on Add and click Class...
- Set the Name to Property and press Enter
- Change the file as follows:
using System;
namespace RealEstate3
{
public enum PropertyCondition
{
Unknown,
Excellent,
Good,
NeedsRepair,
BadShape
}
public class Property
{
private string propNbr;
private PropertyCondition cond;
private short beds;
private float baths;
private int yr;
private decimal val;
public Property()
{
}
public string PropertyNumber
{
get { return propNbr; }
set
{
if (propNbr == "")
propNbr = "N/A";
else
propNbr = value;
}
}
public PropertyCondition Condition
{
get { return cond; }
set { cond = value; }
}
public short Bedrooms
{
get
{
if (beds <= 1)
return 1;
else
return beds;
}
set { beds = value; }
}
public float Bathrooms
{
get{ return (baths <= 0) ? 0.00f : baths; }
set { baths = value; }
}
public int YearBuilt
{
get { return yr; }
set { yr = value; }
}
public decimal Value
{
get{ return (val <= 0) ? 0.00M : val; }
set { val = value; }
}
}
}
|
- On the main menu, click Project -> Add Class...
- Set the Name to HouseType and press Enter
- To derive a class, change the file as follows:
using System;
namespace RealEstate3
{
public class HouseType : Property
{
private short nbrOfStories;
private bool basement;
private bool garage;
public short Stories
{
get { return nbrOfStories; }
set { nbrOfStories = value; }
}
public bool FinishedBasement
{
get { return basement; }
set { basement = value; }
}
public bool IndoorGarage
{
get { return garage; }
set { garage = value; }
}
}
}
|
- On the main menu, click Project -> Add -> Class...
- Set the Name to Condominium and press Enter
- To create another class based on the Property class, change the file as follows:
using System;
namespace RealEstate3
{
public class Condominium : Property
{
private bool handicap;
public bool HandicapAccessible
{
get { return handicap; }
set { handicap = value; }
}
}
}
|
- To create a new class, in the Solution Explorer, right-click
RealEstate3, position the mouse on Add and click Class...
- Set the Name to PropertyListing and press Enter
- Change the file as follows:
using System;
namespace RealEstate3
{
public enum PropertyType
{
Unknown,
SingleFamily,
Townhouse,
Condominium
}
public class PropertyListing
{
private Property prop;
private HouseType hse;
private Condominium cond;
private PropertyType tp;
public Property ListProperty
{
get { return prop; }
set { prop = value; }
}
public HouseType House
{
get { return hse; }
set { hse = value; }
}
public Condominium Condo
{
get { return cond; }
set { cond = value; }
}
public PropertyType Type
{
get { return tp; }
set { tp = value; }
}
public PropertyListing()
{
prop = new Property();
hse = new HouseType();
cond = new Condominium();
}
public void CreateListing()
{
char answer = 'n';
short propType = 1;
short condition = 1;
Console.WriteLine(" =//= Altair Realty =//=");
Console.WriteLine("-=- Property Creation -=-");
try
{
Console.WriteLine("\nTypes of Properties");
Console.WriteLine("1. Single Family");
Console.WriteLine("2. Townhouse");
Console.WriteLine("3. Condominium");
Console.WriteLine("4. Don't Know");
Console.Write("Enter Type of Property: ");
propType = short.Parse(Console.ReadLine());
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
Console.Write("\nEnter Property #: ");
ListProperty.PropertyNumber = Console.ReadLine();
try
{
Console.WriteLine("\nProperties Conditions");
Console.WriteLine("1. Excellent");
Console.WriteLine("2. Good (may need minor repair)");
Console.WriteLine("3. Needs Repair");
Console.Write("4. In Bad Shape (property needs ");
Console.WriteLine("major repair or rebuild)");
Console.Write("Enter Property Condition: ");
condition = short.Parse(Console.ReadLine());
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
if (condition == 1)
ListProperty.Condition = PropertyCondition.Excellent;
else if (condition == 2)
ListProperty.Condition = PropertyCondition.Good;
else if (condition == 3)
ListProperty.Condition = PropertyCondition.NeedsRepair;
else if (condition == 4)
ListProperty.Condition = PropertyCondition.BadShape;
else
ListProperty.Condition = PropertyCondition.Unknown;
switch ((PropertyType)propType)
{
case PropertyType.SingleFamily:
Type = PropertyType.SingleFamily;
try
{
Console.Write("\nHow many stories (levels)? ");
House.Stories = short.Parse(Console.ReadLine());
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
try
{
Console.Write(
"Does it have an indoor car garage (y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
House.IndoorGarage = true;
else
House.IndoorGarage = false;
}
catch (FormatException)
{
Console.WriteLine("Invalid Indoor Car Garage Answer");
}
try
{
Console.Write("Is the basement finished(y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
House.FinishedBasement = true;
else
House.FinishedBasement = false;
}
catch (FormatException)
{
Console.WriteLine("Invalid Basement Answer");
}
break;
case PropertyType.Townhouse:
Type = PropertyType.Townhouse;
try
{
Console.Write("\nHow many stories (levels)? ");
House.Stories = short.Parse(Console.ReadLine());
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
Console.Write("Does it have an indoor car garage (y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
House.IndoorGarage = true;
else
House.IndoorGarage = false;
Console.Write("Is the basement finished(y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
House.FinishedBasement = true;
else
House.FinishedBasement = false;
break;
case PropertyType.Condominium:
Type = PropertyType.Condominium;
Console.Write(
"\nIs the building accessible to handicapped (y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
Condo.HandicapAccessible = true;
else
Condo.HandicapAccessible = false;
break;
default:
Type = PropertyType.Unknown;
break;
}
try
{
Console.Write("\nHow many bedrooms? ");
ListProperty.Bedrooms = short.Parse(Console.ReadLine());
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
try
{
Console.Write("How many bathrooms? ");
ListProperty.Bathrooms = float.Parse(Console.ReadLine());
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
try
{
Console.Write("Year built: ");
ListProperty.YearBuilt = int.Parse(Console.ReadLine());
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
try
{
Console.Write("Property Value: ");
ListProperty.Value = decimal.Parse(Console.ReadLine());
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
}
public void ShowProperty()
{
Console.WriteLine("==================================");
Console.WriteLine(" =//=//= Altair Realty =//=//=");
Console.WriteLine("-=-=-=- Properties Listing -=-=-=-");
Console.WriteLine("----------------------------------");
Console.WriteLine("Property #: {0}",
ListProperty.PropertyNumber);
Console.WriteLine("Property Type: {0}", Type);
switch (Type)
{
case PropertyType.SingleFamily:
case PropertyType.Townhouse:
Type = PropertyType.SingleFamily;
Console.WriteLine("Stories: {0}",
House.Stories);
Console.WriteLine("Has Indoor Car Garage: {0}",
House.IndoorGarage);
Console.WriteLine("Finished Basement: {0}",
House.FinishedBasement);
break;
case PropertyType.Condominium:
Console.WriteLine("Handicapped Accessible Building: {0}",
Condo.HandicapAccessible);
break;
}
Console.WriteLine("Condition: {0}",
ListProperty.Condition);
Console.WriteLine("Bedrooms: {0}",
ListProperty.Bedrooms);
Console.WriteLine("Bathrooms: {0}",
ListProperty.Bathrooms);
Console.WriteLine("Year Built: {0}",
ListProperty.YearBuilt);
Console.WriteLine("Market Value: {0:C}",
ListProperty.Value);
}
}
}
|
- Access the Program.cs file and change it as follows:
using System;
namespace RealEstate3
{
public static class Program
{
static int Main()
{
PropertyListing listing = new PropertyListing();
listing.CreateListing();
Console.WriteLine("\n");
listing.ShowProperty();
Console.WriteLine();
return 0;
}
}
}
|
- Execute the application and test it. Here is an example:
=//= Altair Realty =//=
-=- Property Creation -=-
Types of Properties
1. Single Family
2. Townhouse
3. Condominium
4. Don't Know
Enter Type of Property: 1
Enter Property #: 276744
Properties Conditions
1. Excellent
2. Good (may need minor repair)
3. Needs Repair
4. In Bad Shape (property needs major repair or rebuild)
Enter Property Condition: 3
How many stories (levels)? -2
Does it have an indoor car garage (y/n): g
Is the basement finished(y/n): m
How many bedrooms? -5
How many bathrooms? 3684634
Year built: 87324
Property Value: 2
==================================
=//=//= Altair Realty =//=//=
-=-=-=- Properties Listing -=-=-=-
----------------------------------
Property #: 276744
Property Type: SingleFamily
Stories: -2
Has Indoor Car Garage: False
Finished Basement: False
Condition: NeedsRepair
Bedrooms: 1
Bathrooms: 3684634
Year Built: 87324
Market Value: $2.00
Press any key to continue . . .
|
- Close the DOS window
 |
When a bad behavior occurs in your application, the program
is said to throw an exception. Your job is to know, as much as possible, when
and why this would happen. In the previous lesson and in the above section, we
saw that, when an application throws an exception, you could display your own
message. To effectively address a particular exception, you need to identify the
(exact, if possible) section of code where the exception would occur and you
should know why the error would be produced. After identifying the area in code
where an exception is likely to occur, you can further customize the behavior of
your application.
|
 |
To customize the throwing of an exception, in the section
of code where you are anticipating the
error, type the throw keyword followed by a new instance of the Exception
class (or one of its derived classes) using the constructor that takes a string. Here is an example:
using System;
class Program
{
static int Main()
{
double Operand1, Operand2;
double Result = 0.00;
char Operator;
Console.WriteLine(
"This program allows you to perform an operation on two numbers");
try
{
Console.Write("Enter a number: ");
Operand1 = double.Parse(Console.ReadLine());
Console.Write("Enter an operator: ");
Operator = char.Parse(Console.ReadLine());
Console.Write("Enter a number: ");
Operand2 = double.Parse(Console.ReadLine());
if (Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/')
throw new Exception(Operator.ToString());
switch (Operator)
{
case '+':
Result = Operand1 + Operand2;
break;
case '-':
Result = Operand1 - Operand2;
break;
case '*':
Result = Operand1 * Operand2;
break;
case '/':
Result = Operand1 / Operand2;
break;
default:
Console.WriteLine("Bad Operation");
break;
}
Console.WriteLine("\n{0} {1} {2} = {3}", Operand1, Operator, Operand2, Result);
}
catch (Exception ex)
{
Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message);
}
return 0;
}
}
Here is an example of running the program:
This program allows you to perform an operation on two numbers
Enter a number: 124.55
Enter an operator: &
Enter a number: 36.85
Operation Error: & is not a valid operator
Press any key to continue . . .
|
Practical Learning: Throwing an Exception
|
|
- Access the PropertyListing.cs file
- To throw some exceptions, change the file as follows:
using System;
namespace RealEstate3
{
public enum PropertyType
{
Unknown,
SingleFamily,
Townhouse,
Condominium
}
class PropertyListing
{
. . . No Change
public void CreateListing()
{
. . . No Change
switch ((PropertyType)propType)
{
case PropertyType.SingleFamily:
Type = PropertyType.SingleFamily;
try
{
Console.Write("\nHow many stories (levels)? ");
House.Stories = short.Parse(Console.ReadLine());
if (House.Stories < 1)
{
House.Stories = 1;
throw new OverflowException(
"The number of stories must be positive");
}
}
catch (OverflowException ex)
{
Console.WriteLine(ex.Message);
}
. . . No Change
break;
case PropertyType.Townhouse:
Type = PropertyType.Townhouse;
try
{
Console.Write("\nHow many stories (levels)? ");
House.Stories = short.Parse(Console.ReadLine());
if (House.Stories < 1)
{
House.Stories = 1;
throw new OverflowException(
"The number of stories must be positive");
}
}
catch (OverflowException ex)
{
Console.WriteLine(ex.Message);
}
. . . No Change
}
. . . No Change
}
public void ShowProperty()
{
. . . No Change
}
}
}
|
- Execute the application and test it. Here is an example:
=//= Altair Realty =//=
-=- Property Creation -=-
Types of Properties
1. Single Family
2. Townhouse
3. Condominium
4. Don't Know
Enter Type of Property: 2
Enter Property #: 463864
Properties Conditions
1. Excellent
2. Good (may need minor repair)
3. Needs Repair
4. In Bad Shape (property needs major repair or rebuild)
Enter Property Condition: 3
How many stories (levels)? -3
The number of levels must be positive
Does it have an indoor car garage (y/n): G
Is the basement finished(y/n): K
How many bedrooms? -4
How many bathrooms? -2
Year built: 2994
Property Value: 425885
==================================
=//=//= Altair Realty =//=//=
-=-=-=- Properties Listing -=-=-=-
----------------------------------
Property #: 463864
Property Type: Townhouse
Stories: 1
Has Indoor Car Garage: False
Finished Basement: False
Condition: NeedsRepair
Bedrooms: 1
Bathrooms: 0.00
Year Built: 2994
Market Value: $425,885.00
Press any key to continue . . .
|
- Close the DOS window
|
Catching Various Exceptions
|
|
In the examples above, when we anticipated some type
of problem, we instructed the compiler to use our default catch
section. We left it up to the compiler to find out when there was a
problem and we provided a catch section to deal with it. A method with numerous or complex
operations and requests can also produce different types of
errors. With such a type of program, you should be able to face different
problems and deal with them individually, each by its own kind. To do
this, you can create different catch sections, each made for a
particular error. The formula used would be:
try {
// Code to Try
}
catch(Arg1)
{
// One Exception
}
catch(Arg2)
{
// Another Exception
}
The compiler would proceed in a top-down:
- Following the normal flow of the program, the compiler
enters the try block
- If no exception occurs in the try block, the rest of the try
block is executed
If an exception occurs in the try block, the compiler
registers the type of error that occurred. If there is a throw line,
the compiler registers it also:
- The compiler gets out of the try
section
- The compiler examines the first
catch. If the first catch matches the thrown
error, that catch executes and the exception handling routine
may seize. If the first catch doesn’t match the thrown error, the
compiler proceeds with the next catch
- The compiler checks the next match, if any, and proceeds as in
the first catch. This continues
until the compiler finds a catch that matches the thrown
error
- If one of the catches matches the thrown error, its body
executes. If no catch matches the thrown error, the compiler
calls the Exception class and uses the default message
Multiple catches are written if or when a try block
is expected to throw different types of errors. Once again, consider the
previous program. That program works fine as long as the user types a
valid sequence of values made of a number, a valid arithmetic
operator, and a number. Anything else, such as an invalid number,
an unexpected operator, or a wrong sequence (such as a number then
another number instead of an operator), would cause an error. Obviously various bad
things could happen when this program is running. To handle the
exceptions that this program could produce, you can start with the most
likely problem that would occur. Trusting that a user is able to provide
the two numbers that are requested, it is possible that a user would
type an invalid operator. For example, for this program we will perform
only the addition (+), the subtraction(-), the multiplication(*), and
the division(/). Therefore, we will first validate the operator. This
can be done as follows:
using System;
class Program
{
static int Main()
{
double Operand1, Operand2;
double Result = 0.00;
char Operator;
Console.WriteLine("This program allows you to perform an operation on two numbers");
try
{
Console.Write("Enter a number: ");
Operand1 = double.Parse(Console.ReadLine());
Console.Write("Enter an operator: ");
Operator = char.Parse(Console.ReadLine());
Console.Write("Enter a number: ");
Operand2 = double.Parse(Console.ReadLine());
if (Operator != '+' &&
Operator != '-' &&
Operator != '*' &&
Operator != '/')
throw new Exception(Operator.ToString());
switch (Operator)
{
case '+':
Result = Operand1 + Operand2;
break;
case '-':
Result = Operand1 - Operand2;
break;
case '*':
Result = Operand1 * Operand2;
break;
case '/':
Result = Operand1 / Operand2;
break;
default:
Console.WriteLine("Bad Operation");
break;
}
Console.WriteLine("\n{0} {1} {2} = {3}", Operand1, Operator, Operand2, Result);
}
catch (Exception ex)
{
Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message);
}
return 0;
}
}
When this program runs, if the user provides a valid number
followed by a wrong operator, we call the Exception(string message)
constructor and pass it a string converted from the character that was typed.
Imagine that the user wants to perform a division. You need to
tell the compiler what to do if the user enters the denominator as 0
(or 0.00). If this happens, probably the best option is to display a message and get out.
Fortunately, the .NET Framework provides the DivideByZeroException class
to deal with an exception caused by division by zero. As done with the message passed
to the Exception class, you can compose your own message and pass it to the DivideByZeroException(string
message) constructor.
Here is an example that catches two types of exceptions:
using System;
class Program
{
static int Main()
{
double Operand1, Operand2;
double Result = 0.00;
char Operator;
Console.WriteLine("This program allows you to perform an operation on two numbers");
try
{
Console.Write("Enter a number: ");
Operand1 = double.Parse(Console.ReadLine());
Console.Write("Enter an operator: ");
Operator = char.Parse(Console.ReadLine());
Console.Write("Enter a number: ");
Operand2 = double.Parse(Console.ReadLine());
if (Operator != '+' &&
Operator != '-' &&
Operator != '*' &&
Operator != '/')
throw new Exception(Operator.ToString());
if (Operator == '/') if (Operand2 == 0)
throw new DivideByZeroException("Division by zero is not allowed");
switch (Operator)
{
case '+':
Result = Operand1 + Operand2;
break;
case '-':
Result = Operand1 - Operand2;
break;
case '*':
Result = Operand1 * Operand2;
break;
case '/':
Result = Operand1 / Operand2;
break;
default:
Console.WriteLine("Bad Operation");
break;
}
Console.WriteLine("\n{0} {1} {2} = {3}", Operand1, Operator, Operand2, Result);
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message);
}
return 0;
}
}
When running this program, if the user types a wrong operator,
the compiler gets out of the try block
and looks for a catch that takes an Exception as argument.
It finds the second and executes it. If the user enters the right values (a
number, an operator, and another number), then the compiler finds out if the
operator entered was a forward slash “/” used to perform a
division. If the user wants to perform a division, the compiler
finds out if the second operand, the denominator, is 0. If it is, we
create a DivideByZeroException instance and pass our own message to it. Based on this
exception, the compiler gets out of the try block and starts looking
for a catch block that takes a DivideByZeroException
argument. It finds it in the first catch. Therefore, the compiler executes it.
|
Practical Learning: Catching Various Exceptions
|
|
- Change the PropertyListing.cs file as follows:
using System;
namespace RealEstate3
{
public enum PropertyType
{
Unknown,
SingleFamily,
Townhouse,
Condominium
}
class PropertyListing
{
private Property prop;
private HouseType hse;
private Condominium cond;
private PropertyType tp;
public Property ListProperty
{
get { return prop; }
set { prop = value; }
}
public HouseType House
{
get { return hse; }
set { hse = value; }
}
public Condominium Condo
{
get { return cond; }
set { cond = value; }
}
public PropertyType Type
{
get { return tp; }
set { tp = value; }
}
public PropertyListing()
{
prop = new Property();
hse = new HouseType();
cond = new Condominium();
}
public void CreateListing()
{
char answer = 'n';
short propType = 1;
short condition = 1;
Console.WriteLine(" =//= Altair Realty =//=");
Console.WriteLine("-=- Property Creation -=-");
try
{
Console.WriteLine("\nTypes of Properties");
Console.WriteLine("1. Single Family");
Console.WriteLine("2. Townhouse");
Console.WriteLine("3. Condominium");
Console.WriteLine("4. Don't Know");
Console.Write("Enter Type of Property: ");
propType = short.Parse(Console.ReadLine());
}
catch (FormatException)
{
Console.WriteLine("The value you entered is not a valid number");
}
catch (Exception)
{
Console.WriteLine("Something bad happened and we don't like it");
}
Console.Write("\nEnter Property #: ");
ListProperty.PropertyNumber = Console.ReadLine();
try
{
Console.WriteLine("\nProperties Conditions");
Console.WriteLine("1. Excellent");
Console.WriteLine("2. Good (may need minor repair)");
Console.WriteLine("3. Needs Repair");
Console.Write("4. In Bad Shape (property needs ");
Console.WriteLine("major repair or rebuild)");
Console.Write("Enter Property Condition: ");
condition = short.Parse(Console.ReadLine());
}
catch (FormatException)
{
Console.WriteLine("The value you typed for the property condition is not good");
}
catch (Exception)
{
Console.WriteLine("Something unacceptable has just happened");
}
if (condition == 1)
ListProperty.Condition = PropertyCondition.Excellent;
else if (condition == 2)
ListProperty.Condition = PropertyCondition.Good;
else if (condition == 3)
ListProperty.Condition = PropertyCondition.NeedsRepair;
else if (condition == 4)
ListProperty.Condition = PropertyCondition.BadShape;
else
ListProperty.Condition = PropertyCondition.Unknown;
switch ((PropertyType)propType)
{
case PropertyType.SingleFamily:
Type = PropertyType.SingleFamily;
try
{
Console.Write("\nHow many stories (levels)? ");
House.Stories = short.Parse(Console.ReadLine());
if (House.Stories < 1)
{
House.Stories = 1;
throw new OverflowException("The number of levels must be positive");
}
}
catch (OverflowException ex)
{
Console.WriteLine(ex.Message);
}
catch (FormatException)
{
Console.WriteLine("The number you entered for the stories is not allowed");
}
catch (Exception)
{
Console.WriteLine("This is one of those abnormal behaviors");
}
try
{
Console.Write("Does it have an indoor car garage (y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
House.IndoorGarage = true;
else
House.IndoorGarage = false;
}
catch (FormatException)
{
Console.WriteLine("Invalid Indoor Car Garage Answer");
}
try
{
Console.Write("Is the basement finished(y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
House.FinishedBasement = true;
else
House.FinishedBasement = false;
}
catch (FormatException)
{
Console.WriteLine("Invalid Basement Answer");
}
break;
case PropertyType.Townhouse:
Type = PropertyType.Townhouse;
try
{
Console.Write("\nHow many stories (levels)? ");
House.Stories = short.Parse(Console.ReadLine());
if (House.Stories < 1)
{
House.Stories = 1;
throw new OverflowException("The number of levels must be positive");
}
}
catch (OverflowException ex)
{
Console.WriteLine(ex.Message);
}
catch (FormatException)
{
Console.WriteLine("The number you entered for the stories is not allowed");
}
catch (Exception)
{
Console.WriteLine("This is one of those abnormal behaviors");
}
Console.Write("Does it have an indoor car garage (y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
House.IndoorGarage = true;
else
House.IndoorGarage = false;
Console.Write("Is the basement finished(y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
House.FinishedBasement = true;
else
House.FinishedBasement = false;
break;
case PropertyType.Condominium:
Type = PropertyType.Condominium;
Console.Write("\nIs the building accessible to handicapped (y/n): ");
answer = char.Parse(Console.ReadLine());
if ((answer == 'y') || (answer == 'Y'))
Condo.HandicapAccessible = true;
else
Condo.HandicapAccessible = false;
break;
default:
Type = PropertyType.Unknown;
break;
}
try
{
Console.Write("\nHow many bedrooms? ");
ListProperty.Bedrooms = short.Parse(Console.ReadLine());
}
catch (FormatException)
{
Console.WriteLine(
"The value you entered for the number of bedrooms is not acceptable");
}
catch (Exception)
{
Console.WriteLine("The program has decided to stop");
}
try
{
Console.Write("How many bathrooms? ");
ListProperty.Bathrooms = float.Parse(Console.ReadLine());
}
catch (FormatException)
{
Console.WriteLine("The value you decided to enter for the bedrooms is rejected");
}
catch (Exception)
{
Console.WriteLine("The computer doesn't like what is going on");
}
try
{
Console.Write("Year built: ");
ListProperty.YearBuilt = int.Parse(Console.ReadLine());
}
catch (FormatException)
{
Console.WriteLine("You didn't enter a valid number for the year built");
}
catch (Exception)
{
Console.WriteLine("Whatever, whatever, whatever");
}
try
{
Console.Write("Property Value: ");
ListProperty.Value = decimal.Parse(Console.ReadLine());
}
catch (FormatException)
{
Console.WriteLine(
"The value of a property must be a good decimal number");
}
catch (Exception)
{
Console.WriteLine(
"This is where the application draws the line: it stops!");
}
}
public void ShowProperty()
{
Console.WriteLine("==================================");
Console.WriteLine(" =//=//= Altair Realty =//=//=");
Console.WriteLine("-=-=-=- Properties Listing -=-=-=-");
Console.WriteLine("----------------------------------");
Console.WriteLine("Property #: {0}",
ListProperty.PropertyNumber);
Console.WriteLine("Property Type: {0}", Type);
switch (Type)
{
case PropertyType.SingleFamily:
case PropertyType.Townhouse:
Type = PropertyType.SingleFamily;
Console.WriteLine("Stories: {0}",
House.Stories);
Console.WriteLine("Has Indoor Car Garage: {0}",
House.IndoorGarage);
Console.WriteLine("Finished Basement: {0}",
House.FinishedBasement);
break;
case PropertyType.Condominium:
Console.WriteLine("Handicapped Accessible Building: {0}",
Condo.HandicapAccessible);
break;
}
Console.WriteLine("Condition: {0}",
ListProperty.Condition);
Console.WriteLine("Bedrooms: {0}",
ListProperty.Bedrooms);
Console.WriteLine("Bathrooms: {0:F}",
ListProperty.Bathrooms);
Console.WriteLine("Year Built: {0}",
ListProperty.YearBuilt);
Console.WriteLine("Market Value: {0:C}",
ListProperty.Value);
}
}
}
|
- Execute the application and test it. Here is an example:
=//= Altair Realty =//=
-=- Property Creation -=-
Types of Properties
1. Single Family
2. Townhouse
3. Condominium
4. Don't Know
Enter Type of Property: 1
Enter Property #: 228046
Properties Conditions
1. Excellent
2. Good (may need minor repair)
3. Needs Repair
4. In Bad Shape (property needs major repair or rebuild)
Enter Property Condition: 4
How many stories (levels)? -5
The number of levels must be positive
Does it have an indoor car garage (y/n): y
Is the basement finished(y/n): Not at all
Invalid Basement Answer
How many bedrooms? Apparently 3 or 4
The value you entered for the number of bedrooms is not acceptable
How many bathrooms? 1 can flush, the owner is working on another
The value you decided to enter for the bedrooms is rejected
Year built: Around 1962
You didn't enter a valid number for the year built
Property Value: A little over 420000
The value of a property must be an good decimal number
==================================
=//=//= Altair Realty =//=//=
-=-=-=- Properties Listing -=-=-=-
----------------------------------
Property #: 228046
Property Type: SingleFamily
Stories: 1
Has Indoor Car Garage: True
Finished Basement: False
Condition: BadShape
Bedrooms: 1
Bathrooms: 0.00
Year Built: 0
Market Value: $0.00
Press any key to continue . . .
|
- Close the DOS window
The calculator simulator we have studied so far
performs a division as one of its assignments. We learned that, in order
to perform any operation, the compiler must first make sure that the
user has entered a valid operator. Provided the operator is one of those
we are expecting, we also must make sure that the user typed valid numbers. Even
if these two criteria are met, it was possible that the
user enter 0 for the denominator. The block that is used to check for a
non-zero denominator depends on the exception that validates the
operators. The exception that could result from a zero denominator depends on the
user first entering a valid number for the denominator.
You can create an exception inside of another. This
is referred to as nesting an exception. This is done by applying the same
techniques we used to nest conditional statements. This means that
you can write an exception that depends on, or is subject to, another
exception. To nest an exception, write a try block in the body of the
parent exception. The nested try block must be followed by its own
catch(es) clause. To effectively handle the exception, make sure you include an
appropriate throw in the try block. Here is an example:
using System;
class Program
{
static int Main()
{
double Operand1, Operand2;
double Result = 0.00;
char Operator;
Console.WriteLine(
"This program allows you to perform an operation on two numbers");
try
{
Console.Write("Enter a number: ");
Operand1 = double.Parse(Console.ReadLine());
Console.Write("Enter an operator: ");
Operator = char.Parse(Console.ReadLine());
Console.Write("Enter a number: ");
Operand2 = double.Parse(Console.ReadLine());
if (Operator != '+' &&
Operator != '-' &&
Operator != '*' &&
Operator != '/')
throw new Exception(Operator.ToString());
switch (Operator)
{
case '+':
Result = Operand1 + Operand2;
Console.WriteLine("\n{0} + {1} = {2}", Operand1, Operand2, Result);
break;
case '-':
Result = Operand1 - Operand2;
Console.WriteLine("\n{0} - {1} = {2}", Operand1, Operand2, Result);
break;
case '*':
Result = Operand1 * Operand2;
Console.WriteLine("\n{0} * {1} = {2}", Operand1, Operand2, Result);
break;
case '/':
// The following exception is nested in the previous try
try
{
if (Operand2 == 0)
throw new DivideByZeroException("Division by zero is not allowed");
Result = Operand1 / Operand2;
Console.WriteLine("\n{0} / {1} = {2}", Operand1, Operand2, Result);
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
break;
}
}
catch (Exception ex)
{
Console.WriteLine("\nOperation Error: {0} is not a valid operator", ex.Message);
}
return 0;
}
}
Here is an example of running the program:
This program allows you to perform an operation on two numbers
Enter a number: 324.53
Enter an operator: /
Enter a number: 0
Division by zero is not allowed
Press any key to continue . . .
One of the most effective techniques used to deal
with code is to isolate assignments. We learned this when studying
methods of classes. For example, the switch statement that was performing the
operations in the “normal” version of our program can be written as
follows:
using System;
class Program
{
static int Main()
{
double Number1, Number2;
double Result = 0.00;
char Operator;
Console.WriteLine(
"This program allows you to perform an operation on two numbers");
try
{
Console.WriteLine("To proceed, enter");
Console.Write("First Number: ");
Number1 = double.Parse(Console.ReadLine());
Console.Write("An Operator (+, -, * or /): ");
Operator = char.Parse(Console.ReadLine());
if (Operator != '+' && Operator != '-' &&
Operator != '*' && Operator != '/')
throw new Exception(Operator.ToString());
Console.Write("Second Number: ");
Number2 = double.Parse(Console.ReadLine());
if (Operator == '/')
if (Number2 == 0)
throw new DivideByZeroException("Division by zero is not allowed");
Result = Calculator(Number1, Number2, Operator);
Console.WriteLine("\n{0} {1} {2} = {3}", Number1, Operator, Number2, Result);
}
catch (FormatException)
{
Console.WriteLine("The number you typed is not valid");
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(
"\nOperation Error: {0} is not a valid operator", ex.Message);
}
return 0;
}
static double Calculator(double Value1, double Value2, char Symbol)
{
double Result = 0.00;
switch (Symbol)
{
case '+':
Result = Value1 + Value2;
break;
case '-':
Result = Value1 - Value2;
break;
case '*':
Result = Value1 * Value2;
break;
case '/':
Result = Value1 / Value2;
break;
}
return Result;
}
}
Here is one example of running the program:
This program allows you to perform an operation on two numbers
To proceed, enter
First Number: 248.84
An Operator (+, -, * or /): *
Second Number: 57.93
248.84 * 57.93 = 14415.3012
Press any key to continue . . .
Here is another example of running the program:
This program allows you to perform an operation on two numbers
To proceed, enter
First Number: 12.55
An Operator (+, -, * or /): [
Operation Error: [ is not a valid operator
Press any key to continue . . .
You can still use regular methods along with methods
that handle exceptions. As done in Main(), any method of a program can take care of its own exceptions that would occur in its
body. Here is an example of an exception handled in a method:
using System;
class Program
{
static int Main()
{
double Number1, Number2;
double Result = 0.00;
char Operator;
Console.WriteLine(
"This program allows you to perform an operation on two numbers");
try
{
Console.WriteLine("To proceed, enter");
Console.Write("First Number: ");
Number1 = double.Parse(Console.ReadLine());
Console.Write("An Operator (+, -, * or /): ");
Operator = char.Parse(Console.ReadLine());
if (Operator != '+' && Operator != '-' &&
Operator != '*' && Operator != '/')
throw new Exception(Operator.ToString());
Console.Write("Second Number: ");
Number2 = double.Parse(Console.ReadLine());
Result = Calculator(Number1, Number2, Operator);
}
catch (FormatException)
{
Console.WriteLine("The number you typed is not valid");
}
catch (Exception ex)
{
Console.WriteLine(
"\nOperation Error: {0} is not a valid operator", ex.Message);
}
return 0;
}
static double Calculator(double Value1, double Value2, char Symbol)
{
double Result = 0.00;
switch (Symbol)
{
case '+':
Result = Value1 + Value2;
Console.WriteLine("\n{0} + {1} = {2}", Value1, Value2, Result);
break;
case '-':
Result = Value1 - Value2;
Console.WriteLine("\n{0} - {1} = {2}", Value1, Value2, Result);
break;
case '*':
Result = Value1 * Value2;
Console.WriteLine("\n{0} * {1} = {2}", Value1, Value2, Result);
break;
case '/':
// The following exception is nested in the previous try
try
{
if (Value2 == 0)
throw new DivideByZeroException("Division by zero is not allowed");
Result = Value1 / Value2;
Console.WriteLine("\n{0} / {1} = {2}", Value1, Value2, Result);
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
break;
}
return Result;
}
}
Isolating assignments and handing them to method is an important matter in the area of application
programming. Consider a program that handles a simple exception such as
this one:
using System;
class Program
{
static int Main()
{
double Number1, Number2;
double Result = 0.00;
char Operator = '/';
Console.WriteLine("This program allows you to perform a division of two numbers");
try
{
Console.Write("Enter a number: ");
Number1 = double.Parse(Console.ReadLine());
Console.Write("Enter a number: ");
Number2 = double.Parse(Console.ReadLine());
if (Number2 == 0)
throw new DivideByZeroException("Division by zero is not allowed");
Result = Number1 / Number2;
Console.WriteLine("\n{0} / {1} = {2}", Number1, Number2, Result);
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
return 0;
}
}
One of the ways you can use methods in exception
routines is to have a central method that receives variables, and sends
them to other external methods. The external method tests the value of a
variable. If an exception occurs, the external method displays or
sends a throw. This throw can be picked up by the method that sent the
error. Observe the following example that
implements this scenario:
using System;
class Program
{
static int Main()
{
double Number1, Number2;
Console.WriteLine("This program allows you to perform a division of two numbers");
try
{
Console.Write("Enter a number: ");
Number1 = double.Parse(Console.ReadLine());
Console.Write("Enter a number: ");
Number2 = double.Parse(Console.ReadLine());
Division(Number1, Number2);
}
catch(DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}
return 0;
}
static void Division(double a, double b)
{
double Result;
// If an exception occurred,
if( b == 0 ) // then throw a string to the function caller
throw new DivideByZeroException("Division by zero is not allowed");
Result = a / b;
Console.WriteLine("\n{0} / {1} = {2}", a, b, Result);
}
}
In this program, the Division method receives two
values that it is asked to perform a division with. The Division method analyzes the second argument that represents the denominator.
If this argument is zero, an exception is found and the Division method throws a
DivideByZeroException exception.
A method can also be called to perform more than
one test to eventually throw more than one exception. Such a
method can (and should) be programmed to throw different types
of exceptions.
As seen in the previous lesson and in the above
sections, exception handling is a great part of the .NET Framework. As
high as it is supported by various classes of the .NET, it is possible
that you want to further customize the handling of exceptions in your
application. One way you can do this is to create your own exception
class.
|
Creating an Exceptional Class
|
|
The Exception class of the .NET Framework is a great
tool for handling exceptions in a C# application. To deal with
particular errors, various classes are derived from Exception. If
for some reason the Exception class and none of the Exception-based classes fulfills your requirement, you can derive a new
class from Exception or from one of the available Exception-based
classes.
To derive a class from Exception or from one of its
classes, simply follow the rules we reviewed from class inheritance.
Here is an example of a class based on Exception:
public class CustomException : Exception
{
}
There is no real rule to follow as to what class to
derive from but it may be a good idea to derive your class from one that
already addresses your issue but not the way you want. For example, if
you want to create a class that would process numeric values but you
think the FormatException class is not doing what you want, you can
derive your class from FormatException.
After deriving the class, you can add the necessary members
as you see fit. Remember that the primary characteristic of an exception is to
present a message to the user. In Exception-based classes, this message is
represented by the Message property. Therefore, if you want to prepare a custom
message for your class, you can override or new this property.
Here is an example:
public class IntegerException : Exception
{
public override string Message
{
get
{
return "The value you entered is not a valid integer";
}
}
}
Once you have created and customized your exceptional class,
you can use it the same way you would another exception class, such as throwing
it. Here is an example
using System;
public class DigitException : Exception
{
private char c;
public DigitException(char x)
{
c = x;
}
public override string Message
{
get
{
return "The character you entered is not a valid digit";
}
}
}
class Program
{
static int Main()
{
try
{
char chNumber = '0';
Console.Write("Enter a digit (0 to 9): ");
chNumber = char.Parse(Console.ReadLine());
if ((chNumber != '0') &&
(chNumber != '1') &&
(chNumber != '2') &&
(chNumber != '3') &&
(chNumber != '4') &&
(chNumber != '5') &&
(chNumber != '6') &&
(chNumber != '7') &&
(chNumber != '8') &&
(chNumber != '9'))
throw new DigitException(chNumber);
Console.WriteLine("Number: {0}\n", chNumber);
}
catch (DigitException ex)
{
Console.WriteLine(ex.Message);
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return 0;
}
}
Here is an example of running the program:
Enter a digit (0 to 9): 8
Number: 8
Press any key to continue . . .
Here is another example of running the program:
Enter a digit (0 to 9): w
The character you entered is not a valid digit
Press any key to continue . . .
|
|