Method Behaviour Parameters in .Net
Method Behaviour Parameters in .Net
Both value and reference arguments can be provided to parameters in C#. Keep in mind that C# types come in both reference (class) and value (struct) types:
- Pass by value means passing a copy of the variable to the method.
- Pass by reference means passing access to the variable to the method.
- A variable of a reference type contains a reference to its data.
- A variable of a value type contains its data directly.
Because a struct is a value type, the method receives and uses a copy of the struct parameter when you send it by value to it. The method cannot alter the original struct in any manner because it has no access to it in the calling method.
A class instance is a reference type. A copy of the reference to the class instance is supplied to the method when a reference type is passed as a value to a method. In other words, the calling method keeps the instance's original address while passing a copy of it to the called method. The address of the class instance in the calling method and the address of the parameter in the called method both point to the same object.The called method cannot modify the address of the class instance in the calling method because the argument simply carries a copy of the address. The called method can, however, access the class members that both the original address and the copy of the address reference by using the copy of the address. The original class instance in the calling method also changes if the called method modifies a class member.
The result of the next case demonstrates the distinction. Calling method ClassTaker changes the value of the willIChange field of the class instance because the method uses the address in the parameter to locate the desired field of the class instance. Because the value of the argument is a copy of the struct itself and not a copy of its address, the call to method StructTaker does not update the willIChange field of the struct in the calling method. When the call to StructTaker is finished, the copy that was made is gone.
class TheClass
{
public string? willIChange;
}
struct TheStruct
{
public string willIChange;
}
class TestClassAndStruct
{
static void ClassTaker(TheClass c)
{
c.willIChange = "Changed";
}
static void StructTaker(TheStruct s)
{
s.willIChange = "Changed";
}
public static void Main()
{
TheClass testClass = new TheClass();
TheStruct testStruct = new TheStruct();
testClass.willIChange = "Not Changed";
testStruct.willIChange = "Not Changed";
ClassTaker(testClass);
StructTaker(testStruct);
Console.WriteLine("Class field = {0}", testClass.willIChange);
Console.WriteLine("Struct field = {0}", testStruct.willIChange);
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Class field = Changed
Struct field = Not Changed
*/
How an argument is passed, and whether it's a reference type or value type controls what modifications made to the argument are visible from the caller.
Pass a value type by value.
When you pass a value type by value:
- If the method assigns the parameter to refer to a different object, those changes aren't visible from the caller.
- If the method modifies the state of the object referred to by the parameter, those changes aren't visible from the caller.
The transmission of value-type arguments by value is demonstrated in the example below. The method SquareIt receives the variable n as a value argument. The variable's initial value is unaffected by any changes made inside the method.
int n = 5;
System.Console.WriteLine("The value before calling the method: {0}", n);
SquareIt(n); // Passing the variable by value.
System.Console.WriteLine("The value after calling the method: {0}", n);
// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
static void SquareIt(int x)
// The parameter x is passed by value.
// Changes to x will not affect the original value of x.
{
x *= x;
System.Console.WriteLine("The value inside the method: {0}", x);
}
/* Output:
The value before calling the method: 5
The value inside the method: 25
The value after calling the method: 5
*/
The variable n is a value type. It contains its data, the value 5. When SquareIt is invoked, the contents of n are copied into the parameter x, which is squared inside the method. In Main, however, the value of n is the same after calling the SquareIt method as it was before. The change that takes place inside the method only affects the local variable x.
Pass a value type by reference
When you pass a value type by reference:
- If the method assigns the parameter to refer to a different object, those changes aren't visible from the caller.
- If the method modifies the state of the object referred to by the parameter, those changes are visible from the caller.
The following example is the same as the previous example, except that the argument is passed as a ref parameter. The value of the underlying argument, n, is changed when x is changed in the method.
int n = 5;
System.Console.WriteLine("The value before calling the method: {0}", n);
SquareIt(ref n); // Passing the variable by reference.
System.Console.WriteLine("The value after calling the method: {0}", n);
// Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
static void SquareIt(ref int x)
// The parameter x is passed by reference.
// Changes to x will affect the original value of x.
{
x *= x;
System.Console.WriteLine("The value inside the method: {0}", x);
}
/* Output:
The value before calling the method: 5
The value inside the method: 25
The value after calling the method: 25
*/
In this example, it is not the value of n that is passed; rather, a reference to n is passed. The parameter x is not an int; it is a reference to an int, in this case, a reference to n. Therefore, when x is squared inside the method, what actually is squared is what x refers to, n.
Pass a reference type by value.
When you pass a reference type by value:
If the method assigns the parameter to refer to a different object, those changes aren't visible from the caller. If the method modifies the state of the object referred to by the parameter, those changes are visible from the caller. The following example demonstrates passing a reference-type parameter, arr, by value, to a method, Change. Because the parameter is a reference to arr, it is possible to change the values of the array elements. However, the attempt to reassign the parameter to a different memory location only works inside the method and does not affect the original variable, arr.
int[] arr = { 1, 4, 5 };
System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr[0]);
Change(arr);
System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr[0]);
static void Change(int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] { -3, -1, -2, -3, -4 }; // This change is local.
System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]);
}
/* Output:
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: 888
*/
The array, arr, which is a reference type, is supplied to the method in the previous example without the ref parameter. In this scenario, the method receives a copy of the reference that points to arr. The result demonstrates that the procedure can change an array element's contents, in this case from 1 to 888. However, the variable pArray now references a new array when a new chunk of memory is allocated inside the Change function by using the new operator. Any modifications made after that won't have an impact on the initial array, arr, which is formed inside Main. In actuality, this example creates two arrays—one in Main and one in the Change method.
GitHub 💡