ms_learn_csharp/028_method_parameters/028_csharp.md
2024-08-11 14:50:17 -04:00

900 lines
27 KiB
Markdown

# 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:
```cs
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:
```cs
int[] schedule = {800, 1200, 1600, 2000};
```
To create a method with parameters, enter the following code on a new blank
line:
```cs
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:
```cs
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:
```cs
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:
```cs
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:
```cs
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:
```txt
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.
- [Program.cs](./parameters/Program.cs)
### 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:
```cs
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:
```txt
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:
```cs
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:
```cs
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:
```cs
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:
```cs
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:
```cs
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:
```txt
Circle with radius 12
Area = 452.38896
Circumference = 75.39815999999999
Circle with radius 24
Area = 1809.55584
Circumference = 150.79631999999998
```
- [Program.cs](./parameter_scope/Program.cs)
### 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:
```cs
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:
```cs
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:
```cs
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:
```txt
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:
```cs
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:
```txt
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:
```cs
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:
```txt
Start: Healthy
Middle: Unhealthy
End: Unhealthy
```
Now the updated string is captured and stored correctly.
- [Program.cs](./value_and_ref_params/Program.cs)
### 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.
---
## Exercise
### Methods with optional parameters
The C Sharp language allows the use of named and optional parameters. These
types of parameters let you select which arguments you want to supply to the
method, so you aren't restricted to the structure defined in the method
signature.
Named arguments allow you to specify the value for a parameter using its name
rather than position. Optional parameters allow you to omit those arguments
when calling the method.
In this exercise, you'll learn how to use both named and optional parameters.
#### Create an RSVP application
In this task, you'll create a brief application for guests to RSVP to an event.
The guests will provide their party size and any allergies. You'll also add the
option to restrict RSVPs to an invite-only guest list.
Type the following code into the Visual Studio Code Editor:
```cs
string[] guestList = {"Rebecca", "Nadia", "Noor", "Jonte"};
string[] rsvps = new string[10];
int count = 0;
void RSVP(string name, int partySize, string allergies, bool inviteOnly) {
if (inviteOnly) {
// search guestList before adding rsvp
}
rsvps[count] = $"Name: {name}, \tParty Size: {partySize}, \tAllergies: {allergies}";
count++;
}
void ShowRSVPs() {
Console.WriteLine("\nTotal RSVPs:");
for (int i = 0; i < count; i++){
Console.WriteLine(rsvps[i]);
}
}
```
In this code, you create variables to store the guest list and rsvps. The `RSVP`
method appends guest information to the list, and the `ShowRSVPs` method
displays the total RSVPs using the tab escape sequence to separate guest
information.
Enter the following code in the `RSVP` method to search the guest list:
```cs
if (inviteOnly) {
bool found = false;
foreach (string guest in guestList) {
if (guest.Equals(name)) {
found = true;
break;
}
}
if (!found) {
Console.WriteLine($"Sorry, {name} is not on the guest list");
return;
}
}
```
In this code, you check to see if the given name is equal to any of the names
on the guest list. If a match is found, you set `found` to true and break out
of the `foreach` loop. If `found` is false, you display a message and use the
`return` keyword to terminate the method.
Call your method by adding the following code above the `RSVP` method signature:
```cs
RSVP("Rebecca", 1, "none", true);
RSVP("Nadia", 2, "Nuts", true);
RSVP("Linh", 2, "none", false);
RSVP("Tony", 1, "Jackfruit", true);
RSVP("Noor", 4, "none", false);
RSVP("Jonte", 2, "Stone fruit", false);
ShowRSVPs();
```
Save and run the code to observe the following output:
```txt
Sorry, Tony is not on the guest list
Total RSVPs:
Name: Rebecca, Party Size: 1, Allergies: none
Name: Nadia, Party Size: 2, Allergies: Nuts
Name: Linh, Party Size: 2, Allergies: none
Name: Noor, Party Size: 4, Allergies: none
Name: Jonte, Party Size: 2, Allergies: Stone fruit
```
#### Use named arguments
When calling a method that accepts many parameters, it can be tricky to
understand what the arguments represent. Using named arguments can improve the
readability of your code. Use a named argument by specifying the parameter name
followed by the argument value. In this task, you'll practice using named
arguments.
Locate the following line of code: `RSVP("Linh", 2, "none", false);`
Update the method call as follows:
```cs
RSVP(
name: "Linh",
partySize: 2,
allergies: "none",
inviteOnly: false
);
```
Notice that you supply the name of the parameter, followed by a colon and the
value. This syntax defines a named argument. It isn't necessary to name all of
the arguments. For example, the following syntax is also valid:
- `RSVP("Linh", 2, allergies: "none", inviteOnly: false);`
- `RSVP("Linh", partySize: 2, "none", false);`
Named arguments, when used with positional arguments, are valid if they're used
in the correct position. Named arguments are also valid as long as they're not
followed by any positional arguments. For example, including `"Linh"` and `2` at
the end would be invalid:
`RSVP(allergies: "none", inviteOnly: false, "Linh", 2);`
If you entered this code, you would get the following error:
`Named argument 'allergies' is used out-of-position but is followed by an
unnamed argument`
Locate the following line of code: `RSVP("Tony", 1, "Jackfruit", true);`
Update the method call as follows:
```cs
RSVP(
"Tony",
inviteOnly: true,
allergies: "Jackfruit",
partySize: 1
);
```
Notice that the named arguments don't have to appear in the original order.
However, the unnamed argument `Tony` is a positional argument, and must appear
in the matching position.
Save and run the code to observe the following output:
```txt
Sorry, Tony is not on the guest list
Total RSVPs:
Name: Rebecca, Party Size: 1, Allergies: none
Name: Nadia, Party Size: 2, Allergies: Nuts
Name: Linh, Party Size: 2, Allergies: none
Name: Noor, Party Size: 4, Allergies: none
Name: Jonte, Party Size: 2, Allergies: Stone fruit
```
Notice that using named arguments doesn't change the output.
Declare optional parameters
A parameter becomes optional when it's assigned a default value. If an optional parameter is omitted from the arguments, the default value is used when the method executes. In this step, you'll make the parameters partySize, allergies and inviteOnly optional.
To define optional parameters, update the RSVP method signature as follows:
```cs
void RSVP(
string name,
int partySize = 1,
string allergies = "none",
bool inviteOnly = true
)
```
Take a moment to observe the syntax. The parameters are still separated by
commas, but the parameters `partySize`, `allergies`, and `inviteOnly` are each
assigned to a value.
Next, you'll update the calls to `RSVP` to apply the optional parameters.
Update your code to the following:
```cs
RSVP("Rebecca");
RSVP("Nadia", 2, "Nuts");
RSVP(name: "Linh", partySize: 2, inviteOnly: false);
RSVP("Tony", allergies: "Jackfruit", inviteOnly: true);
RSVP("Noor", 4, inviteOnly: false);
RSVP("Jonte", 2, "Stone fruit", false);
```
In each method call, notice that the name is never omitted. When a method is
called, all required arguments must always be included. However, any optional
arguments can be omitted.
In this code, you removed the arguments `1, "none", true` from Rebecca's rsvp.
Since these arguments match the default value, the result of Rebecca's rsvp is the same.
You removed the `inviteOnly` argument from Nadia's rsvp. Since the default
value of `inviteOnly` is `true`, the result of Nadia's rsvp is the same.
You removed the `partySize` argument from Tony's rsvp. If Tony had an
invitation, the default value of `partySize` would be used in the RSVP.
You removed the `allergies` argument from both Linh and Noor's rsvps. Their
rsvps will display the default value of `none` for "Allergies".
Save and run the code to observe the following output:
```txt
Sorry, Tony is not on the guest list
Total RSVPs:
Name: Rebecca, Party Size: 1, Allergies: none
Name: Nadia, Party Size: 2, Allergies: Nuts
Name: Linh, Party Size: 2, Allergies: none
Name: Noor, Party Size: 4, Allergies: none
Name: Jonte, Party Size: 2, Allergies: Stone fruit
```
Notice that the default values are used in place of omitted arguments, such as
`partySize` and `allergies`.
- [Program.cs](./rsvp_app/Program.cs)
### Recap
Here's what you've learned about optional and named arguments so far:
- Parameters are made optional by setting a default value in the method
signature.
- Named arguments are specified with the parameter name, followed by a colon
and the argument value.
- When combining named and positional arguments, you must use the correct order
of parameters.
---
## Exercise
### Complete the challenge to display email addresses
Code challenges reinforce what you've learned, and help you gain some
confidence before continuing on.
The focus of this challenge is to create a method with the proper parameters,
including an optional parameter.
### Display email addresses
Your challenge is to create a method that displays the correct email address
for both internal and external employees. You're given lists of internal and
external employee names. An employee's email address consists of their username
and company domain name.
The username format is the first two characters of the employee first name,
followed by their last name. For example, an employee named "Robert Bavin"
would have the username "robavin". The domain for internal employees is
"contoso.com".
In this challenge, you're given some starting code. You must decide how to
create and call a method to display email addresses.
### Code challenge: Add a method to display email addresses
In the code you start with, there are two arrays for internal and external
employees. Remember, the domain for internal employees is "contoso.com" and the
username for all employees is the first two characters of their first name,
followed by their full last name.
Your challenge is to create a method that will display the email address of
internal and external employees. The method should include an optional
parameter for the domain name of external employees.
Copy and paste the following code into the code editor.
```cs
string[,] corporate = {
{"Robert", "Bavin"}, {"Simon", "Bright"},
{"Kim", "Sinclair"}, {"Aashrita", "Kamath"},
{"Sarah", "Delucchi"}, {"Sinan", "Ali"}
};
string[,] external = {
{"Vinnie", "Ashton"}, {"Cody", "Dysart"},
{"Shay", "Lawrence"}, {"Daren", "Valdes"}
};
string externalDomain = "hayworth.com";
for (int i = 0; i < corporate.GetLength(0); i++) {
// display internal email addresses
}
for (int i = 0; i < external.GetLength(0); i++) {
// display external email addresses
}
```
Update the code to use a method to display the email addresses according to the
challenge specifications.
Use what you've learned about using parameters and optional arguments to
complete the update.
Verify that your code produces the following output:
```txt
robavin@contoso.com
sibright@contoso.com
kisinclair@contoso.com
aakamath@contoso.com
sadelucchi@contoso.com
siali@contoso.com
viashton@hayworth.com
codysart@hayworth.com
shlawrence@hayworth.com
davaldes@hayworth.com
```
Whether you get stuck and need to peek at the solution or you finish
successfully, continue on to view a solution to this challenge.
- [Program.cs](./disp_mail_addr/Program.cs)
---
### Summary
Your goal was to learn more about using parameters in methods and to understand
method scope. You learned about value and reference types and how data is
affected inside of a method. You also learned how to use named and optional
arguments to extend your method's capabilities.