为什么使用值类型和引用类型时接口的行为不同?

本文关键字:接口 类型 引用类型 为什么 | 更新日期: 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

为什么使用值类型和引用类型时接口的行为不同?

值类型语义使得值在赋值时被复制。这意味着当您在赋值后进行更改时,变量指向不同的对象。

对于引用类型,引用被复制,这意味着当您在赋值后更改时,两个变量都指向同一个对象。

请参见MSDN上的值类型和引用类型。

结构类型定义实际上定义了两种东西:一种是存储位置,另一种是继承自抽象类System.ValueType的堆对象。堆对象实际上有一个相应存储位置类型的字段,但是公开了该存储位置类型的所有成员,就好像它们是堆对象自己的一样。对于外部世界,堆类型的行为就像一个类对象;然而,在内部,对this的引用是对其相应存储位置类型的字段的引用。

尽管c#在定义术语"继承"时假装存储位置类型和堆对象类型是同一种类型,但这两种类型的行为不同。将值类型强制转换为它所代表的接口将生成一个新的堆对象,该对象保存被强制转换的值类型的公共和私有字段的副本,然后返回对该新实例的引用。结果引用将显示引用语义,因为它将是一个引用。

如果一个人认为堆对象和值类型存储位置存在于不同的宇宙中,并且认识到值必须从一个宇宙复制到另一个宇宙的情况,他会发现这样的模型将准确地预测事物的行为。