Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I not modify the result of an unboxing conversion?

struct Point
{
    public int x;
    public int y;
}
void Main()
{
    Point p;
    p.x = 1;
    p.y = 1;
    Object o = p;
    ((Point) o).x = 4; // error
    ((Point) o).x = 5; // error
    ((Point) o).x = 6; // error
    p = (Point) o  // expect 6
}

Why doesn't it compile to

ldloc.1 // o
unbox Point
ldc.i4.4
stfld Point.x

Where C++ CLI allows it.

For those who don't know, unbox is not required to create a copy of value types, instead it pushes a pointer to the value on to the stack. Only assignment would create a copy.

like image 769
colinfang Avatar asked Jun 24 '13 16:06

colinfang


2 Answers

Because of how value types work, the boxed Point is a copy of the original, and "unboxing" it by casting back to Point creates yet another copy. From the C# language spec (§1.3, "Types and Variables"):

When a value of a value type is converted to type object, an object instance, also called a “box,” is allocated to hold the value, and the value is copied into that box. Conversely, when an object reference is cast to a value type, a check is made that the referenced object is a box of the correct value type, and, if the check succeeds, the value in the box is copied out.

Modifying the copy wouldn't change the original anyway, so it wouldn't make much sense to allow it.

As for C++...well...of course, the rules of C# don't necessarily apply to it. :) The CLR actually has quite a bit more flexibility with pointers and references than you'd first think, and C++ -- being known for such flexibility -- probably takes advantage of it.

like image 200
cHao Avatar answered Sep 22 '22 12:09

cHao


You can't do this, because the result of unboxing is a copy of the boxed value, not the boxed value itself. And casting object to a value type is the definition of unboxing. So, if the compiler allowed you to do this, it would be very confusing, because the assignments wouldn't actually do anything.

I think the reason your code works in C++/CLI is because that language in general has more support for working (or not) with references, including strongly-typed boxes (e.g. Point^) and treating (some) classes as value types (e.g. using MemoryStream without ^).

like image 22
svick Avatar answered Sep 21 '22 12:09

svick