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

倾然丶 夕夏残阳落幕 提交于 2019-12-17 13:41:25

问题


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.


回答1:


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.




回答2:


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 ^).




回答3:


You can accomplish this using the System.Runtime.CompilerService.Unsafe.Unbox function:

    static void Main()
    {
        Point p;
        p.x = 1;
        p.y = 1;
        Object o = p;
        Unsafe.Unbox<Point>(o).x = 6; // error
        p = (Point)o;  // 6
        Console.WriteLine(p.x);
    }

As the documentation notes, and I presume the reason it is considered unsafe, is that you must not do this with an immutable built-in type [e.g. Unbox<int>(i) = 42], and the system does nothing to enforce this.



来源:https://stackoverflow.com/questions/17280547/why-can-i-not-modify-the-result-of-an-unboxing-conversion

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!