C
C#3y ago
wcasa

✅ reference type values are strange..

static void bar(ref int[] baz, int newElem)
{
int newLength = baz.Length + 1;
int [] quux = new int [newLength];

for(int i = 0; i < baz.Length; i++)
quux[i] = baz[i];

quux[newLength - 1] = newElem;

baz = quux;
}

static void Main()
{
int[] foo = {1, 2, 3, 4};
foreach(int i in foo)
Console.Write(i + " ");

Console.WriteLine();
bar(ref foo, 42);

foreach(int i in foo)
Console.Write(i + " ");
}
static void bar(ref int[] baz, int newElem)
{
int newLength = baz.Length + 1;
int [] quux = new int [newLength];

for(int i = 0; i < baz.Length; i++)
quux[i] = baz[i];

quux[newLength - 1] = newElem;

baz = quux;
}

static void Main()
{
int[] foo = {1, 2, 3, 4};
foreach(int i in foo)
Console.Write(i + " ");

Console.WriteLine();
bar(ref foo, 42);

foreach(int i in foo)
Console.Write(i + " ");
}
63 Replies
wcasa
wcasaOP3y ago
why do i have to pass by reference array that I am going to change? arrays are reference type value, what is the point of passing them by reference?
cap5lut
cap5lut3y ago
to make baz = quux; work
void Example1(int[] array)
{
array = new int[] { 1, 2 };
}
void Example2(ref int[] array)
{
array = new int[] { 1, 2 };
}

static void Main()
{
var data = new int[0];
Example1(data);
foreach (var e in data) Console.WriteLine(e); // will not print anything because data itself wasnt modified

Example2(ref data);
foreach (var e in data) Console.WriteLine(e); // will not print 1 and 2 because data itself was modified

}
void Example1(int[] array)
{
array = new int[] { 1, 2 };
}
void Example2(ref int[] array)
{
array = new int[] { 1, 2 };
}

static void Main()
{
var data = new int[0];
Example1(data);
foreach (var e in data) Console.WriteLine(e); // will not print anything because data itself wasnt modified

Example2(ref data);
foreach (var e in data) Console.WriteLine(e); // will not print 1 and 2 because data itself was modified

}
wcasa
wcasaOP3y ago
you mean will print 1 and 2 because data itself was modified?
cap5lut
cap5lut3y ago
yes
wcasa
wcasaOP3y ago
(last comment) but why isnt it modified
cap5lut
cap5lut3y ago
by data i mean the local variable in Main() was modified, because it is a reference (like a pointer) to it
wcasa
wcasaOP3y ago
shouldnt method without reference point to the same memory as array is in
cap5lut
cap5lut3y ago
in Example1 array is just a copy of the reference stored in data
wcasa
wcasaOP3y ago
ooooohhhh
cap5lut
cap5lut3y ago
yeah, but with baz = quux u basically let it point to other memory
wcasa
wcasaOP3y ago
but how does it make difference
wcasa
wcasaOP3y ago
wcasa
wcasaOP3y ago
imagine third box as copy of reference that was created by method
cap5lut
cap5lut3y ago
hmm, lets take it with int instead of int[]:
void Example1(int x) { x = x + 1; }
void Example2(ref int x) { x = x + 1 }
void Main()
{
int value = 0;
Example1(value);
Example2(ref value);
}
void Example1(int x) { x = x + 1; }
void Example2(ref int x) { x = x + 1 }
void Main()
{
int value = 0;
Example1(value);
Example2(ref value);
}
wcasa
wcasaOP3y ago
but int is value type, not reference type this one will change
cap5lut
cap5lut3y ago
but a reference itself is something similar to a value type when Example1 will be called, another stack frame will be created, which introduces the parameter x, then the value of value will be copied to x (in this case 0), then the method will be executed and x will be 1, then the method will be left and the stack frame will be discarded (x and its modification is lost)
wcasa
wcasaOP3y ago
yes
cap5lut
cap5lut3y ago
when Example2 will be called its a bit different: a stack frame will be created, which introduces a pointer to an int will be introduced (thats more or less x) then the pointer of value (from Main()) will be copied to that x the the method will execute and the memory x is pointing to will be modified (thats value on the stack frame of Main()) the method will be left, the stack frame is gone and thus the copy of the pointer of value, but its modification not, because its on the stack frame of Main() in c it would look like
void Example1(int x) { x = x + 1; }
void Example2(int* x) { *x = *x + 1 }
void Main()
{
int value = 0;
Example1(value);
Example2(&value);
}
void Example1(int x) { x = x + 1; }
void Example2(int* x) { *x = *x + 1 }
void Main()
{
int value = 0;
Example1(value);
Example2(&value);
}
if thats more understandable for u
wcasa
wcasaOP3y ago
yeah, i totally understand this but its for the value type
cap5lut
cap5lut3y ago
so now comming back for reference types:
Aaron
Aaron3y ago
ref int[] isn't a reference to the array itself
cap5lut
cap5lut3y ago
int[] value; doesnt store the array, but a pointer to a memory block on the heap
Aaron
Aaron3y ago
it's a reference to the local variable in your method
cap5lut
cap5lut3y ago
if u would just modify the elements of the array, it wouldnt matter if its a int[] or ref int[] parameter, because u just modify the memory on the heap. but as u want to assign it to a new memory block baz = quux; it makes a difference if u just modify the stack frame's local variable or the pointer/reference itself
wcasa
wcasaOP3y ago
oohhhh missed the fact that quux is new memory block
cap5lut
cap5lut3y ago
;p so, is ur question answered? if yes, please $close the thread
MODiX
MODiX3y ago
Use the /close command to mark a forum thread as answered
wcasa
wcasaOP3y ago
wait a sec, thinking it over wait fucking what so its like referencing to a reference?
Aaron
Aaron3y ago
yes that's exactly what it is a ref int[] is effectively a int** + a length
333fred
333fred3y ago
Whenever you see a reference type variable in code, you can think of it as a pointer So string s is a pointer to character data
cap5lut
cap5lut3y ago
that why i took the int example first, to verify the general understanding ;p
333fred
333fred3y ago
int[] is a pointer to int data
wcasa
wcasaOP3y ago
wow
333fred
333fred3y ago
Adding a ref modifier is adding a pointer to the signature So ref int[] is effectively int** But safer
wcasa
wcasaOP3y ago
so i took reference, and dereferenced it to a new chank of memory that i have created?
333fred
333fred3y ago
In which example?
cap5lut
cap5lut3y ago
yes
wcasa
wcasaOP3y ago
static void bar(ref int[] baz, int newElem)
{
int newLength = baz.Length + 1;
int [] quux = new int [newLength];

for(int i = 0; i < baz.Length; i++)
quux[i] = baz[i];

quux[newLength - 1] = newElem;

baz = quux;
}

