文章出處

一直在博客園懟人,非常慚愧。所以鄭重決定:

好好寫一篇干貨,然后再接著懟人。

這是一起幫陳百萬同學的求助,講了一會之后,我覺得很有些普世價值,干脆就發到園子來。面向小白,高手輕拍。

 

我們從最簡單的說起(基礎知識,懂的同學直接往下拉),直接上代碼:

        static int amount;
        static void AddTV(int amount)
        {
            amount++;
            Console.WriteLine("方法中,amount="+amount);
        }

然后,我們將參數amout傳入AddTV()方法,希望能讓其+1

            amount = 10;
            AddTV(amount);
            Console.WriteLine("AddTV(amount)執行之后,amount=" + amount);

 那么執行的結果呢?

amount的數量并沒有發生變化。

為什么沒有變呢?

這是最入門的知識,通常的解釋是:

amount是int類型,int是值類型,所以當它作為參數時,傳遞給方法的是它的一個副本(復制品),因此方法中改變的是它的副本的值,amount本身并沒有改變。

OK,這完全沒有問題。而且,如果想改變的值,就需要加 ref 關鍵字,如下所示:

        static void AddTV(ref int amount)
        {
            amount++;
            Console.WriteLine("方法中,amount="+amount);
        }

大家自己跑一下,看看結果有什么不一樣。

這叫做參數的引用傳遞。

 

這是最基礎的知識,非常清晰。好的,接著,C#是面向對象的語言嘛,我們要引入一個對象。

    public class House
    {
        public int TVAmount { get; set; }
    }

 

然后,我們把House對象作為參數傳遞,值傳遞,不帶ref的。如下所示:

        static void AddTV(House house)
        {
            house.TVAmount++;
            Console.WriteLine("方法中,house.TVAmount=" + house.TVAmount);
        }


            House house = new House();
            AddTV(house);
            Console.WriteLine("AddTV(house)執行之后,house.TVAmount=" + house.TVAmount);

執行之后你會發現:

咦?house.TVAmount的值變了耶!

為什么呢?

有的同學聽到的解釋是這樣的:

House是對象,是引用類型,引用類型作為參數傳遞到方法中,它的值會被方法改變。

 

有些同學,哦!記住了:值類型傳進去不變,引用類型傳進去要變,但值類型引用傳遞又要變……雖然有點繞,但死背下來也行。

但有的同學就開始開始拋問題了:(這種同學特別值得表揚!安利一篇我的文章:講課這些天(五)怎么才能把代碼寫好?

值類型的引用傳遞,和引用類型的值傳遞,效果都一樣,那他們有什么區別呢?

 

Good question!

實際上,死背上面的,是會出問題的,我還是用代碼展示一下:

        static void AddTV(House house)
        {
            house = new House();
            house.TVAmount++;
            Console.WriteLine("方法中,house.TVAmount=" + house.TVAmount);
        }

這樣寫,眼尖的同學一眼就能看出差別:這一次方法體內多了一個:house = new House();

不要以為這是抽風啊,實際的開發代碼中,各種各樣的原因,很多時候都確實會在方法體內重新new一個參數實例的。

那運行結果怎么樣的呢?

怎么樣?!引用類型也不好使了?現在,是不是

 

 

不像JavaScript到處都是bug和設計缺陷(是的,日常黑js一百年,),C#是一門嚴謹清晰的語言,不會有什么“靈異”事件。現象和你的想法不一致,一定是你的想法出了問題。

所以,要真正地弄明白這里面的道道,我們還是要回到原點:

 

首先的首先,看看這代碼,你真的明白是什么意思么:

            House house 
                = 
                new House();

我為什么要寫成三行?

因為這其實是三個過程:

  • House house 這是聲明了一個變量
  • new House() 這是生成了一個對象
  • = 把 house 和 new House() 關聯起來

注意,注意我用的是“關聯”,很多人喜歡說“賦值”,甚至“等于”,這就容易造成我們理解上的誤區。

為了理解這種關聯,我畫了一幅圖:

 

觀察這幅圖,house和New House,是不同的數據儲存。事實上,在house里面,有一個記錄了new Houuse()存儲位置的“引用”(reference,這個英文單詞有助于我們理解)。所以,當我們house.TVAmount的時候,是通過house找到new House(),然后得到new House()的數據進行操作。

不知道大家能不能明白這一點?

作為對比,我們來看看值類型是怎么樣子的。

整個這一塊都是int i,int i 里面就直接的存儲了10這個數據,沒有引用,int i里直接存放數值10,所以叫做“值類型”。

 

好了,理解了上面的概念之后,我們回頭來看方法參數。

C#的說法非常的清晰,只看有沒有 ref 關鍵字:

  1. 不帶ref的,一定是“值傳遞”
  2. 帶ref的,一定是“引用傳遞”

和傳遞的是什么類型的參數,半毛錢關系沒有。

關鍵是,你要知道:當參數為引用類型時,傳遞的不是對象(new House()),而是對象的引用(house)

所以,

  • 如果是值傳遞,傳遞的是 對象引用的 副本
  • 如果是引用傳遞,傳遞的是 對象引用 本身

什么叫做對象引用的副本呢?還是給一幅圖:

 

 明白了吧?作為參數的house的副本,還是指向的New House對象,所以,在方法體中使用:house.TVAmount++,最終修改的還是原來的new House()里面的值。

 

但是,當你在方法體中:house = new House(); 你實際上就干了件啥事呢?

 

然后,你再:house.TVAmount++,改變的是新的House對象的值啊!(請結合英文單詞 new 來理解這一點)

所以,原來的 house 引用指向的對象,就根本沒有發生改變。

 

希望你仍然還保持著清醒的頭腦,沒有被我弄暈,O(∩_∩)O~

這樣我們就可以接著往下走。那假如我們既要保留方法體內的:house = new House(); 又要通過方法,改變傳入對象的值,我應該怎么辦呢?

……

干脆留作思考題吧? o( ̄┰ ̄*)ゞ 

請同學們在理解原理的基礎上自己去寫一寫,跑一跑,仔細的體會體會。

 

最后,為了更清晰直觀的看到所謂“對象的引用”的變化,我給大家一個神器:在調試時使用一元運算符 & 來查看變量的內存地址:

 

好了,自己折騰去吧!

enjoy it。

周末發帖,送給愛學習的同學們!

 

 

+++++++++++++++++++++++++++++++

 

最后,悄悄的說一句:我們的 一起幫 有了好多新功能,不想去看一看?


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()