Rouper

关于 C# readonly 关键字的分析

今天看了《何时readonly 字段不是 readonly 的?结果出呼你想象!!!》,实在是让我看花了眼。关于此文中S3的方式其实也已经不必多说。关键却是S这一个结构。(我最讨厌这样的命名!!)

为了便于说明,我重新写了一个类似的代码: 

    class Program
    {
        static void Main(string[] args)
        {
            Fuck f = new Fuck(100);    
            f.Change(1000, out f);
            Console.Read();
        }
    }
    struct Fuck
    {
        public  readonly int Num;

        public Fuck(int value)
        {
            Num = value;
        }

        public void Change(int value, out Fuck data)
        {
            System.Console.WriteLine(this.Num);
            data = new Fuck(value);
            System.Console.WriteLine(this.Num);
        }
    }

运行结果自然是:

100

1000

这是为何呢?我把Fuck的定义改成了class 即

 

    Fuck
    {
        public  readonly int Num;

        public Fuck(int value)
        {
            Num = value;
        }

        public void Change(int value, out Fuck data)
        {
            System.Console.WriteLine(this.Num);
            data = new Fuck(value);
            System.Console.WriteLine(this.Num);
        }
    }

 运行结果是:

100

100

 

问题肯定在于值类型与引用类型处理机制上。尽管在C#中,所有的类型都以object的形式存在,但在内部,值类型与引用类型的处理机制仍然不同。

让我们回到 readonly 的问题上来。


readonly 关键字与 const 关键字不同。const 字段只能在该字段的声明中初始化。readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly 字段可能具有不同的值。另外,const 字段为编译时常数,而 readonly 字段可用于运行时常数

摘自于MSDN

 

根据这段话 ,readonly 字段是可以在运行时更改的。因此,readonly 在除构造函数外的函数中肯定也可以发生改变。

 

我们不仿来看看 readonly 字段 是怎么改变的

 

 

进入 Change 函数之后我们可以发现,变量  f  的地址并未发生改变.同时,this关键字指身的地址,与传入的参数 data 相同。

 

 

 

执行完 data = new Fuck(value) 之后,this关键字指向的地址,data指向的地址仍然没有改变,但是该地址的值发生了变化。

 

 

这么看来,问题就出在于 data = new Fuck(value) 这句话上。可能有些朋友对于 out 关键字很疑惑,其实跟 out 关键字并没有太大的关系。

这里又让我们回到了C#中对于值类型的处理上。

 

基于值类型的变量直接包含值。将一个值类型变量赋给另一个值类型变量时,将复制包含的值。

摘自MSDN

 

也就是说,在给 data 赋值的时候,C#将 new Fuck(value)  的值按地址复制到了data 所指向的地址中。这也是为什么把Fuck的声明改成了class之后,这个现象就并不存在了。


与引用类型变量的赋值不同,引用类型变量的赋值只复制对对象的引用,而不复制对象本身

摘自MSDN


这么看来。要改变 readonly 的值并不需要这么复杂的过程。

 

轻轻修改一下代码:

        unsafe static void Main(string[] args)
        {
            Fuck f = new Fuck(100);            
            Fuck* pf = &f;
            int* p = (int*)pf;
            *p = 123456;
            f.Change(1000, out f);
            Console.Read();
        }
运行结果是:
123456
1000

不过的确,像那种奇怪的编码方式是会引起一些奇怪的现象。不知道这算不算.net的BUG

posted on 2010-07-03 12:31  Rouper  阅读(15263)  评论(34编辑  收藏  举报

导航