I understand that this is an implementation detail. I'm actually curious what that implementation detail is in Microsoft's CLR.
Now, bear with me as I did not study CS in college, so I might have missed out on some fundamental principles.
But my understanding of the "stack" and the "heap" as implemented in the CLR as it stands today is, I think, solid. I'm not going to make some inaccurate umbrella statement such as "value types are stored on the stack," for example. But, in most common scenarios -- plain vanilla local variables, of value type, either passed as parameters or declared within the method and not contained inside a closure -- value type variables are stored on the stack (again, in Microsoft's CLR).
I guess what I'm unsure of is where ref
value type parameters come in.
Originally what I was thinking was that, if the call stack looks like this (left = bottom):
A() -> B() -> C()
...then a local variable declared within the scope of A and passed as a ref
parameter to B could still be stored on the stack--couldn't it? B would simply need the memory location where that local variable was stored within A's frame (forgive me if that isn't the right terminology; I think it's clear what I mean, anyway).
I realized this couldn't be strictly true, though, when it occurred to me that I could do this:
delegate void RefAction<T>(ref T arg);
void A()
{
int x = 100;
RefAction<int> b = B;
// This is a non-blocking call; A will return immediately
// after this.
b.BeginInvoke(ref x, C, null);
}
void B(ref int arg)
{
// Putting a sleep here to ensure that A has exited by the time
// the next line gets executed.
Thread.Sleep(1000);
// Where is arg stored right now? The "x" variable
// from the "A" method should be out of scope... but its value
// must somehow be known here for this code to make any sense.
arg += 1;
}
void C(IAsyncResult result)
{
var asyncResult = (AsyncResult)result;
var action = (RefAction<int>)asyncResult.AsyncDelegate;
int output = 0;
// This variable originally came from A... but then
// A returned, it got updated by B, and now it's still here.
action.EndInvoke(ref output, result);
// ...and this prints "101" as expected (?).
Console.WriteLine(output);
}
So in the example above, where is x
(in A's scope) stored? And how does this work? Is it boxed? If not, is it subject to garbage collection now, despite being a value type? Or can the memory immediately be reclaimed?
I apologize for the long-winded question. But even if the answer is quite simple, maybe this will be informative to others who find themselves wondering the same thing in the future.