static void Main()
{
int[] foo = {1, 2, 3, 4};
foreach(int i in foo)
Console.Write(i + " ");

Console.WriteLine();
bar(ref foo, 42);

foreach(int i in foo)
Console.Write(i + " ");
}
static void bar(ref int[] baz, int newElem)
{
int newLength = baz.Length + 1;
int [] quux = new int [newLength];

for(int i = 0; i < baz.Length; i++)
quux[i] = baz[i];

quux[newLength - 1] = newElem;

baz = quux;
}

static void Main()
{
int[] foo = {1, 2, 3, 4};
foreach(int i in foo)
Console.Write(i + " ");

Console.WriteLine();
bar(ref foo, 42);

foreach(int i in foo)
Console.Write(i + " ");
}
333fred
333fred3y ago
Right, so bar has an effectively int** parameter
wcasa
wcasaOP3y ago
wow wowowowowoowowow and all of the arrays, classes are all same as just pointer to smth? like int*?
cap5lut
cap5lut3y ago
all reference types are
wcasa
wcasaOP3y ago
damn c# is cool
cap5lut
cap5lut3y ago
effectively they work like pointers, but because this is managed code its a bit different because the memory manager could move the memory blocks around, thus pointers would be invalid if they would contain "real" addresses
Aaron
Aaron3y ago
they do contain the real addresses
wcasa
wcasaOP3y ago
thank you everyone, thats a big help! !close
Accord
Accord3y ago
Closed!
cap5lut
cap5lut3y ago
yeah but there is more to it because of the memory management i mean, or am i wrong here? basically what i meant is, that the address can change without every touching that one reference
Aaron
Aaron3y ago
yeah, the GC can move things around
wcasa
wcasaOP3y ago
btw can i somehow pass the copy of array itself?
cap5lut
cap5lut3y ago
copy the array, and pass the copy ;p
333fred
333fred3y ago
Not in one easy action
wcasa
wcasaOP3y ago
static void bar(int[] baz)
{
int [] quux = new int[baz.Length];

for(int i = 0; i < baz.Length; i++)
quux[i] = baz[i];

//here you go quux is now copy

}
static void bar(int[] baz)
{
int [] quux = new int[baz.Length];

for(int i = 0; i < baz.Length; i++)
quux[i] = baz[i];

//here you go quux is now copy

}
is this ok?
cap5lut
cap5lut3y ago
isnt it just for example new int[] { 1, 2, 3 }.ToArray()?
333fred
333fred3y ago
Sure.
Aaron
Aaron3y ago
fastest way is just array.AsSpan().ToArray() I think
333fred
333fred3y ago
Probably
Aaron
Aaron3y ago
but all of them work fine
333fred
333fred3y ago
Well, it should be pretty close in perf to what was written above, but it's certainly easier to type
wcasa
wcasaOP3y ago
ok, thank in advance
Aaron
Aaron3y ago
just doing .ToArray() on the array gets you the LINQ method, which has a fast path for lists and the like where length is known, but also has to do a type check and multiple vcalls to get there so ¯\_(ツ)_/¯
cap5lut
cap5lut3y ago
well, for optimization just throw a benchmark on all, but i guess the span version would be the fastest
333fred
333fred3y ago
I meant in comparison to the foreach
Aaron
Aaron3y ago
yeah I know, was just comparing to caps answer

Did you find this page helpful?