Modifiers 2020
Methods can be implemented within the scope of classes or structures and prototyped within interface types. Methods may be decorated with various keywords (internal, virtual, public, new, etc.) to qualify their behavior.
While the definition of a method in C# is straightforward, there are a handful of keywords that we can use to control how arguments are passed to the method.
TABLE
(None) | If a parameter is not marked with a parameter modifier, it is assumed to be passed by value, meaning the called method receives a copy of the original data. |
out | Output parameters must be assigned by the method being called (and therefore are passed be reference). If the called method fails to assign output parameters, we are issued a compiler error. |
ref | The value is initially assigned by the caller and may be optionally reassigned by the called method (as the data is also passed by reference). No compiler error is generated if the called method fails to assign a ref parameter. |
params | This parameter modifier allows us to send in a variable number of arguments as a single logical parameter. A method can have only a single params modifier, and it must be the final parameter of the method. |
The default manner in which a parameter is sent into a function is by value. In other words, if we do not mark an argument with a parameter-centric modifier, a copy of the data is passed into the function. Exactly what is copied will depend on whether the parameter is a value type of a reference type.
// Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyFirstCSharpCode { class Program { static int add(int x, int y) { int ans = x + y; x = 100; y = 200; return ans; } static void Main(string[] args) { int a = 10, b = 20; Console.WriteLine("Before call add(): a = {0}, b = {1}", a, b); Console.WriteLine("Ans = {0}", add(a, b)); Console.WriteLine("Before call add(): a = {0}, b = {1}", a, b); Console.ReadLine(); } } }
Output is:
Before call add(): a = 10, b = 20 Ans = 30 Before call add(): a = 10, b = 20
Numerical data is value types. So, if we change the values of the parameters within the scope of the member, the caller is unaware, given that we are changing the values on a copy of the caller's data:
static int add(int x, int y) { ... x = 100; y = 200; ... }
In this section, we are dealing with output modifier.
Methods that have been defined to take output parameters (via the out keyword) are under obligation to assign them to an appropriate value before exiting the method.
To demonstrate, here is an alternative version of the add() method that returns the sum of two integers using the out modifier while the physical return value of this method is now void.
// Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyFirstCSharpCode { class Program { static void add(int x, int y, out int ans) { ans = x + y; } static void Main(string[] args) { int answer; add(2010, 2011, out answer); Console.WriteLine("2010 + 2011 = {0}", answer); Console.ReadLine(); } } }
Output from the run is:
2010 + 2011 = 4021
Calling a method with output parameter also requires the use of the out modifier. Local variables passed as output variables are not required to be assigned before use.
The example above is just for demonstration purpose, and actually we have no reason to return the value of our summation using an output parameter. But the out modifier does serve a very useful purpose: it allows the called to obtain multiple return values from a single method invocation.
// Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyFirstCSharpCode { class Program { static void GetMultipleValues(out int a, out string s, out bool b) { a = 2010; s = "string as one of multiple return values"; b = true; } static void Main(string[] args) { int i; string str; bool b; GetMultipleValues(out i, out str, out b); Console.WriteLine("Int is: {0}", i); Console.WriteLine("String is: {0}", str); Console.WriteLine("Boolean is: {0}", b); Console.ReadLine(); } } }
Output is:
Int is: 2010 String is: string as one of multiple return values Boolean is: True
Methods that define output parameters must assign the parameters to a valid value before exiting the methods. So, the following method will result in a compiler error, as the integer parameter has not been assigned within the method scope:
static void ThisIsCompilerError(out in myInt) { Console.WriteLine("Error: myInt not assigned"); }
Reference parameters are necessary when we want to allow a method to operate on various data points declared in the caller's scope such as sorting or swapping routine. Here is the distinction between output and reference parameters:
- Output parameters do not need to be initialized before they passed to the method. It's because the method must assign output parameters before exiting.
- Reference parameters must be initialized before they are passed to the method. It's because we are passing a reference to an existing variable. If we don't assign it to an initial value, that would be the equivalent of operating on an unassigned local variable.
Let's look at the following example which uses ref keyword by way of a method that swaps two strings:
// Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyFirstCSharpCode { class Program { static void SwapStrings(ref string s1, ref string s2) { string temp = s1; s1 = s2; s2 = temp; } static void Main(string[] args) { string s1 = "Edsger"; string s2 = "Dijkstra"; Console.WriteLine("Before: {0} {1}", s1, s2); SwapStrings(ref s1, ref s2); Console.WriteLine("After: {0} {1}", s1, s2); Console.ReadLine(); } } }
With the output:
Before: Edsger Dijkstra After: Dijkstra Edsger
The caller has assigned an initial value to local string data, s1 and s2. Once the call to SwapStrings() returns, s1 now contains the value Dijkstra while s2 holds the value Edsger.
C# supports the use of parameter arrays. To understand the role of the params keyword, we must understand how to manipulate C# arrays.
The params keyword allows us to pass into a method a variable number of parameters of the same type as a single logical parameter. Arguments marked with the params keyword can be processed if the caller sends in a strongly typed array or a comma-delimited list of items.
Assume we wish to create a function that allows the called to pass in any number of arguments and return the calculated average. If we were to prototype this method to take an array of double, this would force the caller to first define the array, then fill the array and finally pass it into the method. However, if we define Avarage() to take a params of integer data types, the caller can simply pass a comma-delimited list of doubles. The .NET runtime will automatically package the set of doubles into an array of type double behind the scenes:
// Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyFirstCSharpCode { class Program { static double Average(params double[] values) { Console.WriteLine("{0} doubles", values.Length); double sum = 0; if (values.Length == 0) return sum; for (int i = 0; i < values.Length; i++) sum += values[i]; return (sum / values.Length); } static void Main(string[] args) { double avg; avg = Average(82, 94, 72, 64, 97); Console.WriteLine("Average of data = {0}", avg); double[] data = { 82, 94, 72, 64, 97 }; avg = Average(data); Console.WriteLine("Average of data = {0}", avg); Console.WriteLine("Average of data = {0}", Average()); Console.ReadLine(); } } }
Output is:
5 doubles Average of data = 81.8 5 doubles Average of data = 81.8 0 doubles Average of data = 0
The method Average() has been defined to take a parameter array of doubles. What this method is actually saying is "Send me any number of doubles and I'll calculate the average." So, we can call the method in any of the ways shown above example.
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization