Account
Class with a Balance; Processing Monetary AmountsIn this section, we’ll declare an Account
class that maintains an Account
’s balance in addition to its name. Most account balances are not whole numbers (such as 0, –22 and 1024), rather they’re numbers that include a decimal point, such as 99.99 or –20.15. For this reason, class Account
represents the account balance using type decimal
, which is designed to precisely represent numbers with decimal points, especially monetary amounts.
Account
Class with a decimal balance
Instance VariableA typical bank services many accounts, each with its own balance, so our next Account
class (Fig. 4.11) maintains a bank account’s
name—as the auto-implemented Name
property (line 6)—and
balance—as the private decimal
instance variable balance
(line 7) and a corresponding public Balance
property (lines 17–32). We use a fully implemented Balance
property here so we can ensure that the set
accessor’s argument is valid before assigning it to the balance
instance variable.
A decimal
instance variable is initialized to zero by default. Every instance (i.e., object) of class Account
contains its own name and balance.
Account
Class Two-Parameter ConstructorIt’s common for someone opening an account to deposit money immediately, so the constructor (lines 10–14) now receives a second parameter—initialBalance
of type decimal
that represents the starting balance. Line 13 assigns initialBalance
to the property Balance
, invoking Balance
’s set
accessor to ensure that the initialBalance
argument is valid before assigning a value to the instance variable balance
.
Account
Property Balance
Property Balance
(lines 17–32) of type decimal
provides a get
accessor, which allows clients of the class to obtain a particular Account
object’s balance
. The property also provides a set
accessor.
In Fig. 4.6, class Account
defined a Name
property in which the set
accessor simply assigned the value received in its implicit parameter value
to class Account
’s instance variable name
. The Name
property did not ensure that name
contains only valid data.
The Balance
property’s set
accessor performs validation (also known as validity checking). Line 27 (Fig. 4.11) ensures that the set
accessor’s implicit value
parameter is greater than 0.0m
—the letter m
(or M
) indicates that 0.0 is a decimal
literal.4 If value
is greater than 0.0m
, the amount stored in value
is assigned to instance variable balance
(line 29). Otherwise, balance
is left unchanged—we’ll say more about error processing throughout the book.
Though we validated the balance in this example, we did not validate the name. Names are normally quite “free form”—there’s a wide variety of acceptable name formats. Often when you fill out a form, you’re asked to limit a name to a certain number of characters. In Chapter 16, Strings and Characters: A Deeper Look, you’ll learn how to check a string
’s length, so that you can validate string
s by checking that they’re not too long.
set
and get
Accessors with Different Access ModifiersBy default, a property’s get
and set
accessors have the same access as the property—e.g., public
property Name
’s accessors are public
. It’s possible to declare the get
and set
accessors with different access modifiers. In this case, one of the accessors must implicitly have the same access as the property and the other must be explicitly declared with a more restrictive access modifier than the property. We declared the Balance
property’s set
accessor private
—this indicates that it may be used only in class Account
, not by the class’s clients. This enables us to ensure that once an Account
object exists, its balance
can be modified only by method Deposit
(and in the exercises, by method Withdraw
as well).
The benefits of data integrity are not automatic simply because instance variables are made private
—you must provide appropriate validity checking and report the errors.
set accessors that set the values of private
data should verify that the intended new values are proper; if they’re not, the set
accessors should leave the instance variables unchanged and indicate an error. We demonstrate how to indicate errors by throwing exceptions in Chapter 10.
Account
Class Deposit
MethodThe public
method Deposit
(lines 35–41) enables the client code to deposit money into an Account
, thus increasing its balance
. Line 35 indicates that
the method does not return any information to its caller (as indicated by the return type void
) and
receives one parameter named depositAmount
—a decimal
value.
The depositAmount
is added to the balance
only if the parameter’s value is valid—that is, it’s greater than 0.0m
as specified in line 37. Line 39 first adds the current Balance
and depositAmount
, forming a temporary sum which is then assigned to the Balance
property. The property’s set
accessor then replaces instance variable balance
’s prior value (recall that addition has a higher precedence than the assignment operator). So line 39 uses the Balance
property’s get
accessor on the right side of the assignment and the set
accessor on the left side. It’s important to understand that the calculation on the right side of the assignment operator in line 39 does not modify the instance variable balance
—that’s why the assignment is necessary.
When implementing a method of a class, although it’s possible for the method to access the class’s instance variables directly, always use the class’s properties for that purpose. In Chapter 10, we’ll take a deeper look at this issue.
AccountTest
Class That Uses Account
Objects with BalancesClass AccountTest
(Fig. 4.12) creates two Account
objects (lines 9–10) and initializes them with a valid balance of 50.00m
and an invalid balance of -7.53m
, respectively—for the purpose of our examples, we assume that balances must be greater than or equal to zero. The calls to method Console.WriteLine
in lines 13–16 output the account names and initial balances, which are obtained from each Account
’s Name
and Balance
properties.
Account
Objects’ Initial BalancesWhen line 14 accesses account1
’s Balance
property, the value of account1
’s balance
instance variable is returned from line 21 of Fig. 4.11 and inserted into the interpolated string
at line 14 of Fig. 4.12 for display. Similarly, when line 16 accesses account2
’s Balance
property, the value of account2
’s balance
instance variable is returned from line 21 of Fig. 4.11 and inserted into the interpolated string
at line 16 of Fig. 4.12 for display. The balance
of account2
is initially 0.00
, because the constructor rejected the attempt to start account2
with a negative balance
, so the balance
instance variable retains its default initial value.
string
Interpolation Expressions with Formatting6 This app displays each Account
’s balance
as a monetary amount. You can specify formatting in a C# 6 string
interpolation expression by following the value in the braces with a colon and a format specifier. For example, in line 14, the interpolation expression
{account1.Balance:C}
uses the format specifier C to format account1.Balance
as currency. The Windows culture settings on the user’s machine determine the format for displaying currency amounts, such as the commas vs. periods for separating thousands, millions, etc. For example,
50
displays as $50.00
in the United States (U.S.), as 50,00€
(€
for euros) in Germany and as ¥50
in Japan.
4382.51
displays as $4,382.51
in the U.S., as 4.382,51 €
in Germany and as ¥4,382
in Japan.
1254827.40
displays as $1,254,827.40
in the U.S., as 1.254.827,40€
in Germany and as ¥1,254,827
in Japan.
Figure 4.13 lists additional format specifiers.
string
format specifiers.Format specifier | Description |
---|---|
C or c |
Formats the string as currency. Includes an appropriate currency symbol ($ in the U.S.) next to the number. Separates digits with an appropriate separator character (in the U.S. its a comma between every three digits for thousands, millions, etc.) and sets the number of decimal places to two by default. |
D or d |
Formats the string as a whole number (integer types only). |
N or n |
Formats the string with a thousands separator and a default of two decimal places. |
E or e |
Formats the number using scientific notation with a default of six decimal places. |
F or f |
Formats the |
G or g |
Formats the number normally with decimal places or using scientific notation, depending on context. If a format item does not contain a format specifier, format G is assumed implicitly. |
X or x |
Formats the string as hexadecimal (base 16 numbers; we discuss these in the online Number Systems appendix). |
decimal
Value from the UserLine 19 (Fig. 4.12) prompts the user to enter a deposit amount for account1
. Line 20 declares local variable depositAmount
to store each deposit amount entered by the user. Unlike instance variables (such as name
and balance
in class Account)
, local variables (like depositAmount
in Main
) are not initialized by default, so they normally must be initialized explicitly. As you’ll learn momentarily, variable depositAmount
’s initial value will be determined by the user’s input.
The C# compiler will issue the compilation error " Use of unassigned local variable 'variableName' " if you attempt to use the value of an uninitialized local variable—in the error message, variableName will be the actual variable name. This helps you avoid dangerous execution-time logic errors. It’s always better to get the errors out of your programs at compilation time rather than execution time.
Line 20 obtains the input from the user by calling the Console
class’s ReadLine
method, then passing the string
entered by the user to type decimal
’s Parse
method, which returns the decimal
value in this string
—each simple type has a Parse
method. Lines 21–22 display the deposit amount in currency format.
Line 23 calls object account1
’s Deposit
method and supplies depositAmount
as the method’s argument. The method then adds the parameter’s value to the Balance
property (line 39 of Fig. 4.11). Then lines 26–29 in Fig. 4.12 output the balances of both Account
s again to show that only account1
’s balance
instance variable changed.
decimal
Value and Depositing into account2
Line 32 prompts the user to enter a deposit amount for account2
. Line 33 obtains the input from the user by calling method Console.ReadLine
and passing the return value to type decimal
’s Parse
method. Lines 34–35 display the deposit amount. Line 36 calls object account2
’s Deposit
method and supplies depositAmount
as the method’s argument. Then, the method adds that value to account2
’s Balance
property. Finally, lines 39–42 output the Balance
s of both Account
s again to show that only account2
’s balance
instance variable changed.
Main
The six statements at lines 13–14, 15–16, 26–27, 28–29, 39–40 and 41–42 are almost identical—they each output an Account
’s name
and balance
. They differ only in the name of the Account
object—account1
or account2
. Duplicate code like this can create code-maintenance problems when it needs to be updated—if six copies of the same code require an error correction or update, you must make that change six times, without making errors. Exercise 4.13 asks you to modify Fig. 4.12 to include a DisplayAccount
method that takes as a parameter an Account
object and outputs the object’s name
and balance
, and contains only one copy of the output code. You’ll then replace Main
’s duplicated statements with six calls to DisplayAccount
.
Replacing duplicated code with calls to a method that contains one copy of that code can reduce the size of your program and improve its maintainability.
Account
The class diagram in Fig. 4.14 concisely models class Account
of Fig. 4.11. The diagram models in its second compartment the public
properties Name
of type string
and Balance
of type decimal
. Class Account
’s constructor is modeled in the third compartment with parameters name
of type string
and initialBalance
of type decimal
. The class’s public Deposit
operation also is modeled in the third compartment—Deposit
has a depositAmount
parameter of type decimal
. Method Deposit
does not return a value (because it returns void
in C#), so the UML class diagram does not specify a return type for this operation.