ms_learn_csharp/028_method_parameters/028_csharp.md
2024-08-10 23:44:42 -04:00

17 KiB

Create C# methods with parameters

Learn how to use different types of input parameters in methods

Learning objectives

  • Learn more about using parameters

  • Understand method scope

  • Understand pass-by-reference and pass-by-value parameter types

  • Learn how to use optional and named arguments

Introduction

Methods have the ability to perform operations on input. Passing parameters to your methods allows you to perform the method's task with different input values. Using method parameters lets you extend your code while keeping your program organized and readable. If you consider a method to be a black box that accepts input and performs a single task, you can quickly divide a large problem into workable pieces.

Suppose you need to write code that performs the same operation on different sets of input. You might have three different arrays, and need to display the contents of each one. You can create a DisplayArray method that accepts a single array as input and displays the contents. Instead of writing code to display each individual array, you can call the same method and provide the different arrays as input.

Parameters can make your methods more robust while still performing the same general task. In this module, you'll learn more about working with parameters and solidify your understanding of methods.

Learning Objectives

In this module, you will:

  • Learn more about using parameters
  • Understand method scope
  • Understand pass-by-reference and pass-by-value parameter types
  • Learn how to use optional and named arguments

Prerequisites

  • Experience using C# data types including int, string, arrays, and 2D arrays
  • Experience using switch statements, if-else statements, and for-loops
  • Experience using the Random class to generate a random number.
  • Basic understanding of C# methods

Exercise

Use parameters in methods

When creating methods, you'll often want to provide some information for the method to use. Information consumed by a method is called a parameter. You can supply as many parameters as needed to accomplish its task, or none at all.

The terms 'parameter' and 'argument' are often used interchangeably. However, 'parameter' refers to the variable in the method signature. The 'argument' is the value passed when the method is called.

Add parameters to methods

Parameters in a method work similar to variables. A parameter is defined by specifying the data type followed by the name of the parameter. Parameters are declared in the method signature, and the values for the parameters are provided by the method caller instead of being initialized inside the method itself. Consider the following code:

    CountTo(5);

    void CountTo(int max) {
        for (int i = 0; i < max; i++) {
            Console.Write($"{i}, ");
        }
    }

In this example, the method CountTo accepts an integer parameter named max. The parameter is referenced in the for loop of the method. When CountTo is called, the integer 5 is supplied as an argument.

In this exercise, you'll learn how to create and use your own method parameters.

Create a method with parameters

In this task, you'll create a method that adjusts scheduled times to a different GMT time zone. The method should accept a list of times, the current time zone, and the new time zone. Let's get started!

Enter the following code into the code editor:

int[] schedule = {800, 1200, 1600, 2000};

To create a method with parameters, enter the following code on a new blank line:

void DisplayAdjustedTimes(int[] times, int currentGMT, int newGMT) {

}

Notice that parameters are declared similar to the way you declare variables, using the data type followed by the variable name. You can use parameters of any data type, such as string, bool, int, arrays, and more! Multiple parameters in a method are always comma separated.

Enter the following code in the DisplayAdjustedTimes method:

int diff = 0;
if (Math.Abs(newGMT) > 12 || Math.Abs(currentGMT) > 12) {
    Console.WriteLine("Invalid GMT");
}

Notice how you don't have to declare the variables newGMT and currentGMT since they're already declared in the method signature. You also don't initialize the variables since the method assumes the caller supplies those arguments with assigned values.

In this step, you create int diff to store the time difference and then check to see that the provided GMT values are between -12 and 12. Using Math.Abs gives you the absolute value of a number, so the GMT values are invalid if they're greater than 12.

To calculate the time difference, update the DisplayAdjustedTimes method as follows:

int diff = 0;
if (Math.Abs(newGMT) > 12 || Math.Abs(currentGMT) > 12) {
    Console.WriteLine("Invalid GMT");
} else if (newGMT <= 0 && currentGMT <= 0 || newGMT >= 0 && currentGMT >= 0) {
    diff = 100 * (Math.Abs(newGMT) - Math.Abs(currentGMT));
} else {
    diff = 100 * (Math.Abs(newGMT) + Math.Abs(currentGMT));
}

In this code, you check to see whether you need to add or subtract the absolute values of the GMT time zones to get the difference in hours. If the GMT values share the same sign (both positive or both negative), then the hours difference is equal to the difference between the two numbers. If the GMT values have opposite signs, then the difference is equal to the sum of the two numbers. Since hours are represented in hundreds, you multiply the result by 100.

