Skip to content

Latest commit

 

History

History
305 lines (199 loc) · 24.3 KB

methods.md

File metadata and controls

305 lines (199 loc) · 24.3 KB
title description keywords author ms.author ms.date ms.topic ms.prod ms.technology ms.devlang ms.assetid
Methods - C# Guide
Overview of methods, method parameters, and method return values
.NET, .NET Core, C#
rpetrusha
ronpet
10/26/2016
article
.net
devlang-csharp
csharp
577a8527-1081-4b36-9b9e-0685b6553c6e

Methods

A method is a code block that contains a series of statements. A program causes the statements to be executed by calling the method and specifying any required method arguments. In C#, every executed instruction is performed in the context of a method. The Main method is the entry point for every C# application and it is called by the common language runtime (CLR) when the program is started.

Note

This topic discusses named methods. For information about anonymous functions, see Anonymous Functions.

This topic contains the following sections:

Method signatures

Methods are declared in a class or struct by specifying:

  • An optional access level, such as public or private. The default is private.
  • Optional modifiers such as abstract or sealed.
  • The return value, or void if the method has none.
  • The method name.
  • Any method parameters. Method parameters are enclosed in parentheses and are separated by commas. Empty parentheses indicate that the method requires no parameters.

These parts together form the method signature.

Note

A return type of a method is not part of the signature of the method for the purposes of method overloading. However, it is part of the signature of the method when determining the compatibility between a delegate and the method that it points to.

The following example defines a class named Motorcycle that contains five methods:

