Section 8.3. Returning Multiple Values


8.3. Returning Multiple Values

Methods can return only a single value, but this isn't always convenient . Let's return to the Time class. It would be great to create a GetTime( ) method to return the hour , minutes, and seconds. You can't return all three of these as return values, but perhaps you can pass in three parameters, let the GetTime( ) method modify the parameters, and then examine the result in the calling methodin this case, Run( ) . Example 8-3 is a first attempt.

Example 8-3. Retrieving multiple values, first attempt
 using System; namespace PassByRef {    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 );       }       public void GetTime(          int theHour,          int theMinute,          int theSecond )       {          theHour = Hour;          theMinute = Minute;          theSecond )= Second;       }       // constructor       public Time( System.DateTime dt )       {          Year = dt.Year;          Month = dt.Month;          Date = dt.Day;          Hour = dt.Hour;          Minute = dt.Minute;          Second = dt.Second;       }    }    class Tester    {       public void Run()       {          System.DateTime currentTime = System.DateTime.Now;          Time t = new Time( currentTime );          t.DisplayCurrentTime();          int theHour = 0;          int theMinute = 0;          int theSecond = 0;          t.GetTime( theHour, theMinute, theSecond );          System.Console.WriteLine( "Current time: {0}:{1}:{2}",          theHour, theMinute, theSecond );       }       static void Main()       {          Tester t = new Tester();          t.Run();       }    } } 

The output will look something like this:

 7/1/2008 12:22:19  Current time: 0:0:0  

Notice that the "Current time" in the output is 0:0:0. Clearly, this first attempt did not work. The problem is with the parameters. You pass in three integer parameters to GetTime( ) , and you modify the parameters in GetTime( ) , but when the values are accessed back in Run( ) , they are unchanged. This is because integers are value types.

8.3.1. Passing Value Types by Reference

As discussed in Chapter 7, C# divides the world of types into value types and reference types. All intrinsic types (such as int and long ) are value types. Instances of classes (objects) are reference types.

When you pass a value type (such as an int ) into a method, a copy is made. When you make changes to the parameter, you make changes to the copy. Back in the Run( ) method, the original integer variables theHour , theMinute , and theSecond are unaffected by the changes made in GetTime( ) .

What you need is a way to pass in the integer parameters by reference so that changes made in the method are made to the original object in the calling method. When you pass an object by reference, the parameter refers to the same object. Thus when you make changes in GetTime( ) , the changes are also made to the original variables in Run( ) .

Please ignore this note as it is advanced, confusing, and put here just to cut down on my email. Technically , when you pass a reference type, it is in fact passed by value; but the copy that is made is a copy of a reference, and thus that copy points to the same (unnamed) object on the heap as did the original reference object. That is how you achieve the semantics of "pass by reference" in C# using pass by value.

This is the last time I'll point out this esoteric idea as it is perfectly reasonable to consider the reference to be the object. It takes too long to refer to Fido as a "reference to an unnamed Dog object on the heap"; it is easier to say Fido is a Dog object, and it is reasonable to imagine that Fido is passed by reference, even though (technically) we know better.

Not only is this shorthand reasonable, it is how most professional programmers think and talk about it. In most cases, it comes down to a distinction without a meaningful difference.


This requires two small modifications to the code in Example 8-3. First, change the parameters of the GetTime( ) method to indicate that the parameters are ref (reference) parameters:

 public void GetTime(        ref int theHour,        ref int theMinute,        ref int theSecond )     {        theHour = Hour;        theMinute = Minute;        theSecond = Second;     } 

Second, modify the call to GetTime( ) to pass the arguments as references:

 t.GetTime(ref theHour, ref theMinute, ref theSecond); 

If you leave out the second step of marking the arguments with the keyword ref , the compiler will complain that the argument cannot be converted from an int to a ref int .


These changes are shown in Example 8-4.

Example 8-4. Passing by reference
 using System; namespace PassByRef {    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 );       }       // takes references to ints       public void GetTime(          int theHour,          int theMinute,          int theSecond )       {          theHour = Hour;          theMinute = Minute;          theSecond = Second;       }       // constructor       public Time( System.DateTime dt )       {          Year = dt.Year;          Month = dt.Month;          Date = dt.Day;          Hour = dt.Hour;          Minute = dt.Minute;          Second = dt.Second;       }    }    class Tester    {       public void Run()       {          System.DateTime currentTime = System.DateTime.Now;          Time t = new Time( currentTime );          t.DisplayCurrentTime();          int theHour = 0;          int theMinute = 0;          int theSecond = 0;          // pass the ints by reference  t.GetTime( ref theHour, ref theMinute, ref theSecond );  System.Console.WriteLine( "Current time: {0}:{1}:{2}",          theHour, theMinute, theSecond );       }       static void Main()       {          Tester t = new Tester();          t.Run();       }    } } 

This time, the output looks like this:

 7/1/2008 12:25:41  Current time: 12:25:41  

The results now show the correct time.

By declaring these parameters to be ref parameters, you instruct the compiler to pass them by reference. Instead of a copy being made, the parameters in GetTime( ) are references to the corresponding variables ( theHour , theMinute , theSecond ) that were created in Run( ) . When you change these values in GetTime( ) , the change is reflected in Run( ) .

Keep in mind that ref parameters are references to the actual original valueit is as if you said, "here, work on this one." Conversely, value parameters are copiesit is as if you said, "here, work on one just like this."

8.3.2. out Parameters and Definite Assignment

As noted in Chapter 4, C# imposes definite assignment , which requires that all variables be assigned a value before they are used. In Example 8-4, you initialize theHour , theMinute , and theSecond before you pass them as parameters to GetTime( ) , yet the initialization merely sets their values to 0 before they are passed to the method:

 int theHour = 0;     int theMinute = 0;     int theSecond = 0;     t.GetTime( ref theHour, ref theMinute, ref theSecond); 

It seems silly to initialize these values because you immediately pass them by reference into GetTime( ) where they'll be changed, but if you don't, the following compiler errors are reported :

 Use of unassigned local variable 'theHour'     Use of unassigned local variable 'theMinute'     Use of unassigned local variable 'theSecond' 

C# provides the out modifier for situations like this, in which initializing a parameter is only a formality . The out modifier removes the requirement that a reference parameter be initialized . The parameters to GetTime( ) , for example, provide no information to the method; they are simply a mechanism for getting information out of it. Thus, by marking all three as out parameters using the out keyword, you eliminate the need to initialize them outside the method.

Within the called method, the out parameters must be assigned a value before the method returns. Here are the altered parameter declarations for GetTime( ) :

 public void GetTime(        out int theHour,        out int theMinute,        out int theSecond )     {        theHour = Hour;        theMinute = Minute;        theSecond = Second;     } 

Here is the new invocation of the method in Main( ) :

 int theHour;     int theMinute;     int theSecond;     t.GetTime( out theHour, out theMinute, out theSecond); 

The keyword out implies the same semantics as the keyword ref , except that it also allows you to use the variable without first initializing it in the calling method.



Learning C# 2005
Learning C# 2005: Get Started with C# 2.0 and .NET Programming (2nd Edition)
ISBN: 0596102097
EAN: 2147483647
Year: 2004
Pages: 250

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net