Chapter 5. Methods

The Structure of a Method

Essentially, a method is a block of code with a name. You can execute the code by using the method's name. You can pass data into a method and receive data as output.

As you saw in the previous chapter, a method is a function member of a class. Methods have two major sections, as shown in Figure 5-1—the method header and the method body.

  • The method header specifies the method's characteristics, including the following:

    • Whether the method returns data, and if so, what type

    • The name of the method

    • What types of input can be passed to the method

  • The method body contains the sequence of executable code statements. Execution starts at the first statement in the method body and continues sequentially through the method.

The structure of a method

Figure 5-1. The structure of a method

The following example shows the form of the method header. I will cover each part in the following pages.

The structure of a method

Methods can also be function members of another user-defined type called a struct, which is covered in Chapter 12. Most of what this chapter covers about class methods will also be true for struct methods.

For example, the following code shows a simple method called MyMethod, that, in turn, calls the WriteLine method several times:

void MyMethod()
   {
      Console.WriteLine("First");
      Console.WriteLine("Last");
   }

Code Execution in the Method Body

The method body is a block, which (as you will recall from Chapter 2) is a sequence of statements between curly braces. A block can contain the following items:

  • Local variables

  • Flow-of-control constructs

  • Method invocations

  • Blocks nested within it

Figure 5-2 shows an example of a method body and some of its components.

Method body example

Figure 5-2. Method body example

Local Variables

Like fields, local variables store data. While fields usually store data about the state of the object, local variables are usually created to store data for local, or transitory, computations. Table 5-1 compares and contrasts local variables and instance fields.

The following line of code shows the syntax of local variable declarations. The optional initializer consists of the equals sign followed by a value to be used to initialize the variable.

Local Variables
  • The existence of a local variable is limited to the block in which it is created and the blocks nested within it.

    • It comes into existence at the point at which it is declared.

    • It goes out of existence when the block completes execution.

  • You can declare local variables at any position in the method body.

The following example shows the declaration and use of two local variables. The first is of type int, and the second is of type SomeClass.

static void Main( )
   {
      int myInt    = 15;
      SomeClass sc = new SomeClass();
      ...
   }

Table 5-1. Instance Fields vs. Local Variables

 

Instance Field

Local Variable

Lifetime

Starts when the instance is created.

Ends when the instance is no longer accessible.

Starts at the point in the block where it is declared.

Ends when the block completes execution.

Implicit Initialization

Initialized to a default value for the type.

No implicit initialization. The compiler produces an error message if the variable is not assigned to before use.

Storage Area

All the fields of a class are stored in the heap, regardless of whether they are value types or reference types.

Value type: Stored on the stack. Reference type: Reference stored on the stack, and data stored in the heap.

Type Inference and the var Keyword

If you look at the following code, you will see that when you supply the type name at the beginning of the declaration, you are supplying information that the compiler should already be able to infer from the right-hand side of the initialization.

  • In the first variable declaration, the compiler can infer that 15 is an int.

  • In the second declaration, the object-creation expression on the right-hand side returns an object of type MyExcellentClass.

So in both cases, including the explicit type name at the beginning of the declaration is redundant.

static void Main( )
   {
      int total = 15;
      MyExcellentClass mec = new MyExcellentClass();
      ...
   }

Starting with C# 3.0 you can now use the new keyword var in place of the explicit type name at the beginning of the variable declaration, as follows:

Type Inference and the var Keyword

The var keyword does not signal a special kind of variable. It is just syntactic shorthand for whatever type can be inferred from the initialization on the right-hand side. In the first declaration, it is shorthand for int. In the second, it is shorthand for MyExcellentClass. The preceding code segment with the explicit type names and the code segment with the var keywords are semantically equivalent.

Some important conditions on using the var keyword are the following:

  • It can only be used with local variables—not with fields.

  • It can only be used when the variable declaration includes an initialization.

  • Once the compiler infers the type, it is fixed and unchangeable.

Note

The var keyword is not like the JavaScript var that can reference different types. It is shorthand for the actual type inferred from the right side of the equals sign. The var keyword does not change the strongly typed nature of C#.

Local Variables Inside Nested Blocks

Method bodies can have other blocks nested inside them.

  • There can be any number of blocks, and they can be sequential or nested further. Blocks can be nested to any level.

  • Local variables can be declared inside nested blocks, and like all local variables, their lifetime is limited to the block in which they are declared and the blocks nested within it.

