Corrado segnala in questo post, lo strano uso che il compilatore VB .NET fa del metodo RuntimeHelpers.GetObjectValue.
Anche una semplice assegnazione:
Dim foo As Object = New Object
viene vista come:
object foo = System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue(new object());
In realtà, non è così strano. Leggendo i commenti nel codice Rotor per il metodo GetObjectValue, si capisce un po' di più la ragione:
"GetObjectValue is intended to allow value classes to be manipulated as Object but have aliasing behavior of a value class. The intent is that you would use this function just before an assignment to a variable of type Object. If the value being assigned is a mutable value class, then a shallow copy is returned (because value classes have copy semantics), but otherwise the object itself is returned.
Note: VB calls this method when they're about to assign to an Object or pass it as a parameter. The goal is to make sure that boxed value types work identical to unboxed value types - ie, they get cloned when you pass them around, and are always passed by value. Of course, reference types are not cloned."
Il frammento sottolineato rappresenta proprio "l'algoritmo" del metodo. La copia shallow in questo caso è una feature di linguaggio ed è descritta da Paul Vick nell'ultima versione delle specifiche (P. Vick, "The Microsoft Visual Basic Language Specification. Version 8.0 (Beta 2)", Microsoft, 2005):
"If one reference to a boxed structure is used to modify the structure, then all references to the boxed structure will see the change. Because this result may be unexpected, when a value typed as Object is copied from one location to another boxed value types will automatically be cloned on the heap instead of merely having their references copied" (vedi il paragrafo "8.6 Value Type Conversions")
E' quindi del tutto normale il comportamento diverso fra C# e VB nel codice di Corrado, quando ottiene rispettivamente true per ReferenceEquals in C# e false in VB .NET.