3.5. Returned Function Values

You probably have used mathematical functions in algebra class, but they all had calculated values associated with them. For instance if you defined

F(x)=x2

then it follows that F(3) is 32 = 9, and F(3)+F(4) is 32 + 42 = 9 + 16 = 25.

Function calls in expressions get replaced during evaluation by the value of the function.

The corresponding definition and examples in C# would be the following, taken from example program return1.cs. Read and run:

using System;

class Return1
{
   static int F(int x)
   {
      return x*x;
   }

   static void Main()
   {
      Console.WriteLine(F(3));
      Console.WriteLine(F(3) + F(4));
   }
}

The new C# syntax is the return statement, with the word return followed by an expression. Functions that return values can be used in expressions, just like in math class. When an expression with a function call is evaluated, the function call is effectively replaced temporarily by its returned value. Inside the C# function, the value to be returned is given by the expression in the return statement.

Since the function returns data, and all data in C# is typed, there must be a type given for the value returned. Note that the function heading does not start with static void. In place of void is int. The void in earlier function headings meant nothing was returned. The int here means that a value is returned and its type is int.

After the function F finishes executing from inside

Console.WriteLine(F(3));

it is as if the statement temporarily became

Console.WriteLine(9);

and similarly when executing

Console.WriteLine(F(3) + F(4));

the interpreter first evaluates F(3) and effectively replaces the call by the returned result, 9, as if the statement temporarily became

Console.WriteLine(9 + F(4));

and then the interpreter evaluates F(4) and effectively replaces the call by the returned result, 16, as if the statement temporarily became

Console.WriteLine(9 + 16);

resulting finally in 25 being calculated and printed.

C# functions can return any type of data, not just numbers, and there can be any number of statements executed before the return statement. Read, follow, and run the example program return2.cs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
using System;

class Return2
{
   static string LastFirst(string firstName, string lastName)
   {
      string separator = ", ";
      string result = lastName + separator + firstName;
      return result;
   }

   static void Main()
   {
      Console.WriteLine(LastFirst("Benjamin", "Franklin"));
      Console.WriteLine(LastFirst("Andrew", "Harrington"));
   }
}

Many have a hard time following the flow of execution with functions. Even more is involved when there are return values. Make sure you completely follow the details of the execution:

  1. Lines 12: Start at Main
  2. Line 14: call the function, remembering where to return
  3. Line 5: pass the parameters: firstName = "Benjamin"; lastName = "Franklin"
  4. Line 7: Assign the variable separator the value ", "
  5. Line 8: Assign the variable result the value of lastName + separator + firstName which is "Franklin" + ", " + "Benjamin", which evaluates to "Franklin, Benjamin"
  6. Line 9: Return "Franklin, Benjamin"
  7. Line 14: Use the value returned from the function call so the line effectively becomes Console.WriteLine("Franklin, Benjamin");, so print it.
  8. Line 15: call the function with the new actual parameters, remembering where to return
  9. Line 5: pass the parameters: firstName = "Andrew"; lastName = "Harrington"
  10. Lines 7-9: ... calculate and return "Harrington, Andrew"
  11. Line 15: Use the value returned by the function and print "Harrington, Andrew"

Compare return2/return2.cs and addition1/addition1.cs, from the previous section. Both use functions. Both print, but where the printing is done differs. The function SumProblem prints directly inside the function and returns nothing. On the other hand LastFirst does not print anything but returns a string. The caller gets to decide what to do with the returned string, and above it is printed in Main.

In general functions should do a single thing. You can easily combine a sequence of functions, and you have more flexibility in the combinations if each does just one unified thing. The function SumProblem in addition1/addition1.cs does two thing: It creates a sentence, and prints it. If that is all you have, you are out of luck if you want to do something different with the sentence string. A better approach is to have a function that just creates the sentence, and returns it for whatever further use you want. After returning that value, printing is one possibility, done in addition2/addition2.cs:

using System;

class Addition2
{
   static string SumProblemString(int x, int y)
   {
      int sum = x + y;
      string sentence = "The sum of " + x + " and " + y + " is " + sum + ".";
      return sentence;
   }

   static void Main()
   {
      Console.WriteLine(SumProblemString(2, 3));
      Console.WriteLine(SumProblemString(12345, 53579));
      Console.Write("Enter an integer: ");
      int a = int.Parse(Console.ReadLine());
      Console.Write("Enter another integer: ");
      int b = int.Parse(Console.ReadLine());
      Console.WriteLine(SumProblemString(a, b));
   }
}

This example constructs the sentence using the string + operator. Generating a string with substitutions using a format string in Console.Write is neater, but we are forced to directly print the string, and not remember it for later arbitrary use.

It is common to want to construct and immediately print a string, so having Console.Write is definitely handy when we want it., However it is an example of combining two separate steps! Sometimes (like here) we just want to have the resulting string, and do something else with it. We introduce the C# library function string.Format, which does just what we want: The parameters have the same form as for Console.Write, but the formatted string is returned.

Here is a revised version of the function SumProblemString, from example addition2a/addition2a.cs:

static string SumProblemString(int x, int y) // with string.Format
{
   int sum = x + y;
   return string.Format("The sum of {0} and {1} is {2}.", x, y, sum);
}

The only caveat with string.Format is that there is no special function corresponding to Console.WriteLine, with an automatic terminating newline. You can generate a newline with string.Format: Remember the escape code "\n". Put it at the end to go on to a new line.

In class recommendation: Improve example painting/painting.cs with a function used for repeated similar operations. Copy it to a file painting_input.cs in your own project and modify it.

3.5.1. Interview String Return Exercise/Example

Write a program by that accomplishes the same thing as Interview Exercise/Example, but introduce a function InterviewSentence that takes name and time strings as parameters and returns the interview sentence string. For practice use string.Format in the function. With this setup you can manage input from the user and output to the screen entirely in Main, while using InterviewSentence to generate the sentence that you want to later print.

(Here we are having you work on getting used to function syntax while keeping the body of your new function very simple. Combining that with longer, more realistic function bodies is coming!)

If you want a further example on this idea of returning something first and then using the result, or if you want to compare your work to ours, see our solution, interview2/interview2.cs.

3.5.2. Quotient String Return Exercise

Create quotient_return.cs by modifying quotient_prob.cs in Quotient Function Exercise so that the program accomplishes the same thing, but everywhere:

  • Change the QuotientProblem function into one called QuotientString that merely returns the string rather than printing the string directly.
  • Have Main print the result of each call to the QuotientString function.

Use string.Format to create the sentence that you return.