Figure 5-3 illustrates the lifetimes of two local variables, showing the code and the state of the stack. The arrows indicate the line that has just been executed.

  • Variable var1 is declared in the body of the method, before the nested block.

  • Variable var2 is declared inside the nested block. It exists from the time it is declared, until the end of the block in which it was declared.

  • When control passes out of the nested block, its local variables are popped from the stack.

The lifetime of a local variable

Figure 5-3. The lifetime of a local variable

Note

In C and C++ you can declare a local variable, and then within a nested block you can declare another local variable with the same name. The inner name masks the outer name while within the inner scope. In C#, however, you cannot declare another local variable with the same name within the scope of the first name regardless of the level of nesting.

Local Constants

A local constant is much like a local variable, except that once it's initialized, its value can't be changed. Like a local variable, a local constant must be declared inside a block.

The two most important characteristics of a constant are the following:

  • A constant must be initialized at its declaration.

  • A constant cannot be changed after its declaration.

The core declaration for a constant is shown following. The syntax is the same as that of a field or variable declaration, except for the following:

  • The addition of the keyword const before the type.

  • The mandatory initializer. The initializer value must be determinable at compile time, and is usually one of the predefined simple types or an expression made up of them. It can also be the null reference, but it cannot be a reference to an object, because references to objects are determined at run time.

Note

The keyword const is not a modifier, but part of the core declaration. It must be placed immediately before the type.

Local Constants

A local constant, like a local variable, is declared in a method body or code block, and goes out of scope at the end of the block in which it is declared. For example, in the following code, local constant PI goes out of scope at the end of method DisplayRadii.

void DisplayRadii()
   {
      const double PI = 3.1416;                    // Declare local constant

      for (int radius = 1; radius <= 5; radius++)
      {
         double area = radius * radius * PI;       // Read from local constant
         Console.WriteLine
            ("Radius: {0}, Area: {1}", radius, area);
      }
   }

Flow of Control

Methods contain most of the code for the actions that comprise a program. The remainder is in other function members, such as properties and operators—but the bulk is in methods.

The term flow of control refers to the flow of execution through your program. By default, program execution moves sequentially from one statement to the next. The control statements allow you to modify the order of execution.

In this section, I will just mention some of the available control statements you can use in your code. Chapter 9 covers them in detail.

  • Selection statements: These statements allow you to select which statement, or block of statements, to execute.

    • if: Conditional execution of a statement

    • if...else: Conditional execution of one statement or another

    • switch: Conditional execution of one statement from a set

  • Iteration statements: These statements allow you to loop, or iterate, on a block of statements.

    • for: Loop—testing at the top

    • while: Loop—testing at the top

    • do: Loop—testing at the bottom

    • foreach: Execute once for each member of a set

  • Jump statements: These statements allow you to jump from one place in the block or method to another.

    • break: Exit the current loop.

    • continue: Go to the bottom of the current loop.

    • goto: Go to a named statement.

    • return: Return execution to the calling method.

For example, the following method shows two of the flow-of-control statements. Don't worry about the details.

Flow of Control

Method Invocations

You can call other methods from inside a method body.

  • The phrases call a method and invoke a method are synonymous.

  • You call a method by using its name, along with the parameter list, which I will discuss shortly.

For example, the following class declares a method called PrintDateAndTime, which is called from inside method Main.

Method Invocations

Figure 5-4 illustrates the sequence of actions when a method is called:

  1. Execution of the current method suspends at that point of the invocation.

  2. Control transfers to the beginning of the invoked method.

  3. The invoked method executes until it completes.

  4. Control returns to the calling method.

Flow of control when calling a method

Figure 5-4. Flow of control when calling a method

Return Values

A method can return a value to the calling code. The returned value is inserted into the calling code at the position in the expression where the invocation occurred.

  • To return a value, the method must declare a return type before the method name.

  • If a method does not return a value, it must declare a return type of void.

The following code shows two method declarations. The first returns a value of type int. The second does not return a value.

Return Values

A method that declares a return type must return a value from the method by using the following form of the return statement, which includes an expression after the keyword return. Every path through the method must end with a return statement of this form.

Return Values

For example, the following code shows a method called GetHour, which returns a value of type int.

Return Values

You can also return objects of user-defined types. For example, the following code returns an object of type MyClass.

Return Values

As another example, in the following code, method GetHour is called in the WriteLine statement in Main, and returns an int value to that position in the WriteLine statement.

Return Values

The Return Statement and Void Methods

In the previous section, you saw that methods that return a value must contain return statements. Void methods do not require return statements. When the flow of control reaches the closing curly brace of the method body, control returns to the calling code, and no value is inserted back into the calling code.

