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,而将指向该值的指针压入堆栈。只有分配才能创建副本。
由于值类型的工作原理,带框的value types是原始副本的副本,通过将其转换回Point
对其进行“拆箱”会创建另一个副本。根据C#语言规范(第1.3节“类型和变量”):
将值类型的值转换为对象类型时,将分配一个对象实例(也称为“框”)以保存该值,并将该值复制到该框中。相反,当将对象引用转换为值类型时,将检查所引用的对象是否是正确值类型的框,如果检查成功,则将框中的值复制出来。
修改副本无论如何也不会更改原始副本,因此允许它没有太大意义。
至于C ++ ...嗯...当然,C#的规则不一定适用于它。 :) CLR实际上在指针和引用方面比您最初想的要灵活得多,而C ++(以这种灵活性而著称)可能会利用它。
您不能这样做,因为取消装箱的结果是装箱值的副本,而不是装箱值本身。将Point
转换为值类型是取消装箱的定义。因此,如果编译器允许您执行此操作,那将非常令人困惑,因为分配实际上不会执行任何操作。
I think您的代码在C ++ / CLI中工作的原因是因为该语言通常对使用(或不使用)引用(包括强类型的框(例如object
)和处理(某些)类作为值类型(例如,使用Point^
而不使用MemoryStream
)。
您可以使用^
功能完成此操作:
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);
}
],系统不执行任何操作。