Time Class Case Study: Overloaded Constructors

Một phần của tài liệu Visual C 2012 How to Program _ www.bit.ly/taiho123 (Trang 420 - 426)

2. When the app executes, another compiler (known as the just-in-time compiler

10.5 Time Class Case Study: Overloaded Constructors

ClassSimpleTimedeclares threeprivateinstance variables—hour,minuteandsecond (lines 17–19). The constructor (lines 24–29) receives threeintarguments to initialize a

SimpleTimeobject. For the constructor we used parameter names that are identical to the class’s instance-variable names (lines 17–19). We don’t recommend this practice, but we intentionally did it here to hide the corresponding instance variables so that we could illus- trate explicit use of thethisreference. Recall from Section 7.11 that if a method contains a local variable with thesamename as a field, within that method the name refers to the local variable rather than the field. In this case, the parameterhidesthe field in the method’s scope. However, the method can use thethisreference to refer to the hidden instance vari- able explicitly, as shown in lines 26–28 forSimpleTime’s hidden instance variables.

MethodBuildString(lines 32–37) returns astringcreated by a statement that uses thethisreference explicitly and implicitly. Line 35 uses thethisreferenceexplicitlyto call methodToUniversalString. Line 36 uses thethisreferenceimplicitlyto call the same method. Programmers typically do not use thethisreference explicitly to reference other methods in the current object. Also, line 46 in methodToUniversalStringexplicitly uses thethisreference to access each instance variable. This is not necessary here, because the method doesnothave any local variables that hide the instance variables of the class.

Class ThisTest (lines 5–12) demonstrates class SimpleTime. Line 9 creates an instance of classSimpleTime and invokes its constructor. Line 10 invokes the object’s

BuildStringmethod, then displays the results.

10.5 Time Class Case Study: Overloaded Constructors

Next, we demonstrate a class with severaloverloaded constructorsthat enable objects of that class to be conveniently initialized in different ways. To overload constructors, simply provide multiple constructor declarations with different signatures.

ClassTime2with Overloaded Constructors

By default, instance variableshour,minuteandsecondof classTime1(Fig. 10.1) are initial- ized to their default values of0—midnight in universal time. ClassTime1doesn’t enable the class’s clients to initialize the time with specific nonzero values. ClassTime2(Fig. 10.5) con-

Common Programming Error 10.1

It’s often a logic error when a method contains a parameter or local variable that has the same name as an instance variable of the class. In such a case, use referencethisif you wish to access the instance variable of the class—otherwise, the method parameter or local variable will be referenced.

Error-Prevention Tip 10.1

Avoid method-parameter names or local-variable names that conflict with field names.

This helps prevent subtle, hard-to-locate bugs.

Performance Tip 10.1

C# conserves memory by maintainingonly onecopy of each method per class—this method is invoked by every object of the class. Each object, on the other hand, has itsowncopy of the class’s instance variables (i.e., non-staticvariables). Each non-staticmethod of the class implicitly uses thethisreference to determine the specific object of the class to manipulate.

tains overloaded constructors. In this app, one constructor invokes the other constructor, which in turn callsSetTimeto set thehour,minuteandsecond. The compiler invokes the appropriateTime2constructor by matching the number and types of the arguments speci- fied in the constructor call with the number and types of the parameters specified in each constructor declaration.

1 // Fig. 10.5: Time2.cs

2 // Time2 class declaration with overloaded constructors.

3 using System; // for class ArgumentOutOfRangeException 4

5 public class Time2 6 {

7 private int hour; // 0 - 23 8 private int minute; // 0 - 59 9 private int second; // 0 - 59 10

11 // constructor can be called with zero, one, two or three arguments 12

13 14 15 16

17 // Time2 constructor: another Time2 object supplied as an argument 18

19 20

21 // set a new time value using universal time; ensure that

22 // the data remains consistent by setting invalid values to zero 23 public void SetTime( int h, int m, int s )

24 {

25 Hour = h; // set the Hour property 26 Minute = m; // set the Minute property 27 Second = s; // set the Second property 28 } // end method SetTime

29

30 // property that gets and sets the hour 31 public int Hour

32 {

33 get

34 {

35 return hour;

36 } // end get

37 set

38 {

39 if ( value >= 0 && value < 24 )

40 hour = value;

41 else

42 throw new ArgumentOutOfRangeException(

43 "Hour", value, "Hour must be 0-23" );

44 } // end set 45 } // end property Hour

Fig. 10.5 | Time2class declaration with overloaded constructors. (Part 1 of 2.)

public Time2( int h = 0, int m = 0, int s = 0 ) {

SetTime( h, m, s ); // invoke SetTime to validate time } // end Time2 three-argument constructor

public Time2( Time2 time )

: this( time.Hour, time.Minute, time.Second ) { }

10.5 TimeClass Case Study: Overloaded Constructors 381

46

47 // property that gets and sets the minute 48 public int Minute

49 {

50 get

51 {

52 return minute;

53 } // end get

54 set

55 {

56 if ( value >= 0 && value < 60 )

57 minute = value;

58 else

59 throw new ArgumentOutOfRangeException(

60 "Minute", value, "Minute must be 0-59" );

61 } // end set

62 } // end property Minute 63

64 // property that gets and sets the second 65 public int Second

66 {

67 get

68 {

69 return second;

70 } // end get

71 set

72 {

73 if ( value >= 0 && value < 60 )

74 second = value;

75 else

76 throw new ArgumentOutOfRangeException(

77 "Second", value, "Second must be 0-59" );

78 } // end set

79 } // end property Second 80

81 // convert to string in universal-time format (HH:MM:SS) 82 public string ToUniversalString()

83 {

84 return string.Format(

85 "{0:D2}:{1:D2}:{2:D2}", Hour, Minute, Second );

86 } // end method ToUniversalString 87

88 // convert to string in standard-time format (H:MM:SS AM or PM) 89 public override string ToString()

90 {

91 return string.Format( "{0}:{1:D2}:{2:D2} {3}", 92 ( ( Hour == 0 || Hour == 12 ) ? 12 : Hour % 12 ), 93 Minute, Second, ( Hour < 12 ? "AM" : "PM" ) );

94 } // end method ToString 95 } // end class Time2

Fig. 10.5 | Time2class declaration with overloaded constructors. (Part 2 of 2.)

ClassTime2’s Parameterless Constructor

Lines 12–15 declare a constructor with threedefault parameters. This constructor is also considered to be the class’sparameterless constructorbecause you can call the constructor without arguments and the compiler will automatically provide the default parameter val- ues. This constructor can also be called with one argument for the hour, two arguments for the hour and minute, or three arguments for the hour, minute and second. This con- structor callsSetTimeto set the time.

ClassTime2’s Constructor That Receives a Reference to Another Time2 Object Lines 18–19 declare aTime2constructor that receives a reference to aTime2object. In this case, the values from theTime2argument are passed to the three-parameter constructor at lines 12–15 to initialize thehour,minuteandsecond. In this constructor, we usethisin a manner that’s allowedonlyin the constructor’s header. In line 19, the usual constructor header is followed by a colon (:), then the keywordthis. Thethisreference is used in method-call syntax (along with the threeintarguments) to invoke theTime2constructor that takes threeintarguments (lines 12–15). The constructor passes the values of thetime

argument’sHour,MinuteandSecondproperties to set thehour,minuteandsecondof the

Time2object being constructed. Additional initialization code can be placed in this con- structor’s body and it will execute after the other constructor is called.

Constructor Initializers

The use of thethisreference as shown in line 19 is called aconstructor initializer. Con- structor initializers are a popular way to reuse initialization code provided by one of the class’s constructors rather than defining similar code in another constructor’s body. This syntax makes the class easier to maintain, because one constructorreusesthe other. If we needed to change how objects of classTime2are initialized, only the constructor at lines 12–15 would need to be modified. Even that constructor might not need modification—

it simply calls theSetTimemethod to perform the actual initialization, so it’s possible that the changes the class might require would be localized to this method.

Line 19 could have directly accessed instance variableshour,minuteandsecondof the constructor’s time argument with the expressions time.hour, time.minute and

time.second—even though they’re declared asprivatevariables of classTime2.

ClassTime2’sSetTimeMethod

MethodSetTime(lines 23–28) invokes thesetaccessors of the new propertiesHour(lines 31–45),Minute(lines 48–62) andSecond(lines 65–79), which ensure that the value sup- plied forhouris in the range 0 to 23 and that the values forminuteandsecondare each in the range 0 to 59. If a value is out of range, eachsetaccessor throws anArgumentOutOf-

Common Programming Error 10.2

A constructor can call methods of its class. Be aware that the instance variables might not yet be initialized, because the constructor is in the process of initializing the object. Using instance variables before they have been initialized properly is a logic error.

Software Engineering Observation 10.3

When one object of a class has a reference to another object of the same class, the first object can accessallthe second object’s data and methods (including those that areprivate).

10.5 TimeClass Case Study: Overloaded Constructors 383

RangeException(lines 42–43, 59–60 and 76–77). In this example, we use theArgument-

OutOfRangeExceptionconstructor that receives three arguments—the name of the item that was out of range, the value that was supplied for that item and an error message.

Notes Regarding ClassTime2’s Methods, Properties and Constructors

Time2’s properties are accessed throughout the class’s body. MethodSetTimeassigns val- ues to propertiesHour,MinuteandSecondin lines 25–27, and methodsToUniversal- StringandToStringuse propertiesHour,MinuteandSecondin line 85 and lines 92–93, respectively. These methods could have accessed the class’sprivatedata directly. Howev- er, consider changing the representation of the time from threeintvalues (requiring 12 bytes of memory) to a singleintvalue representing the total number of seconds that have elapsed since midnight (requiring only 4 bytes of memory). If we make such a change, only the bodies of the methods that access theprivatedata directly would need to change—

in particular, the individual propertiesHour,MinuteandSecond. There would be no need to modify the bodies of methodsSetTime,ToUniversalStringorToString, because they do not access theprivate data directly. Designing the class in this manner reduces the likelihood of programming errors when altering the class’s implementation.

Similarly, each constructor could be written to include a copy of the appropriate state- ments from methodSetTime. Doing so may be slightly more efficient, because the extra constructor call and the call toSetTimeare eliminated. However, duplicating statements in multiple methods or constructors makes changing the class’s internal data representa- tion more difficult and error-prone. Having one constructor call the other or even call

SetTimedirectly requires any changes toSetTime’s implementation to be made only once.

Using ClassTime2’s Overloaded Constructors

ClassTime2Test(Fig. 10.6) creates sixTime2objects (lines 9–13 and 42) to invoke the over- loadedTime2constructors. Lines 9–13 demonstrate passing arguments to theTime2con- structors. C# invokes the appropriate overloaded constructor by matching the number and types of the arguments specified in the constructor call with the number and types of the pa- rameters specified in each constructor declaration. Lines 9–12 each invoke the constructor at lines 12–15 of Fig. 10.5. Line 9 of Fig. 10.6 invokes the constructor with no arguments, which causes the compiler to supply the default value0for each of the three parameters. Line 10 invokes the constructor with one argument that represents the hour—the compiler sup- plies the default value0for the minute and second. Line 11 invokes the constructor with two arguments that represent the hour and minute—the compiler supplies the default value0for the second. Line 12 invoke the constructor with values for all three parameters. Line 13 in- vokes the constructor at lines 18–19 of Fig. 10.5. Lines 16–37 display thestringrepresen- tation of each initializedTime2object to confirm that each was initialized properly.

Line 42 attempts to intializet6 by creating a newTime2 object and passing three invalid values to the constructor. When the constructor attempts to use the invalid hour value to initialize the object’sHourproperty, anArgumentOutOfRangeExceptionoccurs.

We catch this exception at line 44 and display itsMessageproperty, which results in the last three lines of the output in Fig. 10.6. Because we used the three-argumentArgument-

Software Engineering Observation 10.4

When implementing a method of a class, using the class’s properties to access the class’s

privatedata simplifies code maintenance and reduces the likelihood of errors.

OutOfRangeExceptionconstructor when the exception object was created, the exception’s

Messageproperty also includes theinformation about the out-of-range value.

1 // Fig. 10.6: Time2Test.cs

2 // Overloaded constructors used to initialize Time2 objects.

3 using System;

4

5 public class Time2Test 6 {

7 public static void Main( string[] args )

8 {

9 10 11 12 13

14 Time2 t6; // initialized later in the program 15

16 Console.WriteLine( "Constructed with:\n" );

17 Console.WriteLine( "t1: all arguments defaulted" );

18 Console.WriteLine( " {0}", t1.ToUniversalString() ); // 00:00:00 19 Console.WriteLine( " {0}\n", t1.ToString() ); // 12:00:00 AM 20

21 Console.WriteLine(

22 "t2: hour specified; minute and second defaulted" );

23 Console.WriteLine( " {0}", t2.ToUniversalString() ); // 02:00:00 24 Console.WriteLine( " {0}\n", t2.ToString() ); // 2:00:00 AM 25

26 Console.WriteLine(

27 "t3: hour and minute specified; second defaulted" );

28 Console.WriteLine( " {0}", t3.ToUniversalString() ); // 21:34:00 29 Console.WriteLine( " {0}\n", t3.ToString() ); // 9:34:00 PM 30

31 Console.WriteLine( "t4: hour, minute and second specified" );

32 Console.WriteLine( " {0}", t4.ToUniversalString() ); // 12:25:42 33 Console.WriteLine( " {0}\n", t4.ToString() ); // 12:25:42 PM 34

35 Console.WriteLine( "t5: Time2 object t4 specified" );

36 Console.WriteLine( " {0}", t5.ToUniversalString() ); // 12:25:42 37 Console.WriteLine( " {0}", t5.ToString() ); // 12:25:42 PM 38

39 // attempt to initialize t6 with invalid values

40 try

41 {

42

43 } // end try

44 catch ( ArgumentOutOfRangeException ex )

45 {

46 Console.WriteLine( "\nException while initializing t6:" );

47 Console.WriteLine( ex.Message );

48 } // end catch 49 } // end Main

50 } // end class Time2Test

Fig. 10.6 | Overloaded constructors used to initializeTime2objects. (Part 1 of 2.)

Time2 t1 = new Time2(); // 00:00:00 Time2 t2 = new Time2( 2 ); // 02:00:00 Time2 t3 = new Time2( 21, 34 ); // 21:34:00 Time2 t4 = new Time2( 12, 25, 42 ); // 12:25:42 Time2 t5 = new Time2( t4 ); // 12:25:42

t6 = new Time2( 27, 74, 99 ); // invalid values

Một phần của tài liệu Visual C 2012 How to Program _ www.bit.ly/taiho123 (Trang 420 - 426)

Tải bản đầy đủ (PDF)

(1.020 trang)