In Chapter 7, you saw that classes consist of fields and methods . Fields hold the state of the object, and methods define the object’s behavior.
In this chapter, you’ll explore how methods work in more detail. You’ve already seen how to create methods, and in this chapter, you’ll learn about method overloading , a technique that allows you to create more than one method with the same name. This enables your clients to invoke the method with different parameter types.
This chapter also introduces properties. To clients of your class, properties look like member variables, but properties are implemented as methods. This allows you to maintain good data-hiding, while providing your clients with convenient access to the state of your class.
Chapter 7 described the
difference between value types (such as int
and long
)
and reference types. The most common value types are the “built-in” or
“primitive” types (as well as structs), while the most common reference
types are the user-defined types. This chapter explores the implications
of passing value types to methods and shows how you can pass value types
by reference, allowing the called method to act on
the original object in the calling method.
Often you’ll want to have more than one method with the same name.
The most common example of this is to have more than one constructor
with the same name, which allows you to create the object with different
types of parameters, or a different number of parameters. For example,
if you were creating a Time
object,
you might have circumstances where you want to create the Time
object by passing in the date, hours,
minutes, and seconds. Other times, you might want to create a Time
object by passing in an existing Time
object. Still other times, you might want
to pass in just a date, without hours and minutes. Overloading the
constructor allows you to provide these various options.
Chapter 7 explained that
your constructor is automatically invoked when your object is created.
Let’s return to the Time
class
created in that chapter, to the client who could create a Time
object by passing in a DateTime
object to the constructor.
The DateTime
object is an
object that’s built into the System
library, with many of the same data members as your custom Time
class. In short, having DateTime
means you probably won’t ever
create your own Time
class, but
we’re using our custom Time
class
as an example of many of the issues that arise in creating
classes.
It would be convenient also to allow the client to create a new
Time
object by passing in year,
month, date, hour, minute, and second values. Some clients might prefer
one or the other constructor; you can provide both, and the client can
decide which better fits the situation.
In order to overload your constructor, you must make sure that each constructor has a unique signature. The signature of a method is composed of its name and its parameter list. Two methods differ in their signatures if they have different names or different parameter lists. Parameter lists can differ by having different numbers or types of parameters. The following four lines of code show how you might distinguish methods by signature:
void MyMethod(int p1); void MyMethod(int p1, int p2); // different number void MyMethod(int p1, string s1); // different types void SomeMethod(int p1); // different name
The first three methods are all overloads of the MyMethod( )
method. The first differs from the
second and third in the number of parameters. The second closely
resembles the third version, but the second parameter in each is a
different type. In the second method, the second parameter (p2
) is an integer; in the third method, the
second parameter (s1
) is a string.
These changes to the number or type of parameters are sufficient changes
in the signature to allow the compiler to distinguish the
methods.
The fourth method differs from the other three methods by having a different name. This is not method overloading, just different methods, but it illustrates that two methods can have the same number and type of parameters if they have different names. Thus, the fourth method and the first have the same parameter list, but their names are different.
A class can have any number of methods, as long as each one’s
signature differs from that of all the others. Example 8-1 illustrates a
Time
class with two
constructors : one that takes a DateTime
object and one that takes six
integers.
Example 8-1. Overloading a method
using System; namespace MethodOverloading { public class Time { // private member variables private int Year; private int Month; private int Date; private int Hour; private int Minute; private int Second; // public accessor methods public void DisplayCurrentTime( ) { System.Console.WriteLine( "{0}/{1}/{2} {3}:{4}:{5}", Month, Date, Year, Hour, Minute, Second ); } // constructors public Time( System.DateTime dt ) { Year = dt.Year; Month = dt.Month; Date = dt.Day; Hour = dt.Hour; Minute = dt.Minute; Second = dt.Second; } public Time( int Year, int Month, int Date, int Hour, int Minute, int Second ) { this.Year = Year; this.Month = Month; this.Date = Date; this.Hour = Hour; this.Minute = Minute; this.Second = Second; } } class Tester { public void Run( ) { System.DateTime currentTime = System.DateTime.Now; Time time1 = new Time( currentTime ); time1.DisplayCurrentTime( ); Time time2 = new Time( 2000, 11, 18, 11, 03, 30 ); time2.DisplayCurrentTime( ); } static void Main( ) { Tester t = new Tester( ); t.Run( ); } } }
The output looks like this:
7/10/2008 16:17:32 11/18/2000 11:3:30
Note that the minutes in the second output show as 3 rather than 03. You can fix this by formatting the output string, left as an exercise for the user.
If a function’s signature consisted only of the function name, the
compiler would not know which constructors to call when constructing the
new Time
objects, time1
and time2
. However, because the signature includes
the parameters and their types, the compiler is able to match the
constructor call for time1
with the
constructor whose signature requires a DateTime
object:
System.DateTime currentTime = System.DateTime.Now; Time time1 = new Time(currentTime); public Time(System.DateTime dt)
Likewise, the compiler is able to associate the time2
constructor call with the constructor
whose signature specifies six integer arguments.
Time time2 = new Time(2000,11,18,11,03,30); public Time(int Year, int Month, int Date, int Hour, int Minute, int Second)
When you overload a method, you must change the signature (the name, number, or type of the parameters). You are free, as well, to change the return type, but this is optional. Changing only the return type does not overload the method, and creating two methods with the same signature but differing return types generates a compile error.