To display the results, enter the following code at the end of the DisplayAdjustedTimes method:

for (int i = 0; i < times.Length; i++) {
    int newTime = ((times[i] + diff)) % 2400;
    Console.WriteLine($"{times[i]} -> {newTime}");
}

To call your method, enter the following code after the int[] schedule variable declaration:

DisplayAdjustedTimes(schedule, 6, -6);

Notice that both variables and literals can be supplied as arguments to a method. By using input parameters, the method isn't restricted to using the values of global variables.

Check Your Work

At the Terminal command prompt, enter dotnet run

Verify that your code produces the following output:

800 -> 2000
1200 -> 0
1600 -> 400
2000 -> 800

If your code displays different results, you'll need to review your code to find your error and make updates. Run the code again to see if you've fixed the problem. Continue updating and running your code until your code produces the expected results.

Recap

Here's what you've learned about parameters so far:

  • Information can be passed to methods in the form of parameters.
  • Parameters are declared in the method signature.
  • Multiple parameters are separated by commas.
  • Methods can accept variable or literal arguments.

Exercise

Understand method scope

for loops, if-else statements, and methods all represent different types of code blocks. Each code block has its own 'scope'. 'Scope' is the region of a program where certain data is accessible. Variables declared inside a method, or any code block, are only accessible within that region. As programs become more complicated, this pattern helps programmers consistently use clearly named variables and maintain easy to read code.

In this exercise, you'll learn more about method scope by working with different types of methods and variables.

Test variable scope

Statements declared outside of any code block are called top-level statements. Variables declared in top-level statements are called 'global variables'. Global variables aren't restricted to any scope, and can be used anywhere throughout the program. Global variables can be useful for different methods that need to access the same data. However, it's important to pay attention to variable names in different scopes.

Enter the following code into the code editor:

string[] students = {"Jenna", "Ayesha", "Carlos", "Viktor"};

DisplayStudents(students);
DisplayStudents(new string[] {"Robert","Vanya"});

void DisplayStudents(string[] students) {
    foreach (string student in students) {
        Console.Write($"{student}, ");
    }
    Console.WriteLine();
}

In this code, you create a global students array, and a method DisplayStudents that accepts a parameter with the same name.

Save and run the code to observe the following output:

Jenna, Ayesha, Carlos, Viktor, 
Robert, Vanya, 

Notice that the method parameter student takes precedence over the global student array. It's important to be deliberate about what global variables you want your methods to use.

Delete the previous code.

Enter the following code into the Editor:

PrintCircleArea(12);

void PrintCircleArea(int radius) {
    double pi = 3.14159;
    double area = pi * (radius * radius);
    Console.WriteLine($"Area = {area}");
}

This code calculates and displays the area of a circle.

Attempt to reference the variables inside of the PrintCircleArea method by updating your code as follows:

PrintCircleArea(12);
double circumference = 2 * pi * radius;

Error messages appear informing you that the names pi and radius don't exist in the current scope. Those variables only exist within the PrintCircleArea method scope.

Delete the incorrect code and add the following code:

void PrintCircleCircumference(int radius) {
    double pi = 3.14159;
    double circumference = 2 * pi * radius;
    Console.WriteLine($"Circumference = {circumference}");
}

Since the variable pi is set to the same fixed value and used in both methods, this value is a good candidate for a global variable. In this example, radius isn't a global variable so that you can call the methods with different values of radius without updating a variable each time.

Update your code to the following:

double pi = 3.14159;

void PrintCircleArea(int radius) {
    double area = pi * (radius * radius);
    Console.WriteLine($"Area = {area}");
}

void PrintCircleCircumference(int radius) {
    double circumference = 2 * pi * radius;
    Console.WriteLine($"Circumference = {circumference}");
}

Now both methods can reference the same value of pi without needing to define it. You might have already guessed that methods can call other methods. Generally, as long as a method is defined within the scope of your program, it can be called anywhere.

Add a new method to your code as follows:

double pi = 3.14159;
PrintCircleInfo(12);
PrintCircleInfo(24);

void PrintCircleInfo(int radius) {
    Console.WriteLine($"Circle with radius {radius}");
    PrintCircleArea(radius);
    PrintCircleCircumference(radius);
}

In this code, you create a new method PrintCircleInfo to call the existing methods. The value of radius is also passed down to each method. Creating modularized methods can help keep your code organized and easy to read.

Save and run the code to observe the following output:

Circle with radius 12
Area = 452.38896
Circumference = 75.39815999999999
Circle with radius 24
Area = 1809.55584
Circumference = 150.79631999999998

