Time
Class Case Study; Throwing ExceptionsOur first example consists of classes Time1
(Fig. 10.1) and Time1Test
(Fig. 10.2). Class Time1
represents the time of day.1 Class Time1Test
’s Main
method creates an object of class Time1
and invokes its methods. The output of this app appears in Fig. 10.2.
Time1
Class DeclarationClass Time1
contains three public
properties of type int
—Hour
, Minute
and Second
(Fig. 10.1, lines 7–9). These represent the time in universal-time format (24-hour clock format, in which hours are in the range 0–23). Class Time1
contains public
methods Set-Time
(lines 13–25), ToUniversalString
(lines 28–29) and ToString
(lines 32–34). These are the public
services or the public
interface that this class provides to its clients. In this example, class Time1
does not declare a constructor, so the compiler defines a default constructor. Each property receives the default value 0
for an int
. Instance variables and auto-implemented properties also can be assigned values in their declarations.
public
ClassIn Fig. 10.1, we declared class Time1
as a public
class, meaning that it potentially can be reused in other projects. Although we use class Time1
only in this project, from this point forward, we’ll declare as public
any class that could potentially be reused in another project.
SetTime
and Throwing ExceptionsMethod SetTime
(lines 13–25) is a public
method that declares three int
parameters and uses them to set the time. Lines 16–17 test each argument to determine whether the value is out of range. If all the values are in range, lines 22–24 assign the values to the Hour
, Minute
and Second
properties. The hour
(line 13) must be in the range 0
to 23
, because universal-time format represents hours as integers from 0 to 23 (e.g., 1 PM is hour 13 and 11 PM is hour 23; midnight is hour 0 and noon is hour 12). Similarly, both minute
and second
values must be in the range 0
to 59
. For values outside these ranges, line 19 throws an exception of type ArgumentOutOfRangeException
(namespace System
), which notifies the client code that an invalid argument was passed to the method. As you learned in Chapter 8, you can use try
...catch
to catch exceptions and attempt to recover from them, which we’ll do in Fig. 10.2. The throw
statement (line 19) creates a new object of type ArgumentOutOfRangeException
. The parentheses following the class name indicate a call to the ArgumentOutOfRangeException
constructor. After the exception object is created, the throw
statement immediately terminates method SetTime
and the exception is returned to the code that attempted to set the time, where it can be caught and dealt with.
ToUniversalString
Method ToUniversalString
(lines 28–29) is an expression-bodied method—recall this is a shorthand notation for a method that contains only a return
statement. The method takes no arguments and returns a string
in universal-time format, consisting of six digits—two for the hour, two for the minute and two for the second. For example, if the time were 1:30:07 PM, method ToUniversalString
would return 13:30:07
. The method implicitly returns the value of the string
-interpolation expression in line 29. The D2
format specifier formats an integer with two digits and, where needed, a leading 0
if the integer has fewer than two digits.
ToString
Method ToString
(lines 32–34) is an expression-bodied method that takes no arguments and returns a string
in which the Hour
, Minute
and Second
values are separated by colons and followed by an AM or PM indicator (e.g., 1:27:06
PM
). Like method ToUniversal-String
, method ToString
implicitly returns the value of a string
-interpolation expression. In this case, we do not format the Hour
, but we format the Minute
and Second
as two-digit values with leading 0
s, if necessary. Line 33 uses a conditional operator (?:
) to determine the value for Hour
in the string—if the hour
is 0
or 12
(AM or PM), it appears as 12—otherwise, it appears as a value from 1 to 11. The conditional operator in line 34 determines whether AM or PM will be inserted in the string
.
Time1
The Time1Test
app class (Fig. 10.2) uses class Time1
. Line 10 creates a Time1
object and assigns it to local variable time
. Operator new
invokes class Time1
’s default constructor, since Time1
does not declare any constructors. Lines 13–17 output the time, first in universal-time format (by invoking time
’s ToUniversalString
method in line 14), then in standard-time format (by explicitly invoking time
’s ToString
method in line 16) to confirm that the Time1
object was initialized properly. Line 20 invokes method SetTime
of the time
object to change the time. Then lines 21–24 output the time again in both formats to confirm that the time was set correctly.
Time
Method SetTime
with Invalid ValuesTo illustrate that method SetTime
validates its arguments, line 30 calls method SetTime
with invalid arguments of 99
for the hour
, minute
and second
. This statement is placed in a try
block (lines 28–31) in case SetTime
throws an ArgumentOutOfRangeException
, which it will do since the arguments are all invalid. When this occurs, the exception is caught at lines 32–35 and the exception’s Message
property is displayed. Lines 38–40 output the time again in both formats to confirm that SetTime
did not change the time when invalid arguments were supplied.
Time1
Class DeclarationConsider several class-design issues with respect to class Time1
. The time is represented as three integers for the hour, minute and second. However, the actual data representation used within the class is of no concern to the class’s clients. For example, it would be perfectly reasonable for Time1
to represent the time internally as the number of seconds since midnight or the number of minutes and seconds since midnight. Clients could use the same public
methods and properties to get the same results without being aware of this—of course, the Hour
, Minute
and Second
properties would need to be reimplemented to work with the new data representation. (Exercise 10.4 asks you to represent the time as the number of seconds since midnight and show that indeed no change is visible to the clients of the class.)
Classes simplify programming because the client can use only the public
members exposed by the class. Such members are usually client oriented rather than implementation oriented. Clients are neither aware of, nor involved in, a class’s implementation. Clients generally care about what the class does but not how the class does it. Clients do, of course, care that the class operates correctly and efficiently.
Interfaces change less frequently than implementations. When an implementation changes, implementation-dependent code must change accordingly. Hiding the implementation reduces the possibility that other parts of the app become dependent on class-implementation details.
Date and time manipulations are more complex than the simplified classes we use in this book. For applications that require date and time processing, check out .NET’s DateTimeOffest
, DateTime
, TimeSpan
and TimeZoneInfo
value types in namespace System
.