Search code examples
c#value-typereference-type

What is the hidden value of the pointer/reference assigned to reference type variables?


int number = 1

The value of number is 1 because it is a value type

What is the actual value of the pointer that is assigned to reference type variables?

Is it an int or string? Or is it some bits? What would it look like if you write it out? Is it possible to assign a reference to a variable using that value?

Question harrysQuestion = new Question();

harrysQuestion is just a pointer or reference to the new Question. So what is the value of that pointer? The same value that is assigned to another Question variable if I do this:

Question harrysQuestionAgain = harrysQuestion;

Is it a number that points to some position in my computers memory? Is it an actual C# value variable behind the scenes?


Solution

  • Is it a number that points to some position in my computers memory?

    Conceptually, references and pointers are separate but related. In reality they are virtually interchangeable, with the distinction that the GC knows how to walk and fixup references (garbage collection etc), but not pointers (and there are other things about how fixed works in terms of a hack in the value, allowing a reference value found on the stack to be interpreted as "pinned" cheaply). In reality, they are so close to each-other in all implementations (for performance reasons) that you can think of them as kinda the same.

    It is very rare that you'd actually want to get the "value" of a reference (rather than dereferencing it), and unless you pin the object first you need to be very careful about doing so as the address can change (and the pointer version will not be corrected). The need for this use-case actually increases slightly with the upcoming "pipelines" work, so the corefxlab / myget version of the Unsafe utility type actually provides some methods to facilitate the exchange of references / pointers (including interior pointers/references into objects), but: unless you're doing something low level you'll probably never need that.


    Per request (comments): I mentioned "pinning" and "fixed" - the problem here is that .NET has a "compacting" garbage collector, which is allowed to move objects around at runtime, as long as it promises to fix all the references and make sure that you never notice this from managed code. What it doesn't promise is to fix pointers. So: if you're going to be looking at any object as a pointer, you need to tell the runtime (and in particular: the garbage collector) to not move that object at all, or at least until you tell it that you're done. This is what "pinning" is. There are two ways to "pin":

    • for long-term pins (typically of things like byte[] buffers that you're going to store as a field in an object and pass to unmanaged code as a pointer), you can take a GCHandle against an object, which gets logged in a global structure that the GC knows to look at
    • for short-term pins of references that are on a stack, the fixed keyword does some voodoo that lets the GC (which always looks at every stack) know that a reference - and thus the object referred to (the object at that address) - should be considered pinned, without needing to constantly add/remove to a global structure

    As a perhaps interesting side note: "interior references" and references to value types are a concept that only exists on the stack - not as fields on a type that could end up on the heap (which means any class or struct except for the new ref struct concept). They work the same as regular references, but the target of those references are the contents themselves, not the start of the object header. That means that

    var fieldReference = ref this._someField;
    

    or

    SomeOtherMethod(ref this._someField);
    

    or

    SomeOtherMethod(ref someArray[index]);
    

    work inside a method as long as that interior reference is only on the stack (i.e. no async / yield / captured-variables / etc); the GC is happy to do the overhead of resolving interior pointers to objects but only for the stacks - to reduce the overall scale of the work involved.