I'm going through some code that uses the new features of C# 7 and uses the ref locals & returns feature.
It seems pretty straight forward for value-types
where the ref local variable gets a reference (to the actual storage), and updating that updates the value of the original item.
A little explanation will help in understanding how memory references work in case of ref locals for the reference-types
. I'm pointing to the last line of the code below:
// A simple class
public class CoolClass
{
public string Name { get; set; }
public int Id { get; set; }
public CoolClass(string name, int id) => (Name, Id) = (name, id);
}
//Dummy method that returns first element with Id > 100
public CoolClass GetSpecialItem_Old(CoolClass[] arr)
{
for (int i = 0; i < arr.Length; i++)
if (arr[i].Id > 100)
{
return arr[i];
}
throw new Exception("Not found");
}
//Same as the other one, but returns by ref C# 7
public ref CoolClass GetSpecialItem_New(CoolClass[] arr)
{
for (int i = 0; i < arr.Length; i++)
if (arr[i].Id > 100)
{
return ref arr[i];
}
throw new Exception("Not found");
}
public void TestMethod()
{
var arr = new CoolClass[]
{
new CoolClass("A", 10),
new CoolClass("B", 101),
new CoolClass("C", 11)
};
var byVal = GetSpecialItem_Old(arr); //gets back arr[1]
byVal.Name = "byVal"; //arr[1] = { "byVal", 101 }
byVal = new CoolClass("newByVal", 25); //arr[1] = { "byVal", 101 }
ref var byRef = ref GetSpecialItem_New(arr); //gets back arr[1]
byRef.Name = "byRef"; //arr[1] = { "byRef", 101 }
//Here it has a different behaviour
byRef = new CoolClass("newByRef", 50); //arr[1] = { "newByRef", 50 }
}
The fact that the original designers of C# named the feature "ref" was in my opinion a bad idea. It leads one to confuse reference types and "ref" parameters / returns. The better way to think of "ref" is "alias". That is, ref gives you another name for an existing variable.
In your program, byRef
is another name for arr[1]
regardless of whether arr[1]
is of value type or reference type. If arr[1]
is a string variable (remember, array elements are variables; you can change them) then byref
is a string variable also, and it is the same string variable with a different name.
Note that arr
is also a variable; if you change the value of arr
, then byRef
does not come along for the ride. It remains an alias for the same slot of the same array, regardless of the value of arr
.
So when you say
ref var byRef = ref GetSpecialItem_New(arr); //gets back arr[1]
Then
byRef.Name = "byRef";
is exactly the same as
arr[1].Name = "byRef";
And when you say
byRef = new CoolClass("newByRef", 50);
That's exactly the same as
arr[1] = new CoolClass("newByRef", 50);
With the caveat that if you changed arr
after assigning byRef
, you'd still have an alias to the original arr[1]
.
Again: byRef
is simply another way to spell arr[1]
, such that it always uses the value of arr
that it had when byRef
was assigned. It's not different for value types or reference types.
In contrast, byVal
is NOT an alias for arr[1]
. It is a second variable that has a copy of the contents of arr[1]
. When you assign to byVal
you are not assigning to arr[1]
. You're assigning to byVal
, which is a different variable.
The contents of arr[1]
is a reference, and that reference is copied to byVal
, a separate storage location entirely.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With