Defining and Using Functions


In this section, you see how you can add functions to your applications and then use (call) them from your code. You start with the basics, looking at simple functions that don't exchange any data with code that calls them, and then move on to look at more advanced function usage.

The following Try It Out will get things moving.

Try It Out – Defining and Using a Basic Function

image from book
  1. Create a new console application called Ch06Ex01 in the directory C:\BegVCSharp\Chapter6.

  2. Add the following code to Program.cs:

    class Program { static void Write() { Console.WriteLine("Text output from function.");    }        static void Main(string[] args)    {     Write(); Console.ReadKey(); } }
  3. Execute the code. The result is shown in Figure 6-1.

    image from book
    Figure 6-1

How It Works

The following four lines of your code define a function called Write():

static void Write() {    Console.WriteLine("Text output from function."); }

The code contained here simply outputs some text to the console window. However, this behavior isn't that important at the moment, because the focus here is on the mechanisms behind function definition and use.

The function definition here consists of the following:

  • Two keywords, static and void

  • A function name followed by parentheses, Write()

  • A block of code to execute enclosed in curly braces

    Note

    Function names are usually written in PascalCasing.

The code that defines the Write() function looks very similar to some of the other code in your application:

static void Main(string[] args) {    ... }

This is so because all the code you have written so far (apart from type definitions) has been part of a function. This function, Main(), is (as suggested by the comment in the auto-generated code) the entry point function for a console application. When a C# application is executed, the entry point function it contains is called, and when this function is completed the application terminates. All C# executable code must have an entry point.

The only difference between the Main() function and your Write() function (apart from the lines of code they contain) is that there is some code inside the parentheses after the function name Main. This is how you specify parameters, which you see in more detail shortly.

As mentioned earlier, both Main() and Write() are defined using static and void keywords. the static keyword relates to object-oriented concepts, which you come back to later in the book. For now, you only need to remember that all the functions you use in your applications in this section of the book must use this keyword.

void, in contrast, is much simpler to explain. This keyword is to indicate the function does not return a value. Later on in this chapter, you see what you need to write when a function has a return value.

Moving on, the code that calls your function is:

Write();

You simply type the name of the function followed by empty parentheses. When program execution reaches this point the code in the Write() function will run.

Note

Note that the parentheses used, both in the function definition and where the function is called, are mandatory. Try removing them if you like — the code won't compile.

image from book

Return Values

The simplest way of exchanging data with a function is to make use of a return value. Functions that have return values evaluate to that value, in exactly the same way as variables evaluate to the value they contain when you use them in expressions. Just like variables, return values have a type.

For example, you might have a function called GetString() whose return value is a string. You could use this in code, such as:

 string myString; myString = GetString(); 

Alternatively, you might have a function called GetVal() that returns a double value, which you could use in a mathematical expression:

 double myVal; double multiplier = 5.3; myVal = GetVal() * multiplier; 

When a function returns a value, you have to modify your function in two ways:

  • Specify the type of the return value in the function declaration instead of using the void keyword

  • Use the return keyword to end the function execution and transfer the return value to the calling code

In code terms, this looks like the following in a console application function of the type you've been looking at:

 static <returnType> <functionName>() { ...    return <returnValue>; } 

The only limitation here is that <returnValue> must be a value that is either of type <returnType> or can be implicitly converted to that type. However, <returnType> can be any type you want, including the more complicated types you've seen.

This might be as simple as:

 static double GetVal() { return 3.2; } 

However, return values are usually the result of some processing carried out by the function; the above could be achieved just as easily by using a const variable.

When the return statement is reached, program execution returns to the calling code immediately. No lines of code after this statement will be executed. However, this doesn't mean that return statements can only be placed on the last line of a function body. You can use return earlier in the code, perhaps after performing some branching logic. Placing return in a for loop, an if block, or any other structure causes the structure to terminate immediately and the function to terminate. For example:

static double GetVal() {     double checkVal; // CheckVal assigned a value through some logic. if (checkVal < 5) return 4.7;        return 3.2; }

Here one of two values may be returned, depending on the value of checkVal.

The only restriction here is that a return statement must be processed before reaching the closing } of the function. The following is illegal:

static double GetVal() {    double checkVal;    // CheckVal assigned a value through some logic.    if (checkVal < 5)       return 4.7; }

If checkVal is >= 5, then no return statement is met, which isn't allowed. All processing paths must reach a return statement. In most cases, the compiler will detect this and give you the error "not all code paths return a value."

As a final note, return can be used in functions declared using the void keyword (that don't have a return value). If you do so, then the function will simply terminate. When you use return in this way, it is an error to provide a return value in between the return keyword and the semicolon that follows.

Parameters

When a function is to accept parameters, you must specify the following:

  • A list of the parameters accepted by a function in its definition, along with the types of those parameters

  • A matching list of parameters in each function call

This involves the following code:

 static <returnType> <functionName>(<paramType> <paramName>, ...) { ...    return <returnValue>; } 

Where you can have any number of parameters, each with a type and a name. The parameters are separated using commas. Each of these parameters is accessible from code within the function as a variable.

For example, a simple function might take two double parameters and return their product:

 static double Product(double param1, double param2) { return param1 * param2; } 

The following Try It Out provides a more complex example.

Try It Out – Exchanging Data with a Function

image from book
  1. Create a new console application called Ch06Ex02 in the directory C:\BegVCSharp\Chapter6.

  2. Add the following code to Program.cs:

    class Program { static int MaxValue(int[] intArray) { int maxVal = intArray[0]; for (int i = 1; i < intArray.Length; i++) { if (intArray[i] > maxVal) maxVal = intArray[i]; } return maxVal; }    static void Main(string[] args)    { int[] myArray = {1, 8, 3, 6, 2, 5, 9, 3, 0, 2}; int maxVal = MaxValue(myArray); Console.WriteLine("The maximum value in myArray is {0}", maxVal); Console.ReadKey();    } }

  3. Execute the code. The result is shown in Figure 6-2.

    image from book
    Figure 6-2

How It Works

This code contains a function that does what the example function discussed in the introduction to this chapter hoped to do. It accepts an array of integers as a parameter and returns the highest number in the array. The function definition is as follows:

static int MaxValue(int[] intArray) {    int maxVal = intArray[0];    for (int i = 1; i < intArray.Length; i++)    {       if (intArray[i] > maxVal)          maxVal = intArray[i];    }    return maxVal; }

The function, MaxValue(), has a single parameter defined, an int array called intArray. It also has a return type of int. The calculation of the maximum value is simple. A local integer variable called maxVal is initialized to the first value in the array, and then this value is compared with each of the subsequent elements in the array. If an element contains a higher value than maxVal, then this value replaces the current value of maxVal. When the loop finishes, maxVal contains the highest value in the array, and is returned using the return statement.

The code in Main() declares and initializes a simple integer array to use with the MaxValue() function:

int[] myArray = {1, 8, 3, 6, 2, 5, 9, 3, 0, 2};

The call to MaxValue() is used to assign a value to the int variable maxVal:

int maxVal = MaxValue(myArray);

Next, you write this value to the screen using Console.WriteLine():

Console.WriteLine("The maximum value in myArray is {0}", maxVal); 
image from book

Parameter Matching

When you call a function, you must match the parameters as specified in the function definition exactly. This means matching the parameter types, the number of parameters, and the order of the parameters. This means, for example, that the following function

 static void MyFunction(string myString, double myDouble) { ... } 

can't be called using:

 MyFunction (2.6, "Hello"); 

Here, you are attempting to pass a double value as the first parameter and a string value as the second parameter, which is not the order in which the parameters are defined in the function definition.

You also can't use:

 MyFunction("Hello"); 

Here, you are only passing a single string parameter, where two parameters are required.

Attempting to use either of the two preceding function calls will result in a compiler error, because the compiler forces you to match the signatures of the functions you use.

Going back to the example, this means that MaxValue() can only be used to obtain the maximum int in an array of int values. If you replace the code in Main() with the following code

static void Main(string[] args) { double[] myArray = {1.3, 8.9, 3.3, 6.5, 2.7, 5.3}; double maxVal = MaxValue(myArray);    Console.WriteLine("The maximum value in myArray is {0}", maxVal);    Console.ReadKey(); }

then the code won't compile, because the parameter type is wrong.

Later on in this chapter, in the "Overloading Functions" section, you see a useful technique for getting round this problem.

Parameter Arrays

C# allows you to specify one (and only one) special parameter for a function. This parameter, which must be the last parameter in the function definition, is known as a parameter array. Parameter arrays allow you to call functions using a variable amount of parameters and are defined using the params keyword.

Parameter arrays can be a useful way to simplify your code, because you don't have to pass arrays from your calling code. Instead, you pass several parameters of the same type that are placed in an array that you can use from within your function.

The following code is required to define a function that uses a parameter array:

 static <returnType> <functionName>(<p1Type> <p1Name>, ... ,                                    params <type>[] <name>) { ...    return <returnValue>; } 

You can call this function using code like:

 <functionName>(<p1>, ... , <val1>, <val2>, ...) 

Here <val1>, <val2>, and so on are values of type <type>, which are used to initialize the <name> array. The number of parameters that you can specify here is almost limitless; the only restriction is that they are all of type <type>. You can even specify no parameters at all.

This final point makes parameter arrays particularly useful for specifying additional information for functions to use in their processing. For example, say that you have a function called GetWord() that takes a string value as its first parameter and returns the first word in the string:

 string firstWord = GetWord("This is a sentence."); 

Here firstWord will be assigned the string This.

You might add a params parameter to GetWord(), allowing you to optionally select an alternative word to return by its index:

 string firstWord = GetWord("This is a sentence.", 2); 

Assuming that you start counting at 1 for the first word, this would result in firstWord being assigned the string is.

You might also add the capability to limit the amount of characters returned in a third parameter, also accessible through the params parameter:

 string firstWord = GetWord("This is a sentence.", 4, 3); 

Here firstWord would be assigned the string sen.

Here's a full example. In the following Try It Out you define and use a function with a params type parameter.

Try It Out – Exchanging Data with a Function Part 2

image from book
  1. Create a new console application called Ch06Ex03 in the directory C:\BegVCSharp\Chapter6.

  2. Add the following code to Program.cs:

    class Program { static int SumVals(params int[] vals) { int sum = 0; foreach (int val in vals) { sum += val; } return sum; }        static void Main(string[] args)    { int sum = SumVals(1, 5, 2, 9, 8); Console.WriteLine("Summed Values = {0}", sum); Console.ReadKey();    } }
  3. Execute the code. The result is shown in Figure 6-3.

    image from book
    Figure 6-3

How It Works

In this example, the function SumVals() is defined using the params keyword to accept any number of int parameters (and no others):

static int SumVals(params int[] vals) { ... }

The code in this function simply iterates through the value in the vals array and adds the values together, returning the result.

In Main(), you call this function with five integer parameters:

int sum = SumVals (1, 5, 2, 9, 8);

However, you could just as easily have called this function with none, one, two, or a hundred integer parameters — there is no limit to the amount you can specify.

image from book

Reference and Value Parameters

All the functions defined so far in this chapter have had value parameters. What I mean by this is that when you have used parameters you have passed a value into a variable used by the function. Any changes made to this variable in the function have no effect on the parameter specified in the function call. For example, consider a function that doubles and displays the value of a passed parameter:

 static void ShowDouble(int val) { val *= 2; Console.WriteLine("val doubled = {0}", val); } 

Here, the parameter, val, is doubled in this function. If you call it in the following way:

 int myNumber = 5; Console.WriteLine("myNumber = {0}", myNumber); ShowDouble(myNumber); Console.WriteLine("myNumber = {0}", myNumber); 

The text output to the console is as follows:

myNumber = 5 val doubled = 10 myNumber = 5

Calling showDouble() with myNumber as a parameter doesn't affect the value of myNumber in Main(), even though the parameter it is assigned to, val, is doubled.

This is all very well, but if you want the value of myNumber to change you have a problem. You could use a function that returns a new value for myNumber, for example:

 static int DoubleNum(int val) { val *= 2; return val; } 

You could call this function using:

int myNumber = 5; Console.WriteLine("myNumber = {0}", myNumber); myNumber = DoubleNum(myNumber); Console.WriteLine("myNumber = {0}", myNumber);

But this code is hardly intuitive and won't cope with changing the values of multiple variables used as parameters (as functions have only one return value).

Instead, you want to pass the parameter by reference. This means that the function will work with exactly the same variable as the one used in the function call, not just a variable that has the same value. Any changes made to this variable will, therefore, be reflected in the value of the variable used as a parameter. To do this, you simply have to use the ref keyword to specify the parameter:

 static void ShowDouble(ref int val) {    val *= 2;    Console.WriteLine("val doubled = {0}", val); }

And again in the function call (this is mandatory, as the fact that the parameter is a ref parameter is part of the function signature):

int myNumber = 5; Console.WriteLine("myNumber = {0}", myNumber); ShowDouble(ref myNumber); Console.WriteLine("myNumber = {0}", myNumber);

The text output to the console is now:

myNumber = 5 val doubled = 10 myNumber = 10

This time myNumber has been modified by ShowDouble().

There are two limitations on the variable used as a ref parameter. First, the function may result in a change to the value of a reference parameter, so you must use a nonconstant variable in the function call. The following is therefore illegal:

 const int myNumber = 5; Console.WriteLine("myNumber = {0}", myNumber); ShowDouble(ref myNumber); Console.WriteLine("myNumber = {0}", myNumber);

Second, you must use an initialized variable. C# doesn't allow you to assume that a ref parameter will be initialized in the function that uses it. The following code is also illegal:

 int myNumber; ShowDouble(ref myNumber); Console.WriteLine("myNumber = {0}", myNumber);

Out Parameters

In addition to passing values by reference, you can also specify that a given parameter is an out parameter by using the out keyword, which is used in the same way as the ref keyword (as a modifier to the parameter in the function definition and in the function call). In effect, this gives you almost exactly the same behavior as a reference parameter in that the value of the parameter at the end of the function execution is returned to the variable used in the function call. However, there are important differences:

  • Whereas it is illegal to use an unassigned variable as a ref parameter, you can use an unassigned variable as an out parameter.

  • In addition, an out parameter must be treated as an unassigned value by the function that uses it.

This means that while it is permissible for calling code to use an assigned variable as an out parameter, the value stored in this variable will be lost when the function executes.

As an example, consider an extension to the MaxValue() function you saw earlier, which returns the maximum value of an array. You modify the function slightly so that you obtain the index of the element with the maximum value within the array. To keep things simple, you obtain just the index of the first occurrence of this value when there are multiple elements with the maximum value. To do this, you add an out parameter by modifying the function as follows:

 static int MaxValue(int[] intArray, out int maxIndex) {    int maxVal = intArray[0]; maxIndex = 0;    for (int i = 1; i < intArray.Length; i++)    {       if (intArray[i] > maxVal) {          maxVal = intArray[i]; maxIndex = i; }    }    return maxVal; }

You might use this function as follows:

 int[] myArray = {1, 8, 3, 6, 2, 5, 9, 3, 0, 2}; int maxIndex; Console.WriteLine("The maximum value in myArray is {0}", MaxValue(myArray, out maxIndex)); Console.WriteLine("The first occurrence of this value is at element {0}", maxIndex + 1); 

This results in:

The maximum value in myArray is 9 The first occurrence of this value is at element 7

An important point to note here is that you must use the out keyword in the function call, just as with the ref keyword.

Note

Note that I've added one to the value of maxIndex returned here when it is displayed on screen. This is to translate the index to a more readable form, so that the first element in the array is referred to element 1 rather than element 0.




Beginning Visual C# 2005
Beginning Visual C#supAND#174;/sup 2005
ISBN: B000N7ETVG
EAN: N/A
Year: 2005
Pages: 278

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