I'm trying to understand persistent memory in C# and don't know why this code is not keeping the change made in one of the functions.
using System;
using System.Runtime.InteropServices;
public class Test2
{
public struct value
{
public int sz;
}
public static void Main(string[] args)
{
one foo1 = new one(one_full);
two foo2 = new two(two_full);
make_calls(foo1, foo2);
}
public delegate void one(IntPtr ctx);
public static void one_full(IntPtr ctx)
{
/* set sz and see if persists */
GCHandle gch = GCHandle.FromIntPtr(ctx);
value val = (value)gch.Target;
val.sz = 6;
Console.WriteLine("Changed sz to be 6");
}
public delegate void two(IntPtr ctx);
public static void two_full(IntPtr ctx)
{
GCHandle gch = GCHandle.FromIntPtr(ctx);
value val = (value)gch.Target;
Console.Write("sz is = ");
Console.WriteLine(val.sz);
}
public static void make_calls(one f_one, two f_two)
{
value test = new value();
test.sz = 0;
IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(test));
f_one(ptr);
f_two(ptr);
}
}
I know it's missing the free at the end but that is just leading to messy memory management.... I'm looking for if someone can help me out and explain why sz does not stay as the value 6 for when the second function is called...
The output when ran is:
Changed sz to be 6
sz is = 0
It's all because value is a struct.
GCHandle.Alloc takes an object parameter, so if you pass a struct, it has to be boxed.
Later when you use
value val = (value)gch.Target;
it has to be unboxed, so a copy of it is stored in val. Any modifications you make later are made on the copy, not on the boxed struct.
It has nothing to do with GCHandle, it's how value types (structs) work in C#. That's why it's recommended to make value types immutable.
Becuase value is a sturct and structs are not reference types. when you have an instance of a struct and var b = instanceOfStruct then b is a new struct not a reference to instanceOfStruct. Chaning values in b does not reflect to instanceOfStruct.
In your code:
value val = (value)gch.Target;
will create a new instance of value struct which has same values as the structs that gch.Target point to it. Changing val does not change the struct behind gch.Target. The problem is because of a confusion between value types and reference types in C#. If you change value type to a class instead of struct then you will get the desired result. You can also use dynamic to modify the struct which is target of the handle:
dynamic val = gch.Target;
val.sz = 6;
Related
Warning: This is merely an exercise for those whose are passionate about breaking stuff to understand their mechanics.
I was exploring the limits of what I could accomplish in C# and I wrote a ForceCast() function to perform a brute-force cast without any type checks. Never consider using this function in production code.
I wrote a class called Original and a struct called LikeOriginal, both with two integer variables. In Main() I created a new variable called orig and set it to a new instance of Original with a=7 and b=20. When orig is cast into LikeOriginal and stored in casted, the values of cG and dG become undefined, which is to be expected as LikeOriginal is a struct and class instances contain more metadata than struct instances thus causing memory layout mismatch.
Example Output:
Casted Original to LikeOriginal
1300246376, 542
1300246376, 542
added 3
Casted LikeOriginal back to Original
1300246379, 545
Notice, however, that when I call casted.Add(3) and cast back to Original and print the values of a and b, surprisingly they are successfully incremented by 3, and this has been repeatable.
What is confusing me is the fact that casting the class to the struct will cause cG and dG to map to class metadata, but when they are modified and cast back to a class, they map correctly with a and b.
Why is this the case?
The code used:
using System;
using System.Runtime.InteropServices;
namespace BreakingStuff {
public class Original {
public int a, b;
public Original(int a, int b)
{
this.a = a;
this.b = b;
}
public void Add(int val)
{
}
}
public struct LikeOriginal {
public int cG, dG;
public override string ToString() {
return cG + ", " + dG;
}
public void Add(int val) {
cG += val;
dG += val;
}
}
public static class Program {
public unsafe static void Main() {
Original orig = new Original(7, 20);
LikeOriginal casted = ForceCast<Original, LikeOriginal>(orig);
Console.WriteLine("Casted Original to LikeOriginal");
Console.WriteLine(casted.cG + ", " + casted.dG);
Console.WriteLine(casted.ToString());
casted.Add(3);
Console.WriteLine("added 3");
orig = ForceCast<LikeOriginal, Original>(casted);
Console.WriteLine("Casted LikeOriginal back to Original");
Console.WriteLine(orig.a + ", " + orig.b);
Console.ReadLine();
}
//performs a pointer cast but with the same memory layout.
private static unsafe TOut ForceCast<TIn, TOut>(this TIn input) {
GCHandle handle = GCHandle.Alloc(input);
TOut result = Read<TOut>(GCHandle.ToIntPtr(handle));
handle.Free();
return result;
}
private static unsafe T Read<T>(this IntPtr address) {
T obj = default(T);
if (address == IntPtr.Zero)
return obj;
TypedReference tr = __makeref(obj);
*(IntPtr*) (&tr) = address;
return __refvalue(tr, T);
}
}
}
€dit: Long story short: first create a ForceCast function that correctly handles both identity translations ForceCast<LikeOriginal, LikeOriginal> and ForceCast<Original, Original>, then you might have a chance to get actual conversions working
A working sample
By providing different codes for class->class (CC), class->struct (CS), struct->class (SC) and struct->struct (SS), using Nullable<T> as intermediate for structs, I got a working example:
// class -> class
private static unsafe TOut ForceCastCC<TIn, TOut>(TIn input)
where TIn : class
where TOut : class
{
var handle = __makeref(input);
return Read<TOut>(*(IntPtr*)(&handle));
}
// struct -> struct, require nullable types for in-out
private static unsafe TOut? ForceCastSS<TIn, TOut>(TIn? input)
where TIn : struct
where TOut : struct
{
var handle = __makeref(input);
return Read<TOut?>(*(IntPtr*)(&handle));
}
// class -> struct
private static unsafe TOut? ForceCastCS<TIn, TOut>(TIn input)
where TIn : class
where TOut : struct
{
var handle = __makeref(input);
// one extra de-reference of the input pointer
return Read<TOut?>(*(IntPtr*)*(IntPtr*)(&handle));
}
// struct -> class
private static unsafe TOut ForceCastSC<TIn, TOut>(TIn? input)
where TIn : struct
where TOut : class
{
// get a real pointer to the struct, so it can be turned into a reference type
var handle = GCHandle.Alloc(input);
var result = Read<TOut>(GCHandle.ToIntPtr(handle));
handle.Free();
return result;
}
Now use the appropriate function in your sample and handle the nullable types like the compiler demands:
Original orig = new Original(7, 20);
LikeOriginal casted = ForceCastCS<Original, LikeOriginal>(orig) ?? default(LikeOriginal);
Console.WriteLine("Casted Original to LikeOriginal");
Console.WriteLine(casted.cG + ", " + casted.dG);
Console.WriteLine(casted.ToString());
casted.Add(3);
Console.WriteLine("added 3");
orig = ForceCastSC<LikeOriginal, Original>(casted);
Console.WriteLine("Casted LikeOriginal back to Original");
Console.WriteLine(orig.a + ", " + orig.b);
Console.ReadLine();
For me, this returns the correct numbers at each point.
Details
Some details:
Basically, your problem is you treat a value type like a reference type...
Lets first look at the working case: LikeOriginal -> Original:
var h1 = GCHandle.Alloc(likeOriginal);
var ptr1 = GCHandle.ToIntPtr(h1);
This creates a pointer that points to the memory area of LikeOriginal (€dit: actually, not really exactly that memory area, see below)
var obj1 = default(Original);
TypedReference t1 = __makeref(obj1);
*(IntPtr*)(&t1) = ptr1;
This creates a reference (pointer) to Original with the value of a pointer, pointing to LikeOriginal
var original = __refvalue( t1,Original);
This turns the typed reference into a managed reference, pointing to the memory of LikeOriginal. All values of the starting likeOriginal object are retained.
Now lets analyze some intermediate case that should work, if your code would work bi-directional: LikeOriginal -> LikeOriginal:
var h2 = GCHandle.Alloc(likeOriginal);
var ptr2 = GCHandle.ToIntPtr(h2);
Again, we have a pointer that points to the memory area of LikeOriginal
var obj2 = default(LikeOriginal);
TypedReference t2 = __makeref(obj2);
Now here is the first hint of what is going wrong: __makeref(obj2) will create a reference to the LikeOriginal object, not to some separate area where the pointer is stored.
*(IntPtr*)(&t2) = ptr2;
ptr2 however, is a pointer to some reference value
var likeOriginal2 = __refvalue( t2,LikeOriginal);
Here we are, getting garbage because t2 would be supposed to be a direct reference to the object memory, instead of a reference to some pointer memory.
Following is some testcode I executed to get a better understanding of your approach and what goes wrong (some of it pretty structured, then some parts where I tried some additional things):
Original o1 = new Original(111, 222);
LikeOriginal o2 = new LikeOriginal { cG = 333, dG = 444 };
// get handles to the objects themselfes and to their individual properties
GCHandle h1 = GCHandle.Alloc(o1);
GCHandle h2 = GCHandle.Alloc(o1.a);
GCHandle h3 = GCHandle.Alloc(o1.b);
GCHandle h4 = GCHandle.Alloc(o2);
GCHandle h5 = GCHandle.Alloc(o2.cG);
GCHandle h6 = GCHandle.Alloc(o2.dG);
// get pointers from the handles, each pointer has an individual value
IntPtr i1 = GCHandle.ToIntPtr(h1);
IntPtr i2 = GCHandle.ToIntPtr(h2);
IntPtr i3 = GCHandle.ToIntPtr(h3);
IntPtr i4 = GCHandle.ToIntPtr(h4);
IntPtr i5 = GCHandle.ToIntPtr(h5);
IntPtr i6 = GCHandle.ToIntPtr(h6);
// get typed references for the objects and properties
TypedReference t1 = __makeref(o1);
TypedReference t2 = __makeref(o1.a);
TypedReference t3 = __makeref(o1.b);
TypedReference t4 = __makeref(o2);
TypedReference t5 = __makeref(o2.cG);
TypedReference t6 = __makeref(o2.dG);
// get the associated pointers
IntPtr j1 = *(IntPtr*)(&t1);
IntPtr j2 = *(IntPtr*)(&t2); // j1 != j2, because a class handle points to the pointer/reference memory
IntPtr j3 = *(IntPtr*)(&t3);
IntPtr j4 = *(IntPtr*)(&t4);
IntPtr j5 = *(IntPtr*)(&t5); // j4 == j5, because a struct handle points directly to the instance memory
IntPtr j6 = *(IntPtr*)(&t6);
// direct translate-back is working for all objects and properties
var r1 = __refvalue( t1,Original);
var r2 = __refvalue( t2,int);
var r3 = __refvalue( t3,int);
var r4 = __refvalue( t4,LikeOriginal);
var r5 = __refvalue( t5,int);
var r6 = __refvalue( t6,int);
// assigning the pointers that where inferred from the GCHandles
*(IntPtr*)(&t1) = i1;
*(IntPtr*)(&t2) = i2;
*(IntPtr*)(&t3) = i3;
*(IntPtr*)(&t4) = i4;
*(IntPtr*)(&t5) = i5;
*(IntPtr*)(&t6) = i6;
// translate back the changed references
var s1 = __refvalue( t1,Original); // Ok
// rest is garbage values!
var s2 = __refvalue( t2,int);
var s3 = __refvalue( t3,int);
var s4 = __refvalue( t4,LikeOriginal);
var s5 = __refvalue( t5,int);
var s6 = __refvalue( t6,int);
// a variation, primitively dereferencing the pointer to get to the actual memory
*(IntPtr*)(&t4) = *(IntPtr*)i4;
var s4_1 = __refvalue( t4,LikeOriginal); // partial result, getting { garbage, 333 } instead of { 333, 444 }
// prepare TypedReference for translation between Original and LikeOriginal
var obj1 = default(Original);
var obj2 = default(LikeOriginal);
TypedReference t7 = __makeref(obj1);
TypedReference t8 = __makeref(obj2);
// translate between Original and LikeOriginal
*(IntPtr*)(&t7) = i4; // From struct to class, the pointer aquired through GCHandle is apropriate
var s7 = __refvalue( t7,Original); // Ok
*(IntPtr*)(&t8) = *(IntPtr*)j1;
var s8 = __refvalue( t8,LikeOriginal); // Not Ok - Original has some value comming before its first member - getting { garbage, 111 } instead of { 111, 222 }
*(IntPtr*)(&t8) = j2;
var s9 = __refvalue( t8,LikeOriginal); // Ok by starting at the address of the first member
Conclusion: Going via GCHandle -> IntPtr is creating a pointer that is pointing to one memory location in front of the first member, no matter whether the starting point is a struct or a class. This results in a situation, where struct -> class or class -> class is working but class -> struct or struct -> struct is not working.
The only way I found for targeting structs is to get a pointer to their first member (which in case of an input struct equals the __makeref to the struct without going via GCHandle).
Here is how I see this situation. You have acted upon the reference to Original as if it were a reference to LikeOriginal. Critical point here is that you are invoking LikeOriginal.Add() method, the address of which is resolved statically during compile time.
This method, in turn, operates on a this reference which it implicitly receives. Therefore, it modifies values which are offset by 0 and by 4 bytes relative to this reference it has in its hands.
Since this experiment worked out, it indicates that the layouts of Original object and LikeOriginal struct are the same. I know that structs have flat layout, which makes them useful when allocating arrays of structs - there will be nothing inserted into the sequence of bytes representing flat content of structs. That is precisely what doesn't stand for classes - they need one reference which will be used to resolve virtual functions and type at run time.
Which reminds me to say that the lacking of this added reference is the core reason why structs do not support derivation - you wouldn't know whether you have a base or derived struct in a later call.
Anyway, back to the surprising fact that this code worked fine. I have been working with C++ compilers and I remember that they used to put the v-table pointer before actual data content of the object. In other words, this pointer used to point 4 bytes after actual address of the memory block allocated for that object. Maybe C# is doing the same, in which case this reference in a method invoked on Original points to a, just like the this reference in a method invoked on LikeOriginal points to cG.
I have written the following unsafe class that wraps a pointer to an int:
unsafe class PtrWrapper
{
public PtrWrapper(ref int val)
{
fixed (int* ptr = &val)
{
m_ptr = ptr;
}
}
public int Value
{
get { return *m_ptr; }
set { *m_ptr = value; }
}
int * m_ptr;
}
I've tested it and it seems to work fine, however I've just read the reference on fixed once again and it looks like all operations on a pointer should be done inside the statement:
Without fixed, pointers to movable managed variables would be of
little use since garbage collection could relocate the variables
unpredictably.
So, is it possible that by the time I call the Value property the pointee is relocated in memory and my pointer points to something else? I know that I have a problem if the pointee goes out of scope, but I will account for it by how I use my class. So I'm asking about relocating of a value type variable that has not gone out of scope yet.
Yes, it's quite possible and even probable.
On the heap:
class M
{
public int i;
public PtrWrapper w;
public M()
{
i = 42;
w = new PtrWrapper(ref i);
}
}
var m = new M();
var value = m.w.Value; // probably 42
// move m to gen 2
GC.Collect();
GC.Collect();
value = m.w.Value; // probably a random value
That's why it's called unsafe :) fixed only prevents something from moving while it's within the scope.
On the stack however it should be fine. Only when you go out of scope the variables on the stack would be popped, such as in this case:
PtrWrapper M()
{
var i = 42;
var w = new PtrWrapper(ref i);
return w;
}
var w = M();
var value = w.Value; // some random value
Note that captured variables (in lambdas) are really on the heap so you should be careful with those in that scenario.
This question already has answers here:
What is the difference between 2 methods with ref object par and without?
(5 answers)
Closed 8 years ago.
I have been reading a bit the tutorials on MSDN to get my head around pass-by-reference in C#, ref and out and I came across the following code sample:
using System;
class TheClass
{
public int x;
}
struct TheStruct
{
public int x;
}
class TestClass
{
public static void structtaker(TheStruct s)
{
s.x = 5;
}
public static void classtaker(TheClass c)
{
c.x = 5;
}
public static void Main()
{
TheStruct a = new TheStruct();
TheClass b = new TheClass();
a.x = 1;
b.x = 1;
structtaker(a);
classtaker(b);
Console.WriteLine("a.x = {0}", a.x); //prints 1
Console.WriteLine("b.x = {0}", b.x); //prints 5
}
}
The note to this from the tutorial:
This example shows that when a struct is passed to a method, a copy of
the struct is passed, but when a class instance is passed, a reference
is passed.
I totally understood it, but my question is, if a reference is passed in C# to the parameter, why would they need ref as in the following sample:
void tearDown(ref myClass a)
{
a = null;
}
MyClass b = new MyClass();
this.tearDown(ref b);
assert(b == null);
//b is null
??? I thought C# was the same in C - pass-by-value.
In C#, basically all classes as pointers. However, passing by ref/out or not is like passing the pointer to a pointer or the pointer itself.
When you pass a class (as per the first sample) any changes to the classes members are carried over. However, changing the reference to the object itself would not yield the results. Say you replace
public static void classtaker(TheClass c)
{
c.x = 5;
}
With
public static void classtaker(TheClass c)
{
c = new TheClass();
c.x = 5;
}
Since c is not an out or ref paramter, you're reassigning the local pointer to c, not the value of c itself. Since you only modified the .x of the local c, the result would be that b.x == 1 after calling this modified ClassTaker.
Now, as per your second example, since a is a ref parameter, changes to the value a itself will be seen in the calling scope, as in the example, but removing the ref from the call would cause the null assertion to fail.
Basically, ref passing passes what can be thought of as a pointer to your pointer, while calling without ref/out passes a copied pointer to your object data.
EDIT:
The reason one can assign c.X in method scope is because the object c points to the object X, and you'll always get the same pointer to X regardless of the ref/out parameter or not. Instead, ref/out modifies your ability to change the value c as seen by the calling scope.
i have the following array :
int[] myArray = {21,21,364,658,87};
and a reference to the second element like so:
int rr = myArray[1];
i want something like :
rr = 500
Console.writeLine(myArray[1]);// ---> should print 500 !
i hope you guys got my idea , i can do this easily in python like the example above.
so
how to do this in C#
my solution would probably be create property with arr[1] as its backing property
something like:
public int rr
{
set{ arr[1] = value;}
get{ return arr[1];}
}
and than rr=500; will be the same as arr[1]=500;
You could use something like this:
public static class ArrayExtensions
{
public static Action<int> CreateSetter(this int[] array, int index)
{
return (value) => array[index] = value;
}
}
[TestFixture]
public class ArrayTest
{
[Test]
public void Test()
{
int[] myArray = {21,21,364,658,87};
Action<int> rr = myArray.CreateSetter(1);
rr(500);
Assert.AreEqual(500, myArray[1]);
}
}
When you do this:
int[] myArray = {21,21,364,658,87};
int rr = myArray[1];
rr = 500;
You will only overwrite the value in rr, there is no way for you to get the actual memory address of an arrays inner elements, and thereby updating it.
My answer must therefore be:
myArray[1] = 500;
I'm trying to understand what you're trying to do, if you want to encapsulate your change in a function you could pass the reference on this way, but it's all about what you want to do with it:
public void Proc()
{
var ints = new [] { 1, 2, 3, 4 };
FunctionChangingByReference(ref ints[1]);
}
public void FunctionChangingByReference(ref int x)
{
x = 500;
}
In C# there are no pointers, only references.
(I'm lying a bit, you could use pointers if you create a unsafe context, but we don't do that in C#, and neither should you. When we code C++ we do, but that's C++, and we do it at a cost, we make the code a bit more fragile and error prone. When I code C# I try to optimize the code on a higher level than memory address shuffling. If you really need to optimize on that level you should write the code in C++ and import that code as a dll, then you have a good separation of concern, and don't forget to test drive the development!)
Simply myArray[1] = 500! You could use a property as Nahum Litvin has suggested if you specifically want a reference to a specific integer within the array.
#des answer has awaken my interest. So I tried his solution and it works as expected:
int[] numbers = new[] { 1, 2, 3 };
fixed (int* number = &numbers[0])
{
*number = 10;
}
Console.WriteLine(String.Join(", ", numbers)); // Outputs "10, 2, 3"
You have to compile it with the /unsafe option.
I hope you see that this may bring some problems.
Therefore I don't recommend this solution.
What you want is a basically pointer to a variable.
It's hard to explain the difference between "value type" (like int or struct), a reference and a pointer. I can only recommend learning C.
Here's solution that works, although it may need a lot of changes to your code.
//a class that will hold an int inside
public class myIntWrapper
{
//this is the value wrapper holds
public int theValue;
//constructor taking the value
public myIntWrapper(int argument)
{
theValue = argument;
}
//operator to convert an int into brand-new myIntWrapper class
public static implicit operator myIntWrapper(int argument)
{
return new myIntWrapper(argument);
}
//operator to convert a myIntWrapper class into an int
public static implicit operator int(myIntWrapper wrapper)
{
return wrapper.theValue;
}
}
now you can write:
//create an array -
//setting values to every item in array works
//thanks to operator myIntWrapper(int argument)
myIntWrapper[] myArray = new myIntWrapper[5]{1,2,3,4,5};
//now take a "reference"
myIntWrapper rr = myArray[1];
//change the value
rr.theValue = 500;
//from now on myArray[1].theValue is 500;
//thanks to operator int(myIntWrapper wrapper)
//you can write:
int ss = rr;//it works!
please remember to never do:
rr = 600;
because this will actually create brand new myIntWrapper, that's not "connected" anywhere.
So remember:
rr.theValue = 500;//this changes the value somewhere
rr = myArray[3];//this changes where rr is "pointing" to
Yes, it's quite complicated but I doubt it can be done any simpler without unsafe code. I'm sorry for not explaining it more. I'll answer to all questions in comments.
Could some one please explain, What happens when a reference type is defined inside the value type.
I write the following code:
namespace ClassInsideStruct
{
class ClassInsideStruct
{
static void Main(string[] args)
{
ValueType ObjVal = new ValueType(10);
ObjVal.Display();
ValueType.ReferenceType ObjValRef = new ValueType.ReferenceType(10);
ObjValRef.Display();
Test(ObjVal, ObjValRef);
ObjVal.Display();
ObjValRef.Display();
Console.ReadKey();
}
private static void Test(ValueType v, ValueType.ReferenceType r)
{
v.SValue = 50;
r.RValue = 50;
}
}
struct ValueType
{
int StructNum;
ReferenceType ObjRef;
public ValueType(int i)
{
StructNum = i;
ObjRef = new ReferenceType(i);
}
public int SValue
{
get { return StructNum; }
set
{
StructNum = value;
ObjRef.RValue = value;
}
}
public void Display()
{
Console.WriteLine("ValueType: " + StructNum);
Console.Write("ReferenceType Inside ValueType Instance: ");
ObjRef.Display();
}
public class ReferenceType
{
int ClassNum;
public ReferenceType(int i)
{
ClassNum = i;
}
public void Display()
{
Console.WriteLine("Reference Type: " + ClassNum);
}
public int RValue
{
get { return ClassNum; }
set { ClassNum = value; }
}
}
}
}
Which outputs:
ValueType: 10
ReferenceType Inside ValueType Instance: Reference Type: 10
Reference Type: 10
ValueType: 10
ReferenceType Inside ValueType Instance: Reference Type: 50
Reference Type: 50
I'm curious to know, after calling the method Test(ObjVal, ObjValRef), how the values of ReferenceType is changed to 50 which resides inside the ValueType whose value is not changed?
I don't know for sure, but the compiler probably separates the code into a separate class and then just enforces the rules required. When you use a value type, the value is copied every time it is passed into a method. The reference to a reference type will get copied, but it refers to the same object. This same reference object will get changed while the value type that was copied will get changed. The original that you passed in will not reflect the changes on the copy.
Because Reference Types are Reference Types and Value Types are Value Types. No matter where they Reside.
And also Value type is not changing neither it is changing the Reference its holding. Its the Reference Type that gets changed(Read my words carefully).
i.e the underlying data at that address gets changed. The reference held by value type is still the same.
Value inside value type is reference, that it is not changed. But value that is pointed by the reference could be easily changed.
Reference types are passed into methods as a pointer, so modifying contents will modify the same location in memory. Value types are passed into methods by sending the value on the call stack.
when programming, it's important to understand that calling a method that takes arguments implies/includes/is the same as assigning values to those arguments. plus:
static void Main(string[] args)
{
ValueType ObjVal = new ValueType(10);
ObjVal.Display();
ValueType.ReferenceType ObjValRef = new ValueType.ReferenceType(10);
ObjValRef.Display();
//call to Test(ObjVal, ObjValRef); replaced by the following 4 lines
ValueType v = ObjVal;
ReferenceType r = ObjValRef;
v.SValue = 50;
r.RValue = 50;
ObjVal.Display();
ObjValRef.Display();
Console.ReadKey();
}
should give the same result as your example above. when you declare ValueType v = ObjVal; you are making a copy of the actual struct object, which means that v is a separate object all together. so changing the values of it's members won't affect ObjVal.
however, ReferenceType r = ObjValRef; makes a copy of a reference. So now there are two references, ObjValRef and r, pointing to the same object. Namely the object created when calling new ValueType.ReferenceType(10);
so when changing members of the object pointed to by any of these two references, this object changes, regardless of which pointer is used to perform the change.
oh, by the by.. a reference is just an address of an object. often this is a 32 bit number, but this changes from language to language, and from processor to processor.
and changing the reference copy in itself, e.g. r = null; won't affect the "original" reference ObjValRef, since r is a copy of ObjValRef, and not ObjValRef itself. it just appears as though they are the same, since they both point to the same object.
you can think of the actual object as a place (a park or some famous building, maybe "white mountain park") and the references as street signs pointing to this place. there can be many street signs pointing to the same place, but this doesn't mean that there are many "white mountain park". and this is the difference between value types and reference types.