[!code-csharpcsSnippets.Methods#40]

Note that the Motorcycle class includes an overloaded method, Drive. Two methods have the same name, but must be differentiated by their parameter types.

Method invocation

Methods can be either instance or static. Invoking an instance method requires that you instantiate an object and call the method on that object; an instance method operates on that instance and its data. You invoke a static method by referencing the name of the type to which the method belongs; static methods operate do not operate on instance data. Attempting to call a static method through an object instance generates a compiler error.

Calling a method is like accessing a field. After the object name (if you are calling an instance method) or the type name (if you are calling a static method), add a period, the name of the method, and parentheses. Arguments are listed within the parentheses, and are separated by commas.

The method definition specifies the names and types of any parameters that are required. When a caller invokes the method, it provides concrete values, called arguments, for each parameter. The arguments must be compatible with the parameter type, but the argument name, if one is used in the calling code, does not have to be the same as the parameter named defined in the method. In the following example, the Square method includes a single parameter of type int named i. The first method call passes the Square method a variable of type int named num; the second, a numeric constant; and the third, an expression.

[!code-csharpcsSnippets.Methods#74]

The most common form of method invocation used positional arguments; it supplies arguments in the same order as method parameters. The methods of the Motorcycle class can therefore be called as in the following example. The call to the Drive method, for example, includes two arguments that correspond to the two parameters in the method's syntax. The first becomes the value of the miles parameter, the second the value of the speed parameter.

[!code-csharpcsSnippets.Methods#41]

You can also used named arguments instead of positional arguments when invoking a method. When using named arguments, you specify the parameter name followed by a colon (":") and the argument. Arguments to the method can appear in any order, as long as all required arguments are present. The following example uses named arguments to invoke the TestMotorcycle.Drive method. In this example, the named arguments are passed in the opposite order from the method's parameter list.

[!code-csharpcsSnippets.Methods#45]

You can invoke a method using both positional arguments and named arguments. However, a positional argument cannot follow a named argument. The following example invokes the TestMotorcycle.Drive method from the previous example using one positional argument and one named argument.

[!code-csharpcsSnippets.Methods#46]

##Inherited and overridden methods ##

In addition to the members that are explicitly defined in a type, a type inherits members defined in its base classes. Since all types in the managed type system inherit directly or indirectly from the @System.Object class, all types inherit its members, such as @System.Object.Equals(System.Object), @System.Object.GetType, and @System.Object.ToString. The following example defines a Person class, instantiates two Person objects, and calls the Person.Equals method to determine whether the two objects are equal. The Equals method, however, is not defined in the Person class; it is inherited from @System.Object.

[!code-csharpcsSnippets.Methods#104]

Types can override inherited members by using the override keyword and providing an implementation for the overridden method. The method signature must be the same as that of the overriden method. The following example is like the previous one, except that it overrides the @Object.Equals(System.Object) method. (It also overrides the @Object.GetHashCode method, since the two methods are intended to provide consistent results.)

[!code-csharpcsSnippets.Methods#105]

Passing parameters

Types in C# are either value types or reference types. For a list of built-in value types, see Types and variables. By default, both value types and reference types are passed to a method by value.

Passing parameters by value

When a value type is passed to a method by value, a copy of the object instead of the object itself is passed to the method. Therefore, changes to the object in the called method have no effect on the original object when control returns to the caller.

The following example passes a value type to a method by value, and the called method attempts to change the value type's value. It defines a variable of type int, which is a value type, initializes its value to 20, and passes it to a method named ModifyValue that changes the variable's value to 30. When the method returns, however, the variable's value remains unchanged.

[!code-csharpcsSnippets.Methods#10]

When an object of a reference type is passed to a method by value, a reference to the object is passed by value. That is, the method receives not the object itself, but an argument that indicates the location of the object. If you change a member of the object by using this reference, the change is reflected in the object when control returns to the calling method. However, replacing the object passed to the method has no effect on the original object when control returns to the caller.

The following example defines a class (which is a reference type) named SampleRefType. It instantiates a SampleRefType object, assigns 44 to its value field, and passes the object to the ModifyObject method. This example does essentially the same thing as the previous example -- it passes an argument by value to a method. But because a reference type is used, the result is different. The modification that is made in ModifyObject to the obj.value field also changes the value field of the argument, rt, in the Main method to 33, as the output from the example shows.

[!code-csharpcsSnippets.Methods#42]

Passing parameters by reference

You pass a parameter by reference when you want to change the value of an argument in a method and want to refect that change when control returns to the calling method. To pass a parameter by reference, you use the ref or out keyword.

The following example is identical to the previous one, except the value is passed by reference to the ModifyValue method. When the value of the parameter is modified in the ModifyValue method, the change in value is reflected when control returns to the caller.

[!code-csharpcsSnippets.Methods#106]

A common pattern that uses by ref parameters involves swapping the values of variables. You pass two variables to a method by reference, and the method swaps their contents. The following example swaps integer values.

[!code-csharpcsSnippets.Methods#106]

Passing a reference-type parameter allows you to change the value of the reference itself, rather than the value of its individual elements or fields.

Parameter arrays

Sometimes, the requirement that you specify the exact number of arguments to your method is restrictive. By using the params keyword to indicate that a parameter is a parameter array, you allow your method to be called with a variable number of arguments. The parameter tagged with the params keyword must must be an array type, and it must be the last parameter in the method's parameter list.

A caller can then invoke the method in either of three ways:

  • By passing an array of the appropriate type that contains the desired number of elements.
  • By passing a comma-separated list of individual arguments of the appropriate type to the method.
  • By not providing an argument to the parameter array.

The following example defines a method named DoStringOperation that performs the string operation specified by its first parameter, a StringOperation enumeration member. The strings upon which it is to perform the operation are defined by a parameter array. The Main method illustrates all three ways of invoking the method. Note that the method tagged with the params keyword must be prepared to handle the case in which no argument is supplied for the parameter array, so that its value is null.

[!code-csharpcsSnippets.Methods#106]

Optional parameters and arguments

A method definition can specify that its parameters are required or that they are optional. By default, parameters are required. Optional parameters are specified by including the parameter's default value in the method definition. When the method is called, if no argument is supplied for an optional parameter, the default value is used instead.

The parameter's default value must be assigned by one of the following kinds of expressions:

  • A constant, such as a literal string or number.
  • An expression of the form new ValType, where ValType is a value type. Note that this invokes the value type's implicit default constructor, which is not an actual member of the type.
  • An expression of the form default(ValType), where ValType is a value type.

If a method includes both required and optional parameters, optional parameters are defined at the end of the parameter list, after all required parameters.

The following example defines a method, ExampleMethod, that has one required and two optional parameters.

[!code-csharpcsSnippets.Methods#21]

If a method with multiple optional arguments is invoked using positional arguments, the caller must supply an argument for all optional parameters from the first one to the last one for which an argument is supplied. In the case of the ExampleMethod method, for example, if the caller supplies an argument for the description parameter, it must also supply one for the optionalInt parameter. opt.ExampleMethod(2, 2, "Addition of 2 and 2"); is a valid method call; opt.ExampleMethod(2, , "Addition of 2 and 0); generates an "Argument missing" compiler error.

If a method is called using named arguments or a combination of positional and named arguments, the caller can omit any arguments that follow the last positional argument in the method call.

The following example calls the ExampleMethod method three times. The first two method calls use positional arguments. The first omits both optional arguments, while the second omits the last argument. The third method call supplies a positional argument for the required parameter, but uses a named argument to supply a value to the description parameter while omitting the optionalInt argument.

[!code-csharpcsSnippets.Methods#22]

The use of optional parameters affects overload resolution, or the way in which the C# compiler determines which particular overload should be invoked by a method call, as follows:

  • A method, indexer, or constructor is a candidate for execution if each of its parameters either is optional or corresponds, by name or by position, to a single argument in the calling statement, and that argument can be converted to the type of the parameter.
  • If more than one candidate is found, overload resolution rules for preferred conversions are applied to the arguments that are explicitly specified. Omitted arguments for optional parameters are ignored.
  • If two candidates are judged to be equally good, preference goes to a candidate that does not have optional parameters for which arguments were omitted in the call. This is a consequence of a general preference in overload resolution for candidates that have fewer parameters.

Return values

Methods can return a value to the caller. If the return type (the type listed before the method name) is not void, the method can return the value by using the return keyword. A statement with the return keyword followed by a variable, constant, or expression that matches the return type will return that value to the method caller. Methods with a non-void return type are required to use the return keyword to return a value. The return keyword also stops the execution of the method.

If the return type is void, a return statement without a value is still useful to stop the execution of the method. Without the return keyword, the method will stop executing when it reaches the end of the code block.

For example, these two methods use the return keyword to return integers:

[!code-csharpcsSnippets.Methods#44]

To use a value returned from a method, the calling method can use the method call itself anywhere a value of the same type would be sufficient. You can also assign the return value to a variable. For example, the following two code examples accomplish the same goal:

[!code-csharpcsSnippets.Methods#45]

[!code-csharpcsSnippets.Methods#46]

Using a local variable, in this case, result, to store a value is optional. It may help the readability of the code, or it may be necessary if you need to store the original value of the argument for the entire scope of the method.

Sometimes, you want your method to return more than a single value. Starting with C# 7.0, you can do this easily by using tuple types and tuple literals. The tuple type defines the data types of the tuple's elements. Tuple literals provide the actual values of the returned tuple. In teh following example, (string, string, string, int) defines the tuple type that is returned by the GetPersonalInfo method. The expression (per.FirstName, per.MiddleName, per.LastName, per.Age) is the tuple literal; the method returns the first, middle, and last name, along with the age, of a PersonInfo object.

public (string, string, string, int) GetPersonalInfo(string id)
{
    PersonInfo per = PersonInfo.RetrieveInfoById(id);
    if (per != null)
       return (per.FirstName, per.MiddleName, per.LastName, per.Age);
    else
       return null;
}

The caller can then consume the returned tuple with code like the following:

var person = GetPersonalInfo("111111111")
if (person != null)
   Console.WriteLine("{person.Item1} {person.Item3}: age = {person.Item4}");

Names can also be assigned to the tuple elements in the tuple type definition. The following example shows an alternate version of the GetPersonalInfo method that uses named elements:

public (string FName, string MName, string LName, int Age) GetPersonalInfo(string id)
{
    PersonInfo per = PersonInfo.RetrieveInfoById(id);
    if (per != null)
       return (per.FirstName, per.MiddleName, per.LastName, per.Age);
    else
       return null;
}

The previous call to the GetPersonInfo method can then be modified as follows:

var person = GetPersonalInfo("111111111");
if (person != null)
   Console.WriteLine("{person.FName} {person.LName}: age = {person.Age}");

If a method is passed an array as an argument and modifies the value of individual elements, it is not necessary for the method to return the array, although you may choose to do so for good style or functional flow of values. This is because C# passes all reference types by value, and the value of an array reference is the pointer to the array. In the following example, changes to the contents of the values array that are made in the DoubleValues method are observable by any code that has a reference to the array.

[!code-csharpcsSnippets.Methods#101]

Extension methods

Ordinarily, there are two ways to add a method to an existing type:

  • Modify the source code for that type. You cannot do this, of course, if you do not own the type's source code. And this becomes a breaking change if you also add any private data fields to support the method.
  • Define the new method in a derived class. A method cannot be added in this way using inheritance for other types, such as structures and enumerations. Nor can it be used to "add" a method to a sealed class.

Extension methods let you "add" a method to an existing type without modifying the type itself or implementing the new method in an inherited type. The extension method also does not have to reside in the same assembly as the type it extends. You call an extension method as if it were a defined member of a type.

For more information, see Extension Methods.

Async Methods

By using the async feature, you can invoke asynchronous methods without using explicit callbacks or manually splitting your code across multiple methods or lambda expressions.

If you mark a method with the async modifier, you can use the await operator in the method. When control reaches an await expression in the async method, control returns to the caller if the awaited task is not completed, and progress in the method with the await keyword is suspended until the awaited task completes. When the task is complete, execution can resume in the method.

Note

An async method returns to the caller when either it encounters the first awaited object that’s not yet complete or it gets to the end of the async method, whichever occurs first.

An async method can have a return type of @System.Threading.Tasks.Task, @System.Threading.Tasks.Task, or void. The void return type is used primarily to define event handlers, where a void return type is required. An async method that returns void can't be awaited, and the caller of a void-returning method can't catch exceptions that the method throws. C# 7, when it is released, will ease this restriction to allow an async method to return any task-like type.

In the following example, DelayAsync is an async method that has a return statement that returns an integer. Because it is an async method, its method declaration must have a return type of Task<int>. Because the return type is Task<int>, the evaluation of the await expression in DoSomethingAsync produces an integer, as the following int result = await delayTask statement demonstrates.

[!code-csharpcsSnippets.Methods#102]

An async method can't declare any ref or out parameters, but it can call methods that have such parameters.

For more information about async methods, see Asynchronous Programming with Async and Await, Control Flow in Async Programs, and Async Return Types.

Expression-bodied members

It is common to have method definitions that simply return immediately with the result of an expression, or that have a single statement as the body of the method. There is a syntax shortcut for defining such methods using =>:

public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public void Print() => Console.WriteLine(First + " " + Last);
// Works with operators, properties, and indexers too.
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);

If the method returns void or is an async method, the body of the method must be a statement expression (same as with lambdas). For properties and indexers, they must be read-only, and you do not use the get accessor keyword.

Iterators

An iterator performs a custom iteration over a collection, such as a list or an array. An iterator uses the yield return statement to return each element one at a time. When a yield return statement is reached, the current location is remembered so that the caller can request the next element in the sequence.

The return type of an iterator can be @System.Collections.IEnumerable, @System.Collections.Generic.IEnumerable%601, @System.Collections.IEnumerator, or @System.Collections.Generic.IEnumerator%601.

For more information, see Iterators.

See also

Access Modifiers
Static Classes and Static Class Members
Inheritance
Abstract and Sealed Classes and Class Members
params
out
ref
Passing Parameters