關於for迴圈中變數定義的位置

來源:互聯網
上載者:User

問題

最近跟同事討論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儲存執行個體對象的地址。

參考資料:

Anytao的大作:http://www.cnblogs.com/anytao/archive/2007/12/07/must_net_19.html

好了,有了這個背景知識,就不難理解同事為什麼說第二種寫法會少分配記憶體了。對於第一種寫法會建立多次變數obj,第二種只有一次。那麼事實上是不是如此呢?

答案

要查明這個問題我們只需要藉助IL,看一下這2段代碼的IL就一清二楚了。

看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是否全部一次初始化好。結果如下:

不出所料,IL在一開始就把所有的變數都初始化好了。這樣我就想不通了,如果代碼的中間就有條件陳述式控制return呢,後面的變數不一定都會用到,完全可以不去初始化,這樣難得不會浪費記憶體空間嗎?還是說我對.locas init的理解有誤,望解惑!

解惑

@鈞梓昊逑 方法內部的臨時變數是在進入方法時就在棧上分配的,通過棧頂指標的移動實現變數分配與回收,效率是極高的,對於你說的記憶體浪費,的確會有,這也是為什麼推薦寫小方法的原因。

 我想著應該是最好的答案了~

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.