Collections, References, and Value Types

Friday, January 6, 2006

Here is a basic refresher on value types, reference types, and collections. The following program executes without any assertions failing. Object.ReferenceEquals determines whether the specified Object instances are the same instance. Good stuff to know before an interview.

Update: note John's comment to my post:

To say "myValueType1 and myValueType2 DO NOT point to the same object" is a little bit misleading, because myValueType1 and myValueType2 are just different memory regions on the stack, they're not really 'objects', they're most certainly not 'object references'. The process of passing them as parameters to ReferenceEquals causes them to be boxed (thus becoming 'objects' on the managed heap (complete with object header and GC servicing) with the data from the stack copied into the object on the heap). Another reason why "myValueType1 and myValueType2 DO NOT point to the same object" is a little misleading is that myValueType1 and myValueType2, by virtue of being value types, are not 'pointers', they don't point anywhere. It is true that the value of these value types is stored at different memory addresses, but that's trivially true as the implication of them being local variables.


 

using System;
using System.Collections;
using System.Diagnostics;

namespace ConsoleApplication43
{
  
struct ValueType
  {
    
public ValueType(object theObject)
    {
      
this.theObject = theObject;
    }
    
public object theObject;
  }

  
class RefType
  {
    
public RefType(object theObject)
    {
      
this.theObject = theObject;
    }
    
public object theObject;
  }

  
class Class1
  {
    
static void Main()
    {
      
Object myObject = new object();
      
ValueType myValueType1 = new ValueType(myObject);
      
RefType myRefType1 = new RefType(myObject);

      
Hashtable table = new Hashtable();
      table.Add(
"value", myValueType1);
      table.Add(
"ref", myRefType1);

      
ValueType myValueType2 = (ValueType)table["value"];
      
RefType myRefType2 = (RefType)table["ref"];

      
// myRefTYpe1 and myRefType2 point to the same object
      Debug.Assert(Object.ReferenceEquals(myRefType1, myRefType2));

      
// the object reference inside RefType always points to myObject
      Debug.Assert(Object.ReferenceEquals(myRefType1.theObject, myObject));
      
Debug.Assert(Object.ReferenceEquals(myRefType2.theObject, myObject));

      
// myValueType1 and myValueType2 DO NOT point to the same object
      // myValueType is boxed/unboxed ...
      Debug.Assert(!Object.ReferenceEquals(myValueType1, myValueType2));

      
// .. but the copy still contains a reference to the original object
      Debug.Assert(Object.ReferenceEquals(myValueType1.theObject, myObject));
      
Debug.Assert(Object.ReferenceEquals(myValueType2.theObject, myObject));           
    }
  }
}

Comments
John Friday, January 6, 2006

The value type is boxed before it's added to the table, and it's unboxed shortly after it's retrieved from the table.

However, each instance of the value type in the call to Debug.Assert(!Object.ReferenceEquals(myValueType1, myValueType2)); are boxed before they're passed to ReferenceEquals, so this assertion doesn't really betray anything about what happened with the boxing/unboxing with the Hashtable.

'box' and 'unbox' are explicit IL instructions, so it's usually easiest just to use ildasm to see what's going on if you're confused about the implications of boxing.

To say "myValueType1 and myValueType2 DO NOT point to the same object" is a little bit misleading, because myValueType1 and myValueType2 are just different memory regions on the stack, they're not really 'objects', they're most certainly not 'object references'. The process of passing them as parameters to ReferenceEquals causes them to be boxed (thus becoming 'objects' on the managed heap (complete with object header and GC servicing) with the data from the stack copied into the object on the heap). Another reason why "myValueType1 and myValueType2 DO NOT point to the same object" is a little misleading is that myValueType1 and myValueType2, by virtue of being value types, are not 'pointers', they don't point anywhere. It is true that the value of these value types is stored at different memory addresses, but that's trivially true as the implication of them being local variables.

Not that anything you said was incorrect, just that the call to ReferenceEquals where you pass the value types will cause boxing again, and thus you might not be proving what you think you are...
scott Friday, January 6, 2006
Very true. I think my example is misleading.
John Friday, January 6, 2006

Something like this might help you make the point about the implications of boxing and object reference equality,

ValueType value = new ValueType( myObject );
Object one = value;
Object two = value;
Contract.Assert( ! Object.ReferenceEquals( one, two ) );

Although I'm not sure that the fact the references are not equal is necessarily something you could say you'd assert. Certainly it's 'highly likely', or maybe it will be true for 'all known implementations', but it's not impossible that a compiler was smart enough to realise that only one heap allocation (or none!) was necessary (owing to the fact that after the assignments the variables are only ever used once and that use is read only). I'm not sure if you'd ever expect the JIT compiler to be smart enough to do this, or if there is some reason why it couldn't, but it certainly seems like the kind of place where the compiler might have some leeway for optimisations, at which point your assertion that the "object references returned for two discrete 'box' operations on the same value are distinct" may not hold..? As the JITer can know everything about the future of how these references are used, it might be able to optimise the whole thing away..?

scott Sunday, January 8, 2006
I suppose a smart JIT might be able to optimize those away. I'm wondering if the boxing might be considered a side-effect that the JITter can't remove.
John Tuesday, January 10, 2006
> I'm wondering if the boxing might be considered a side-effect that the JITter can't remove.<

...I doubt it.


Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!