Declaring a Method

This section expands on the explanation of declaring a method to include parameters or a return type. Listing 5.4 contains examples of these concepts, and Output 5.1 shows the results.

Listing 5.4: Declaring a Method
public class IntroducingMethods
{
    public static void Main()
    {
        string firstName;
        string lastName;
        string fullName;
        string initials;
 
        Console.WriteLine("Hey you!");
 
        firstName = GetUserInput("Enter your first name: ");
        lastName = GetUserInput("Enter your last name: ");
 
        fullName = GetFullName(firstName, lastName);
        initials = GetInitials(firstName, lastName);
        DisplayGreeting(fullName, initials);
    }
 
    static string GetUserInput(string prompt)
    {
        Console.Write(prompt);
        return Console.ReadLine() ?? string.Empty;
    }
 
    static string GetFullName(  
          string firstName, string lastName) =>
              $"{ firstName } { lastName }";
 
 
    static void DisplayGreeting(string fullName, string initials)
    {
        Console.WriteLine(
            $"Hello { fullName }! Your initials are { initials }");
        return;
    }
 
    static string GetInitials(string firstName, string lastName)
    {
        return $"{ firstName[0] }{ lastName[0] }.";
    }
}
Output 5.1
Hey you!
Enter your first name: Inigo
Enter your last name: Montoya
Hello Inigo Montoya! Your initials are I. M.

Listing 5.4 declares five methods. From Main() the code calls GetUserInput(), followed by a call to GetFullName() and GetInitials(). All of the last three methods return a value and take arguments. In addition, the listing calls DisplayGreeting(), which doesn’t return any data.

Language Contrast: C++/Visual Basic—Global Methods

C# provides no global method support; everything must appear within a type declaration. This is why the Main() method was marked as static—the C# equivalent of a C++ global and Visual Basic “shared” method. The statements that appear independent from a method and the methods that appear independent from a class are even conforming to this rule because the C# complier generates the surrounding method and class for these seemingly independent constructs. For more information, see “Top-Level Statements” later in this chapter.

Beginner Topic
Refactoring into Methods

Moving a set of statements into a method instead of leaving them inline within a larger method is a form of refactoring. Refactoring reduces code duplication, because you can call the method from multiple places instead of duplicating the code. Refactoring also increases code readability. As part of the coding process, it is a best practice to continually review your code and look for opportunities to refactor. This involves looking for blocks of code that are difficult to understand at a glance and moving them into a method with a name that clearly defines the code’s behavior. This practice is often preferred over adding comments to a block of code, because the method name serves to describe what the implementation does. Listing 5.4 is easy to grasp at a glance by just viewing the Main() method and not worrying about the details of each called method’s implementation.

Visual Studio allows you to right-click on a block of code within a method and click Quick Actions and Refactorings… (Ctrl+.) to extract the block into its own method, automatically inserting code to call the new method from the original location.

Formal Parameter Declaration

Consider the declarations of the DisplayGreeting(), GetFullName(), and the GetInitials() methods. The text that appears between the parentheses of a method declaration is the formal parameter list. (As we will see when we discuss generics, methods may also have a type parameter list. When it is clear from the context which kind of parameters we are discussing, we simply refer to them as parameters in a parameter list.) Each parameter in the parameter list includes the type of the parameter along with the parameter name. A comma separates each parameter in the list.

Behaviorally, most parameters are virtually identical to local variables, and the naming convention of parameters follows accordingly. Therefore, parameter names use camelCase. Also, it is not possible to declare a local variable (a variable declared inside a method) with the same name as a parameter of the containing method, because this would create two local variables of the same name.

Guidelines
DO use camelCasing for parameter names.
Method Return Type Declaration

In addition to GetUserInput(), GetFullName(), and the GetInitials() methods requiring parameters to be specified, each of these methods includes a method return type. You can tell that a method returns a value because a data type appears immediately before the method name in the method declaration. Each of these method examples specifies a string return type. Unlike parameters, of which there can be any number, only one method return type is allowable.

As with GetUserInput() and GetInitials(), methods with a return type almost always2 contain one or more return statements that return control to the caller. A return statement consists of the return keyword followed by an expression that computes the value the method is returning. For example, the GetInitials() method’s return statement is return $"{ firstName[0] }. { lastName[0] }.";. The expression (an interpolated string in this case) following the return keyword must be compatible with the stated return type of the method.

