为什么使用值类型和引用类型时接口的行为不同?
本文关键字:接口 类型 引用类型 为什么 | 更新日期: 2023-09-27 18:02:53
我用c#做了下面的例子
interface IChangeable
{
void Change(params Int32[] array);
}
struct SomeValueType : IChangeable
{
private Int32 m_X;
public SomeValueType(int X)
{
m_X = X;
}
public void Change(params Int32[] array)
{
m_X = array[0];
}
public override string ToString()
{
return String.Format("Crt value of m_X: {0}", m_X);
}
}
static void Main(String[] args)
{
SomeValueType someValueType = new SomeValueType(5);
Console.WriteLine(someValueType); // No boxing occured. It showed: 5
Object someRefType = someValueType; // Boxed
Console.WriteLine(someRefType); // Also Shows 5 (from heap)
someValueType.Change(2); // Change Value of x not o's
Console.WriteLine(someValueType + " " + someRefType); // 2 5
((SomeValueType)someRefType).Change(3); // Copies to a temp stack so no change ocuured in o
Console.WriteLine(someRefType); // 5
IChangeable itfStackChange = (IChangeable)someValueType;
itfStackChange.Change(7);
Console.WriteLine(someValueType); // Shows 2 and not 7 ... why?
IChangeable itfChange = (IChangeable)someRefType;
itfChange.Change(1); // Unboxes the value of o, making the value of x 1 boxes o again?
Console.WriteLine(someRefType); // Shows 1
}
现在我想知道当我这样做时会发生什么:
IChangeable itfStackChange = (IChangeable)someValueType; //value was 2 and its still 2
itfStackChange.Change(7);
Console.WriteLine(someValueType);
但是如果我把struct的定义改成class,如下所示
class SomeValueType : IChangeable
它写的是7而不是2
值类型语义使得值在赋值时被复制。这意味着当您在赋值后进行更改时,变量指向不同的对象。
对于引用类型,引用被复制,这意味着当您在赋值后更改时,两个变量都指向同一个对象。
结构类型定义实际上定义了两种东西:一种是存储位置,另一种是继承自抽象类System.ValueType
的堆对象。堆对象实际上有一个相应存储位置类型的字段,但是公开了该存储位置类型的所有成员,就好像它们是堆对象自己的一样。对于外部世界,堆类型的行为就像一个类对象;然而,在内部,对this
的引用是对其相应存储位置类型的字段的引用。
尽管c#在定义术语"继承"时假装存储位置类型和堆对象类型是同一种类型,但这两种类型的行为不同。将值类型强制转换为它所代表的接口将生成一个新的堆对象,该对象保存被强制转换的值类型的公共和私有字段的副本,然后返回对该新实例的引用。结果引用将显示引用语义,因为它将是一个引用。
如果一个人认为堆对象和值类型存储位置存在于不同的宇宙中,并且认识到值必须从一个宇宙复制到另一个宇宙的情况,他会发现这样的模型将准确地预测事物的行为。