Recap

Here's what you've learned about method scope so far:

Variables declared inside of a method are only accessible to that method. Variables declared in top-level statements are accessible throughout the program. Methods don't have access to variables defined within different methods. Methods can call other methods.


Exercise

Use value and reference type parameters

In C#, variables can be categorized into two main types, value types and reference types. These types describe how variables store their values.

Value types such as int, bool, float, double, and char directly contain values. Reference types such as string, array, and objects (such as instances of Random) don't store their values directly. Instead, reference types store an address where their value is being stored.

Parameters passed by value and passed by reference

When an argument is passed to a method, value type variables have their values copied into the method. Each variable has its own copy of the value, so the original variable isn't modified.

With reference types, the address of the value is passed into the method. The variable given to the method references the value at that address, so operations on that variable affect the value that is referenced by the other.

Note
It is important to remember that string is a reference type, but it is immutable. That means once it has been assigned a value, it can't be altered. In C#, when methods and operators are used to modify a string, the result that is returned is actually a new string object.

In this exercise, you'll learn more about passing reference and value type arguments into methods.

Test pass by value

Enter the following code into the code editor:

int a = 3;
int b = 4;
int c = 0;

Multiply(a, b, c);
Console.WriteLine($"global statement: {a} x {b} = {c}");

void Multiply(int a, int b, int c) {
    c = a * b;
    Console.WriteLine($"inside Multiply method: {a} x {b} = {c}");
}

The variables a, b, and c are passed to the Multiply method. The values of the variables are printed during the method execution, and printed again after the method is complete.

Integers are value types, which have their values copied when passed into methods. What do you think the output of c will be?

Save and run your code to observe the following output:

inside Multiply method: 3 x 4 = 12
global statement: 3 x 4 = 0

Notice that the value of c is only altered within the Multiply method. Outside of the method, c retains its original value.

Test pass by reference

Enter the following code into the code editor:

int[] array = {1, 2, 3, 4, 5};

PrintArray(array);
Clear(array);
PrintArray(array);

void PrintArray(int[] array) {
    foreach (int a in array) {
        Console.Write($"{a} ");
    }
    Console.WriteLine();
}

void Clear(int[] array) {
    for (int i = 0; i < array.Length; i++) {
        array[i] = 0;
    }
}

The code begins with array initialized to contain some integer values. The values are displayed using the PrintArray method. The Clear method is called on the array, and then the array is printed again.

Arrays are reference types. Reference types store the address of their values in memory. What do you think the output will be?

Save and run your code to observe the following output:

1 2 3 4 5 
0 0 0 0 0

Notice that the array remains altered outside of the Clear method scope. This happens because the Clear method updated the values stored at each address.

Test with strings

Earlier, you learned that strings are an immutable type. Even though a string is a reference type, unlike an array, its value can't be altered once it's assigned. You might have noticed this if you've used methods such as string.Replace or string.ToUpper. In this task, you'll learn to correct a common error found when working with strings.

Enter the following code into the code editor:

string status = "Healthy";

Console.WriteLine($"Start: {status}");
SetHealth(status, false);
Console.WriteLine($"End: {status}");

void SetHealth(string status, bool isHealthy) {
    status = (isHealthy ? "Healthy" : "Unhealthy");
    Console.WriteLine($"Middle: {status}");
}

Save and run your code to observe the following output:

Start: Healthy
Middle: Unhealthy
End: Healthy

If the SetHealth method didn't output the status, you might have assumed the method didn't execute correctly. Instead, a new string with the value "Unhealthy" was created and then lost in the method scope.

To correct this problem, you can change SetHealth to use the global status variable instead.

Update your code as follows:

string status = "Healthy";

Console.WriteLine($"Start: {status}");
SetHealth(false);
Console.WriteLine($"End: {status}");

void SetHealth(bool isHealthy) {
    status = (isHealthy ? "Healthy" : "Unhealthy");
    Console.WriteLine($"Middle: {status}");
}

In this code, you overwrite the global status variable with the new string value.

Save and run your code to observe the following output:

Start: Healthy
Middle: Unhealthy
End: Unhealthy

Now the updated string is captured and stored correctly.

Recap

Here's what you've learned about value type and reference type parameters so far:

  • Variables can be categorized as value types and reference types.
  • Value types directly contain values, and reference types store the address of the value.
  • Methods using value type arguments create their own copy of the values.
  • Methods that perform changes on an array parameter affect the original input array.
  • String is an immutable reference type.
  • Methods that perform changes on a string parameter don't affect the original string.