If a method has a return type, the block of statements that makes up the body of the method must not have an unreachable end point. That is, there must be no way for control to “fall off the end” of a method without returning a value. Often the easiest way to ensure that this condition is met is to make the last statement of the method a return statement. However, return statements can appear in locations other than at the end of a method implementation. For example, an if or switch statement in a method implementation could include a return statement within it; see Listing 5.5 for an example.

Listing 5.5: A return Statement before the End of a Method
public class Program
{
    // ...
 
    public static bool MyMethod()
    {
        string command = ObtainCommand();
        switch(command)
        {
            case "quit":
                return false;
            // ... omitted, other cases
            default:
                return true;
        }
    }
 
    // ...
}

(Note that a return statement transfers control out of the switch, so no break statement is required to prevent illegal fall-through in a switch section that ends with a return statement.)

In Listing 5.5, the last statement in the method is not a return statement; it is a switch statement. However, the compiler can deduce that every possible code path through the method results in a return, so that the end point of the method is not reachable. Thus, this method is legal even though it does not end with a return statement.

If particular code paths include unreachable statements following the return, the compiler will issue a warning that indicates the additional statements will never execute.

Though C# allows a method to have multiple return statements, code is generally more readable and easier to maintain if there is a single exit location rather than having multiple returns sprinkled through various code paths of the method.

Specifying void as a return type indicates that there is no return value from the method. As a result, a call to the method may not be assigned to a variable or used as a parameter type at the call site. A void method call may be used only as a statement. Furthermore, within the body of the method the return statement becomes optional, and when it is specified, there must be no value following the return keyword. For example, the return of Main() in Listing 5.4 is void, and there is no return statement within the method. However, DisplayGreeting() includes an (optional) return statement that is not followed by any returned result.

Although, technically, a method can have only one return type, the return type could be a tuple. As a result, starting with C# 7.0, it is possible to return multiple values packaged as a tuple using C# tuple syntax. For example, you could declare a GetName() method, as shown in Listing 5.6.

Listing 5.6: Returning Multiple Values Using a Tuple
public class Program
{
    static string GetUserInput(string prompt)
    {
        Console.Write(prompt);
        return Console.ReadLine() ?? string.Empty;
    }
    static (string First, string Last) GetName()
    {
        string firstName, lastName;
        firstName = GetUserInput("Enter your first name: ");
        lastName = GetUserInput("Enter your last name: ");
        return (firstName, lastName);
    }
    static public void Main()
    {
        (string First, string Last) name = GetName();
        Console.WriteLine($"Hello { name.First } { name.Last }!");
    }
}

Technically, we are still returning only one data type, a ValueTuple<string, string>. However, effectively, you can return any (preferably reasonable) number you like using each item within the tuple.

Expression Bodied Methods

To support the simplest of method declarations without the formality of a method body, C# 6.0 introduced expression bodied methods, which are declared using an expression rather than a full method body. Listing 5.4’s GetFullName() method provides an example of the expression bodied method:

static string GetFullName( string firstName, string lastName) =>

In place of the curly brackets typical of a method body, an expression bodied method uses the “goes to” operator (fully introduced in Chapter 13), for which the resulting data type must match the return type of the method. In other words, even though there is no explicit return statement in the expression bodied method implementation, it is still necessary that the return type from the expression match the method declaration’s return type.

Expression bodied methods are syntactic shortcuts to the fuller method body declaration. As such, their use should be limited to the simplest of method implementations—generally expressible on a single line.

Language Contrast: C++—Header Files

Unlike in C++, C# classes never separate the implementation from the declaration. In C#, there is no header (.h) file or implementation (.cpp) file. Instead, declaration and implementation appear together in the same file. (C# does support an advanced feature called partial methods, in which the method’s defining declaration is separate from its implementation, but for the purposes of this chapter, we consider only nonpartial methods.) The lack of a separate declaration and implementation in C# removes the requirement to maintain redundant declaration information in two places found in languages that have separate header and implementation files, such as C++.

________________________________________

2. For example, you could throw an exception instead.
{{ snackbarMessage }}
;