C
C#•4w ago
Faker

ref keyword in C#

Hello guys, I'm just reading a bit about the ref keyword. I understood that when we use that keyword, we tell the compiler that we want to directly work with the memory address of the variable and not a copy of it (when using it inside a method). I have one question. I noticed that the ref keyword is used both when writing the arguments during the method call like callMethod(ref variable) and also during declaring the parameters of a method, we use the keyword, like static void changeValue(ref int x). Can someone explain why ref should be used in both the arguments and parameters, why not use it either in argument or parameter
22 Replies
Anchy
Anchy•4w ago
my guess is for clarity, so you know that you are passing by reference
333fred
333fred•4w ago
That is correct
Faker
FakerOP•4w ago
yep I see one last thing, consider the following code:
C#
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef x)
{
x = new TestRef();
x.Something = "Not just a changed TestRef, but a completely different TestRef object";
}
C#
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef x)
{
x = new TestRef();
x.Something = "Not just a changed TestRef, but a completely different TestRef object";
}
Normally, for objects, we store their reference, right? So any change made to an object directly affects the value at the memory address itself. So we don't really need to use the ref keyword with objects? But in the code above, why did we use it? What if we wouldn't use it?
333fred
333fred•4w ago
Try printing t.Something after calling DoSomething and see what happens 🙂 Both with and without ref
Faker
FakerOP•4w ago
Okk, so from what I've understood: Like every arguments passed without the ref keyword, we are always passing a copy of something. Similarly, here if we don't use the ref keyword, we would be passing a copy of the reference of t. Now, when we pass a copy of the reference (no use of ref keyword), a local copy of x is created. Initially, both x and t have the same reference so any changes made to x should also affect t. But let's say we want to change the reference of x. We do so using the new keyword to assign a different memory location. Now, since x is a copy of t, changing the reference of x won't change that of t. (I understood it but don't know it's still a bit unclear, I wanted to know how it would appear, like the memory structure, stack/heap etc). So when we write x.Something without the ref keyword, the x.Something is only persistent within the method itself, but after that, it will be garbage collected (when needed) when the method ends? Using the ref keyword allows the x.Something to persist though because both x and t have same reference
333fred
333fred•4w ago
No You were on the right track in the first half, but got a bit sidetracked Translate this to C and pointers. A class local is a pointer to the heap IE, if TestRef is a class, t has a type of TestRef* Then, if you say ref TestRef x, x has a type of TestRef** And it points the location of t on the stack So when you assign x = new TestRef(), you're actually doing *x = new TestRef() If you were to instead say TestRef x, without the leading ref, then the type of x is just TestRef* And x = new TestRef() is just that; no pointer indirection occurs So you just change your local, x, which has no impact on t
Faker
FakerOP•4w ago
yeah I see, like without the ref keyword, x = new TestRef();, there won't be able "indirect" pointer which will point to the reference where the reference of x is, like normally if we use the ref keyword, we would have 2 pointers, one pointing to where x is on the stack and the other pointing to where the reference of x is on the heap. Without the ref keyword, we only have the pointer which points to where reference of x is on the heap ?
333fred
333fred•4w ago
Yup
Faker
FakerOP•4w ago
yep I see, here, where did I go wrong pls, when speaking about the reference ?
333fred
333fred•4w ago
though because both x and t have same reference
This is the bad fundamental They don't have the same reference t has the reference x is a reference to t
Faker
FakerOP•4w ago
ah I see you means like t has the reference, say 0x100, x will also points to same reference of t, 0x100 ?
333fred
333fred•4w ago
No, I mean that x will point to t IE, TestRef** x = &t;
Faker
FakerOP•4w ago
ahh I see is it correct to say the following (assume that x has been passed by ref): When we write x = new TestRef(), internally, the compiler noticed that we are allocating a new memory address to x, so what happens internally is that we are directly modifying the address at which t is, like we are doing *x = new reference ?
333fred
333fred•4w ago
Mostly. It's not really about the compiler noticing anything, that's just how assignment with ref variables works in C# If you want to actually change the TestRef* that x points to, you'd have to use a different syntax
Faker
FakerOP•4w ago
ahh yeah I see true, because we can't really directly assign an object to something, we must use it's fields, like x.Something
333fred
333fred•4w ago
TestRef original = ...;
TestRef @new = ...;
ref TestRef x = ref original;
ref x = ref @new; // This is ref reassignment. x now points to @new, not original
TestRef original = ...;
TestRef @new = ...;
ref TestRef x = ref original;
ref x = ref @new; // This is ref reassignment. x now points to @new, not original
Faker
FakerOP•4w ago
yep I see I believed I now have a clearer understanding of how the memory works internally, thanks ! Really appreciate, I even refresh some of my concepts in C 😂 💯
333fred
333fred•4w ago
No problem. C is often the clearest way to explain how this works in detail, because it already made you think about it in the first place
Faker
FakerOP•4w ago
yep, now I start to see why learning C is very important to understand memory stuff sorry to disturb you once more, I have one lasttt question (it's on pointers in C though :c) consider the following:
typedef struct {
char Something[50];
} TestRef;

void DoSomething(TestRef** x) { // Equivalent to ref TestRef x
*x = (TestRef*)malloc(sizeof(TestRef)); // Allocate a new TestRef object
strcpy((*x)->Something, "Changed"); // Change the new object's value
}

int main() {
TestRef* t = (TestRef*)malloc(sizeof(TestRef)); // t is a pointer to TestRef
strcpy(t->Something, "Foo");

DoSomething(&t); // Pass a pointer to the reference t (TestRef**)

printf("t->Something: %s\n", t->Something); // Output: "Changed"

free(t); // Free allocated memory
return 0;
}
typedef struct {
char Something[50];
} TestRef;

void DoSomething(TestRef** x) { // Equivalent to ref TestRef x
*x = (TestRef*)malloc(sizeof(TestRef)); // Allocate a new TestRef object
strcpy((*x)->Something, "Changed"); // Change the new object's value
}

int main() {
TestRef* t = (TestRef*)malloc(sizeof(TestRef)); // t is a pointer to TestRef
strcpy(t->Something, "Foo");

DoSomething(&t); // Pass a pointer to the reference t (TestRef**)

printf("t->Something: %s\n", t->Something); // Output: "Changed"

free(t); // Free allocated memory
return 0;
}
Why when we pass the address of a pointer, (&t) we must use a double pointer as parameter to the function accepting that address? like TestRef**, why not use a single pointer?
Anton
Anton•4w ago
You can overwrite it if it's a double pointer Which you are doing in DoSomething In C# what you wrote would be valid, because of GC, in C it's a memory leak the object from main is never freed You could still overwrite the object if it was a single pointer But not its address
Faker
FakerOP•4w ago
yeah I see, thanks ! like we can change the content of the object in the heap but the reference/address itself would be unchanged with a single pointer
Anton
Anton•4w ago
yes

Did you find this page helpful?