为什么我不能修改拆箱转换的结果?

问题描述 投票:5回答:3
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
}

为什么不编译为

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

C ++ CLI允许的地方。

对于不知道的人,创建unbox的副本需要not,而将指向该值的指针压入堆栈。只有分配才能创建副本。

c# il boxing unboxing
3个回答
10
投票

由于值类型的工作原理,带框的value types是原始副本的副本,通过将其转换回Point对其进行“拆箱”会创建另一个副本。根据C#语言规范(第1.3节“类型和变量”):

将值类型的值转换为对象类型时,将分配一个对象实例(也称为“框”)以保存该值,并将该值复制到该框中。相反,当将对象引用转换为值类型时,将检查所引用的对象是否是正确值类型的框,如果检查成功,则将框中的值复制出来。

修改副本无论如何也不会更改原始副本,因此允许它没有太大意义。

至于C ++ ...嗯...当然,C#的规则不一定适用于它。 :) CLR实际上在指针和引用方面比您最初想的要灵活得多,而C ++(以这种灵活性而著称)可能会利用它。


1
投票

您不能这样做,因为取消装箱的结果是装箱值的副本,而不是装箱值本身。将Point转换为值类型是取消装箱的定义。因此,如果编译器允许您执行此操作,那将非常令人困惑,因为分配实际上不会执行任何操作。

I think您的代码在C ++ / CLI中工作的原因是因为该语言通常对使用(或不使用)引用(包括强类型的框(例如object)和处理(某些)类作为值类型(例如,使用Point^而不使用MemoryStream)。


0
投票

您可以使用^功能完成此操作:

System.Runtime.CompilerService.Unsafe.Unbox

如文档所述,我认为它被认为是不安全的原因是,您不得使用不可变的内置类型[例如 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); } ],系统不执行任何操作。

© www.soinside.com 2019 - 2024. All rights reserved.