当前位置:编程学习 > 网站相关 >>

关于for循环中变量定义的位置

问题

最近跟同事讨论for循环中变量定义在哪里的问题。先看一段代码:

        private void ForInner()
        {

            for (int i = 0; i < 5; i++)
            {
                var obj = new MyClass();
                Console.WriteLine(obj.name);
            }
        }这是我们正常习惯写的代码。同事的意思是说如果照上面那样写因为每循环一次,obj的变量就要在堆栈上分配一段空间,造成浪费。应该把obj的定义拿到for代码块的外面这样可以少分配一些内存提高效率,代码如下:
     private void ForOuter()
        {

            MyClass obj;
            for (int i = 0; i < 5; i++)
            {
                obj = new MyClass();
                Console.WriteLine(obj.name);
            }

        }从正常的角度上来看这样写变量obj确实比上面要少分配内存,因为obj只是定义了一次,只在堆栈上分配了一次内存,用来保存指向MyClass的实例的地址。理解这个问题首先得对.net的内存分配有个了解。简单科普一下:

一个引用类型的对象被创建分为以下几步

1. MyClass obj ; 在线程堆栈上创建一个obj的变量,用来保存实例对象的地址。

2. new MyClass();在托管堆上创建 MyClass的实例对象。

3. “=”操作符号 obj存储实例对象的地址。

 好了,有了这个背景知识,就不难理解同事为什么说第二种写易做图少分配内存了。对于第一种写易做图创建多次变量obj,第二种只有一次。那么事实上是不是如此呢?

答案

要查明这个问题我们只需要借助IL,看一下这2段代码的IL就一清二楚了。

 

image

看2段IL的代码,我们很容易就发现,其实不管是哪种写法,生成的IL几乎是一样的,不同的只是locals init初始化变量的顺序先后的差异。对于第一种写法IL并没有在循环体内去每次都声明obj变量。所以这两种写法在本质上是一样的。但是本人还是推荐第一种写法,在循环体里直接定义变量。因为循环体里实例化的对象,一般都是循环完成就不在使用了可以被回收,或者被其他业务对象引用,如放入某个List里面去。但是第二种写法的obj变量必定还保持着最后一次循环所创建的对象。这个对象的释放会被限制,且后面的新人接手你的代码时容易误操作了这个变量,造成不必要的bug。

疑惑

经过这次对IL的查看,还发现一个问题,难道在IL中方法的局部变量都是在方法体最上部全部初始化好了吗,于是我又做了测试:

 private void ForMany()
        {

            int z = 1;

            var a = new MyClass();
            var b = new MyClass();
            var c = new MyClass();
            var d = new MyClass();
            var e = new MyClass();
            var f = new MyClass();
            var g = new MyClass();
            if( z==1)
                return;
            var h = new MyClass();
            var i = new MyClass();
            var j = new MyClass();
            var k = new MyClass();
            var l = new MyClass();
            var n = new MyClass();
                return;
        }
我在方法里定义了很多的变量。看看IL是否全部一次初始化好。结果如下:

 

image

不出所料,IL在一开始就把所有的变量都初始化好了。这样我就想不通了,如果代码的中间就有条件语句控制return呢,后面的变量不一定都会用到,完全可以不去初始化,这样难得不会浪费内存空间吗?还是说我对.locas init的理解有误,望解惑!

 

 

补充:综合编程 , 其他综合 ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,