Methods are type members that execute a block of statements.
A method that performs some actions but does not return a value shows this by showing that it returns the void
type before the name of the method. A method that performs some actions and returns a value shows this by showing that it returns the type of that value before the name of the method.
For example, you will create two methods:
WriteToConsole
: This will perform an action (writing a line), but it will return nothing from the method, indicated by the void
keywordGetOrigin
: This will return a string value, indicated by the string
keywordInside the Person
class, statically import System.Console
, and then add the following code:
// methods public void WriteToConsole() { WriteLine($"{Name} was born on {DateOfBirth:dddd, d MMMM yyyy}"); } public string GetOrigin() { return $"{Name} was born on {HomePlanet}"; }
Inside the Main
method, add the following code:
p1.WriteToConsole(); WriteLine(p1.GetOrigin());
Run the application and view the output:
Bob Smith was born on Wednesday, 22 December 1965 Bob Smith was born on Earth
Each method can only return a single value that has a single type. That type could be a simple type, such as string
in the previous example, a complex type, such as Person
, or a collection type, such as List<Person>
.
Imagine that we want to define a method that returns both a string
and an int
value. We could define a new type with a string
field and an int
field and return an instance of that complex type. Or we could use tuples.
Tuples have been a part of some languages such as F# since their first version, but .NET only added support for them in .NET 4.0 with the System.Tuple
type, and it was only in C# 7 that the C# language added syntax support for tuples.
While adding tuple support to the C# 7 language, .NET also added a new System.ValueTuple
type that is more efficient in some common scenarios than the old .NET 4.0 System.Tuple
type.
In Visual Studio 2017, in Solution Explorer, in the Ch06_PacktLibrary project, right-click on Dependencies and choose Manage NuGet Packages...
Click the Browse tab, search for System.ValueTuple
, select the package, and click Install, as shown in the following screenshot:
In the Review Changes dialog, click OK.
In the License Agreement dialog, click I Accept.
In Visual Studio Code, in the Ch06_PacktLibrary
project, open the Ch06_PacktLibrary.csproj
file and add a package reference, as shown in the following markup:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard1.4</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="System.ValueTuple" Version="4.3.0" /> </ItemGroup> </Project>
When prompted, restore dependency packages.
In the Ch06_PeopleApp
project, open the Ch06_PeopleApp.csproj
file, and add the same package reference, and when prompted, restore dependency packages.
First, we will define a method that would work in C# 4 or later. Then we will use the new C# 7 language support.
Inside the Person
class, add the following code to define two methods, the first with a return type of System.Tuple<string, int>
and the second with a return
type using C# 7 syntax:
// the old C# 4 and .NET 4.0 System.Tuple type public Tuple<string, int> GetFruitCS4() { return Tuple.Create("Apples", 5); } // the new C# 7 syntax and new System.ValueTuple type public (string, int) GetFruitCS7() { return ("Apples", 5); }
Inside the Main
method, add the following code:
Tuple<string, int> fruit4 = p1.GetFruitCS4(); WriteLine($"There are {fruit4.Item2} {fruit4.Item1}."); (string, int) fruit7 = p1.GetFruitCS7(); WriteLine($"{fruit7.Item1}, {fruit7.Item2} there are.");
Run the application and view the output:
There are 5 Apples. Apples, 5 there are.
To access the fields of a tuple, the default names are Item1, Item2, and so on.
You can explicitly specify the field names. Inside the Person
class, add the following code to define a method:
public (string Name, int Number) GetNamedFruit() { return (Name: "Apples", Number: 5); }
Inside the Main
method, add the following code:
var fruitNamed = p1.GetNamedFruit(); WriteLine($"Are there {fruitNamed.Number} {fruitNamed.Name}?");
Run the application and view the output:
Are there 5 Apples?
You can also deconstruct tuples into separate variables. The deconstructing declaration has the same syntax as named field tuples, but without a variable name for the whole tuple. This has the effect of splitting the tuple into its parts and assigning those parts to new variables.
Inside the Main
method, add the following code:
(string fruitName, int fruitNumber) = p1.GetFruitCS7(); WriteLine($"Deconstructed: {fruitName}, {fruitNumber}");
Run the application and view the output:
Deconstructed: Apples, 5
Deconstruction is not just for tuples. Any type can be deconstructed if it has a Deconstructor method. You can read about this at the following link: https://docs.microsoft.com/en-us/dotnet/articles/csharp/tuples#deconstruction.
Methods can have parameters passed to them to change their behavior. Parameters are defined a bit like variable declarations, but inside the parentheses of the method.
Inside the Person
class, add the following code to define two methods, the first without parameters and the second with one parameter:
public string SayHello() { return $"{Name} says 'Hello!'"; } public string SayHelloTo(string name) { return $"{Name} says 'Hello {name}!'"; }
Inside the Main
method, add the following code:
WriteLine(p1.SayHello()); WriteLine(p1.SayHelloTo("Emily"));
Run the application and view the output:
Bob Smith says 'Hello!' Bob Smith says 'Hello Emily!'
When typing a statement that calls a method, IntelliSense, in both Visual Studio 2017 and Visual Studio Code with the appropriate language extension installed, should show useful tooltips.
In Visual Studio 2017, you can press Ctrl + K, I or go to Edit | IntelliSense | Quick Info to see Quick Info of a method, as shown in the following screenshot:
Here is the SayHelloTo
method's quick info:
Instead of having two different method names, we could give both methods the same name. This is allowed because the methods each have a different signature. A method signature is a list of parameter types that can be passed when calling the method.
In the Person
class, change the name of the SayHelloTo
method to SayHello
. Now, when you view the quick info for the method, it tells you that it has one additional overload:
Another way to simplify methods is to make parameters optional. You make a parameter optional by assigning a default value inside the method parameter list. Optional parameters must always come last in the list of parameters.
You will now create a method with three optional parameters.
Inside the Person
class, add the following code:
public void OptionalParameters(string command = "Run!", double number = 0.0, bool active = true) { WriteLine($"command is {command}, number is {number}, active is {active}"); }
Inside the Main
method, add the following code:
p1.OptionalParameters();
Watch IntelliSense's Quick Info appear as you type the code, and you will see a tooltip, showing the three optional parameters with default values, as shown in the following screenshot:
When you run the application, you will see the following output:
command is Run!, number is 0, active is True
In the Main
method, add the following line, which passes a string for the command and a double for the number parameters:
p1.OptionalParameters("Jump!", 98.5);
Run the application and see the output:
command is Jump!, number is 98.5, active is True
The default values for command and number have been replaced, but the default for active is still true.
Optional parameters are often combined with naming parameters when you call the method, because naming a parameter allows the values to be passed in a different order than how they were declared.
In the Main
method, add the following line, which passes a string for the command and a double for the number parameters but using named parameters, so that the order they are passed can be swapped around:
p1.OptionalParameters(number: 52.7, command: "Hide!");
Run the application and see the output:
command is Hide!, number is 52.7, active is True
You can even use named parameters to skip over optional parameters.
In the Main
method, add the following line that passes a string for the command using positional order, skips the number parameter, and uses the named active parameter:
p1.OptionalParameters("Poke!", active: false);
Run the application and see the output:
command is Poke!, number is 0, active is False
3.144.39.144