Often, however, you can simplify your program logic by exiting the method early, when certain conditions apply.

  • You can exit from a method at any time by using the following form of the return statement, with no parameters:

    return;
  • This form of the return statement can be used only with methods declared void.

For example, the following code shows the declaration of a void method called SomeMethod, which has three possible places it might return to the calling code. The first two places are in branches called if statements, which are covered in Chapter 9. The last place is the end of the method body.

The Return Statement and Void Methods

The following code shows an example of a void method with a return statement. The method writes out a message only if the time is after noon. The process, illustrated in Figure 5-5, is as follows:

  • First the method gets the current date and time. (Don't worry about understanding the details of this right now.)

  • If the hour is less than 12 (that is, before noon), the return statement is executed, and control immediately returns to the calling method.

  • If the hour is 12 or greater, the return statement is skipped, and the WriteLine statement is executed.

The Return Statement and Void Methods
Using a return statement with a void return type

Figure 5-5. Using a return statement with a void return type

Parameters

So far, you have seen that methods are named units of code that can be called from many places in a program, and can return a single value to the calling code. Returning a single value is certainly valuable, but what if you need to return multiple values? Also, it would be useful to be able to pass data into a method when it starts execution. Parameters are special variables that allow you to do both these things.

Formal Parameters

Formal parameters are local variables that are declared in the method's parameter list, rather than in the body of the method.

The following method header shows the syntax of parameter declarations. It declares two formal parameters—one of type int, and the other of type float.

Formal Parameters
  • Because formal parameters are variables, they have a data type and a name, and can be written to and read from.

  • Unlike a method's other local variables, the parameters are defined outside the method body and initialized before the method starts, except for one type, which I will cover shortly.

  • The parameter list can have any number of formal parameter declarations, and the declarations must be separated by commas.

The formal parameters are used throughout the method body, for the most part, just like other local variables. For example, the following declaration of method PrintSum uses two formal parameters, x and y, and a local variable, Sum, all of type int.

public void PrintSum( int x, int y )
   {
      int Sum = x + y;
      Console.WriteLine("Newsflash:  {0} + {1} is {2}", x, y, Sum);
   }

Actual Parameters

When your code calls a method, the values of the formal parameters must be initialized before the code in the method begins execution.

  • The expressions or variables used to initialize the formal parameters are called the actual parameters.

  • The actual parameters are placed in the parameter list of the method invocation.

For example, the following code shows the invocation of method PrintSum, which has two actual parameters of data type int.

Actual Parameters

When the method is called, the value of each actual parameter is used to initialize the corresponding formal parameter. The method body is then executed. Figure 5-6 illustrates the relationship between the actual parameters and the formal parameters.

Actual parameters initialize the corresponding formal parameters.

Figure 5-6. Actual parameters initialize the corresponding formal parameters.

When you call a method, the following must be true:

  • The number of actual parameters must be the same as the number of formal parameters (with one exception, which I will discuss later).

  • Each actual parameter must match the type of the corresponding formal parameter.

An Example of Methods with Input Parameters

In the following code, class MyClass declares two methods—one that takes two integers and returns their sum, and another that takes two floats and returns their average.

An Example of Methods with Input Parameters

This code produces the following output:

Newsflash:  Sum: 5 and 6 is 11
Newsflash:  Avg: 5 and 6 is 5.5

Value Parameters

There are several kinds of parameters, which pass data to and from the method in slightly different ways. The kind you have been looking at so far is the default type and is called a value parameter.

When you use value parameters, data is passed to the method by copying the value of the actual parameter to the formal parameter. When a method is called, the system does the following:

  • Allocates space on the stack for the formal parameter

  • Copies the actual parameter to the formal parameter

An actual parameter for a value parameter does not have to be a variable. It can be any expression evaluating to the matching data type. For example, the following code shows two method calls. In the first, the actual parameter is a variable of type float. In the second, it is an expression that evaluates to float.

Value Parameters

Variables must be assigned to, before being used as actual parameters (except in the case of output parameters, which I will cover shortly). For reference types, the variable can be assigned either a reference or null.

Chapter 3 covered value types, which, as you will remember, are types that contain their own data. Don't be confused that I'm now talking about value parameters. They are entirely different. Remember that value parameters are parameters where the value of the actual parameter is copied to the formal parameter.

For example, the following code shows a method called MyMethod, which takes two parameters—a variable of type MyClass and an int.

  • The method adds 5 to both the field of the class and the int.

  • You might also notice that MyMethod uses the modifier static, which hasn't been explained yet. You can ignore it for now. I'll talk about static methods in Chapter 6.

Value Parameters

Figure 5-7 illustrates the following about the values of the actual and formal parameters at various stages in the execution of the method:

  • Before the method call, variables A1 and A2, which will be used as the actual parameters, are already on the stack.

  • By the beginning of the method, the system has allocated space on the stack for the formal parameters and copied the values from the actual parameters.

    • Since A1 is a reference type, the reference is copied, resulting in both the actual and formal parameters referring to the same object in the heap.

    • Since A2 is a value type, the value is copied, producing an independent data item.

  • At the end of the method, both f2 and the field of object f1 have been incremented by 5.

  • After method execution, the formal parameters are popped off the stack.

    • The value of A2, the value type, is unaffected by the activity in the method.

    • The value of A1, the reference type, however, has been changed by the activity in the method.

Value parameters

Figure 5-7. Value parameters

Reference Parameters

The second type of parameter is called a reference parameter.

  • When using a reference parameter, you must use the ref modifier in both the declaration and the invocation of the method.

  • The actual parameter must be a variable, which must be assigned to before being used as the actual parameter. If it is a reference type variable, it can be assigned either a reference or the value null.

For example, the following code illustrates the syntax of the declaration and invocation:

Reference Parameters

Remember that for value parameters, the system allocates memory on the stack for the formal parameters. In contrast, reference parameters have the following characteristics:

  • They do not allocate new memory on the stack for the formal parameters.

  • Instead, a formal parameter name acts as an alias for the actual parameter variable, referring to the same memory location.

Since the formal parameter name and the actual parameter name reference the same memory location, clearly any changes made to the formal parameter during method execution will be visible after the method is completed, through the actual parameter variable.

For example, the following code shows method MyMethod again, but this time the parameters are reference parameters rather than value parameters.

Reference Parameters

Figure 5-8 illustrates the following about the values of the actual and formal parameters at various stages in the execution of the method:

  • Before the method call, variables A1 and A2, which will be used as the actual parameters, are already on the stack.

  • By the beginning of the method, the names of the formal parameters have been set as aliases for the actual parameters. Variables A1 and f1 refer to the same memory location, and A2 and f2 refer to the same memory location.

  • At the end of the method, both f2 and the field of the object of f1 have been incremented by 5.

  • After method execution, the names of the formal parameters are out of scope, but both the value of A2, which is the value type, and the value of the object pointed at by A1, which is the reference type, have been changed by the activity in the method.

Reference parameter

Figure 5-8. Reference parameter

Output Parameters

Output parameters are used to pass data from inside the method back out to the calling code. They are very similar to reference parameters. Like reference parameters, output parameters have the following requirements:

  • You must use a modifier in both the method declaration and the invocation. With output parameters, the modifier is out, rather than ref.

  • The actual parameter must be a variable—it cannot be another type of expression.

For example, the following code declares a method called MyMethod, which takes a single output parameter.

Output Parameters

Like reference parameters, the formal parameters of output parameters act as aliases for the actual parameters. Both the formal parameter and the actual parameter are names for the same memory location. Clearly, any changes made to a formal parameter inside the method will be visible through the actual parameter variable after the method.

Unlike reference parameters, output parameters require the following:

  • Inside the method, an output parameter must be assigned to before it can be read from. This means that the initial values of the parameters are irrelevant, and that you don't have to assign values to the actual parameters before the method call.

  • Every output parameter must be assigned to, before the method exits.

Since the code inside the method must write to an output variable before it can read from it, it is impossible to send data into a method by using output parameters. In fact, if there is any execution path through the method that attempts to read the value of an output parameter before the method has assigned it a value, the compiler produces an error message.

public void Add2( out int outValue )
   {
      int var1 = outValue + 2;  // Error! Can't read from an output variable
   }                            // before it has been assigned to by the method.

For example, the following code again shows method MyMethod, but this time using output parameters.

Output Parameters

Figure 5-9 illustrates the following about the values of the actual and formal parameters at various stages in the execution of the method.

  • Before the method call, variables A1 and A2, which will be used as the actual parameters, are already on the stack.

  • At the beginning of the method, the names of the formal parameters are set as aliases for the actual parameters. Variables A1 and f1 refer to the same memory location, and A2 and f2 refer to the same memory location. The names A1 and A2 are out of scope and cannot be accessed from inside MyMethod.

  • Inside the method, the code creates an object of type MyClass and assigns it to f1. It then assigns a value to f1's field and also assigns a value to f2. The assignment to f1 and f2 are both required, since they are output parameters.

  • After method execution, the names of the formal parameters are out of scope, but the values of both A1, the reference type, and A2, the value type, have been changed by the activity in the method.

Output parameters

Figure 5-9. Output parameters

Parameter Arrays

In the parameter types I've covered so far, there must be exactly one actual parameter for each formal parameter. Parameter arrays are different in that they allow zero or more actual parameters for a particular formal parameter. Important points about parameter arrays are the following:

  • There can be only one parameter array in a parameter list.

  • If there is one, it must be the last parameter in the list.

To declare a parameter array, you must do the following:

  • Use the params modifier before the data type.

  • Place a set of empty square brackets after the data type.

The following method header shows the syntax for the declaration of a parameter array of type int. In this example, formal parameter inVals can represent zero or more actual int parameters.

Parameter Arrays

The empty set of square brackets after the type name specifies that the parameter will be an array of ints. You don't need to worry about the details of arrays here. They are covered in detail in Chapter 14. For our purposes here, though, all you need to know is the following:

  • An array is an ordered set of data items of the same type.

  • An array is accessed by using a numerical index.

  • An array is a reference type, and therefore stores all its data items in the heap.

Method Invocation

You can supply the actual parameters in two ways. The forms you can use are the following:

  • A comma-separated list of elements of the data type. All the elements must be of the type specified in the method declaration.

    ListInts( 10, 20, 30 );              // Three ints
  • A one-dimensional array of elements of the data type.

    int[] intArray = {1, 2, 3};
    ListInts( intArray );                   // An array variable

Notice in these examples that you do not use the params modifier in the invocation. The use of the modifier in parameter arrays does not fit the pattern of the other parameter types.

  • The other parameter types are consistent in that they either use a modifier or do not use a modifier.

    • Value parameters take no modifier in either the declaration or the invocation.

    • Reference and output parameters require the modifier in both places.

  • Parameter arrays, however

    • Require the modifier in the declaration

    • Do not allow it in the invocation

Expanded Form

The first form of method invocation, where you use separate actual parameters in the invocation, is sometimes called the expanded form.

For example, the declaration of method ListInts in the following code matches all the method invocations below it, even though they have different numbers of actual parameters.

void ListInts( params int[] inVals ) { ... }       // Method declaration


   ...
   ListInts( );                                       // 0 actual parameters
   ListInts( 1, 2, 3 );                               // 3 actual parameters
   ListInts( 4, 5, 6,  7 );                           // 4 actual parameters
   ListInts( 8, 9, 10, 11, 12 );                      // 5 actual parameters

When you use an invocation with separate actual parameters for a parameter array, the compiler does the following:

  • It takes the list of actual parameters and uses them to create and initialize an array in the heap.

  • It stores the reference to the array in the formal parameter on the stack.

  • If there are no actual parameters at the position corresponding to the formal parameter array, the compiler creates an array with zero elements and uses that.

For example, the following code declares a method called ListInts, which takes a parameter array. Main declares three ints and passes them to the array.

Expanded Form

This code produces the following output:

50
60
70
5, 6, 7

Figure 5-10 illustrates the following about the values of the actual and formal parameters at various stages in the execution of the method:

  • Before the method call, the three actual parameters are already on the stack.

  • By the beginning of the method, the three actual parameters have been used to initialize an array in the heap, and the reference to the array has been assigned to formal parameter inVals.

  • Inside the method, the code first checks to make sure that the array reference is not null, and then processes the array by multiplying each element in the array by 10, and storing it back.

  • After method execution, the formal parameter, inVals, is out of scope.

Parameter arrays

Figure 5-10. Parameter arrays

An important thing to remember about parameter arrays is that when an array is created in the heap, the values of the actual parameters are copied to the array. In this way, they are like value parameters.

  • If the array parameter is a value type, the values are copied, and the actual parameters cannot be affected inside the method.

  • If the array parameter is a reference type, the references are copied, and the objects referenced by the actual parameters can be affected inside the method.

Arrays As Actual Parameters

You can also create and populate an array before the method call, and pass the single array variable as the actual parameter. In this case, the compiler uses your array, rather than creating one.

For example, the following code uses method ListInts, declared in the previous example. In this code, Main creates an array and uses the array variable as the actual parameter, rather than using separate integers.

static void Main()
      {
         int[] MyArr = new int[] { 5, 6, 7 };  // Create and initialize array.

         MyClass mc = new MyClass();
         mc.ListInts(MyArr);                   // Call method.

         foreach (int x in MyArr)
            Console.WriteLine("{0}", x);       // Print out each element.
      }

This code produces the following output:

50
60
70

Summary of Parameter Types

Since there are four parameter types, it is sometimes difficult to remember their various characteristics. Table 5-2 summarizes them, making it easier to compare and contrast them.

Table 5-2. Summary of Parameter Type Syntactic Usage

Parameter Type

Modifier

Used at Declaration?

Used at Invocation?

Implementation

Value

None

  

The system copies the actual parameter to the formal parameter.

Reference

ref

Yes

Yes

The formal parameter aliases the actual parameter.

Output

out

Yes

Yes

The formal parameter aliases the actual parameter.

Array

params

Yes

No

This allows passing a variable number of actual parameters to a method.

Stack Frames

So far, you know that local variables and parameters are kept on the stack. Let's look at that organization a little further.

When a method is called, memory is allocated at the top of the stack to hold a number of data items associated with the method. This chunk of memory is called the stack frame for the method.

  • The stack frame contains memory to hold the following:

    • The return address—that is, where to resume execution when the method exits

    • Those parameters that allocate memory—that is, the value parameters of the method, and the parameter array if there is one

    • Various other administrative data items relevant to the method call

  • When a method is called, its entire stack frame is pushed onto the stack.

  • When the method exits, its entire stack frame is popped from the stack. Popping a stack frame is sometimes called unwinding the stack.

For example, the following code declares three methods. Main calls MethodA, which calls MethodB, creating three stack frames. As the methods exit, the stack unwinds.

class Program
   {
      static void MethodA( int par1, int par2)
      {
         Console.WriteLine("Enter MethodA: {0}, {1}", par1, par2);
         MethodB(11, 18);                                    // Call MethodB.
         Console.WriteLine("Exit  MethodA");
      }

      static void MethodB(int par1, int par2)
      {
         Console.WriteLine("Enter MethodB: {0}, {1}", par1, par2);
         Console.WriteLine("Exit  MethodB");
      }

      static void Main( )
      {
         Console.WriteLine("Enter Main");
         MethodA( 15, 30);                                   // Call MethodA.
         Console.WriteLine("Exit  Main");
      }
   }

This code produces the following output:

Enter Main
Enter MethodA: 15, 30
Enter MethodB: 11, 18
Exit  MethodB
Exit  MethodA
Exit  Main

Figure 5-11 shows how the stack frames of each method are placed on the stack when the method is called, and how the stack is unwound as the methods complete.

Stack frames in a simple program

Figure 5-11. Stack frames in a simple program

Recursion

Besides calling other methods, a method can also call itself. This is called recursion.

Recursion can produce some very elegant code, such as the following method for computing the factorial of a number. Notice that inside the method, the method calls itself, with an actual parameter of one less than its input parameter.

Recursion

The mechanics of a method calling itself are exactly the same as if it had called another, different method. A new stack frame is pushed onto the stack for each call to the method.

For example, in the following code, method Count calls itself with one less than its input parameter and then prints out its input parameter. As the recursion gets deeper, the stack gets larger.

Recursion

This code produces the following output:

1
2
3

Figure 5-12 illustrates the code. Notice that with an input value of 3, there are four different, independent stack frames for method Count. Each has its own value for input parameter inVal.

Example of recursion

Figure 5-12. Example of recursion

Method Overloading

A class can have more than one method with the same name. This is called method overloading. Each method with the same name must have a different signature than the others.

  • The signature of a method consists of the following information from the method header of the method declaration:

    • The name of the method

    • The number of parameters

    • The data types and order of the parameters

    • The parameter modifiers

  • The return type is not part of the signature—although it is a common mistake to believe that it is.

  • The names of the formal parameters are also not part of the signature.

Method Overloading

For example, the following four methods are overloads of the method name AddValues:

class A
   {
      long AddValues( int   a, int   b)           { return a + b; }
      long AddValues( int   a, int   b, int c)    { return a + b + c; }
      long AddValues( float a, float b)           { return a + b; }
      long AddValues( long  a, long  b)           { return a + b; }
   }

The following code shows an illegal attempt at overloading the method name AddValues. The two methods differ only on the return types and the names of the formal parameters. But they still have the same signature, because they have the same method name; and the number, types, and order of their parameters are the same. The compiler would produce an error message for this code.

Method Overloading
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.3.175