亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

深刻理解C#的傳值調用和傳引用調用

2019-11-14 16:15:37
字體:
來源:轉載
供稿:網友

傳值調用和傳引用調用是幾乎所有主流語言都會涉及到的問題,下面我談談我對C#中傳值調用和傳引用調用的理解。

1. 一般對C#中傳值調用和傳引用調用的理解

  • 如果傳遞的參數是基元類型(int,float等)或結構體(struct),那么就是傳值調用。
  • 如果傳遞的參數是類(class)那么就是傳引用調用。
  • 如果傳遞的參數前有ref或者out關鍵字,那么就是傳引用調用。

驗證示例的代碼如下: 

view sourcePRint?

01    using System;

02   

03    public class ArgsByRefOrValue

04    {

05        public static void Main(string[] args)

06        {

07            // 實驗1. 傳值調用--基元類型

08            int i = 10;

09            Console.WriteLine("before call ChangeByInt: i = " + i.ToString());

10            ChangeByInt(i);

11            Console.WriteLine("after call ChangeByInt: i = " + i.ToString());

12   

13            Console.WriteLine("==============================================");

14            // 實驗2. 傳值調用--結構體

15            Person_val p_val = new Person_val();

16            p_val.name = "old val name";

17            Console.WriteLine("before call ChangeByStruct: p_val.name = " + p_val.name);

18            ChangeByStruct(p_val);

19            Console.WriteLine("after call ChangeByStruct: p_val.name = " + p_val.name);

20   

21            Console.WriteLine("==============================================");

22            // 實驗3. 傳引用調用--類

23            Person_ref p_ref = new Person_ref();

24            p_ref.name = "old ref name";

25            Console.WriteLine("before call ChangeByClass: p_ref.name = " + p_ref.name);

26            ChangeByClass(p_ref);

27            Console.WriteLine("after call ChangeByClass: p_ref.name = " + p_ref.name);

28   

29            Console.WriteLine("==============================================");

30            // 實驗4. 傳引用調用--利用ref

31            Person_ref p = new Person_ref();

32            p.name = "old ref name";

33            Console.WriteLine("before call ChangeByClassRef: p.name = " + p.name);

34            ChangeByClassRef(ref p);

35            Console.WriteLine("after call ChangeByClassRef: p.name = " + p.name);

36   

37            Console.ReadKey(true);

38        }

39   

40        static void ChangeByInt(int i)

41        {

42            i = i + 10;

43            Console.WriteLine("when calling ChangeByInt: i = " + i.ToString());

44        }

45   

46        static void ChangeByStruct(Person_val p_val)

47        {

48            p_val.name = "new val name";

49            Console.WriteLine("when calling ChangeByStruct: p_val.name = " + p_val.name);

50        }

51   

52        static void ChangeByClass(Person_ref p_ref)

53        {

54            p_ref.name = "new ref name";

55            Console.WriteLine("when calling ChangeByClass: p_ref.name = " + p_ref.name);

56        }

57   

58        static void ChangeByClassRef(ref Person_ref p)

59        {

60            p.name = "new ref name";

61            Console.WriteLine("when calling ChangeByClassRef: p.name = " + p.name);

62        }

63    }

64   

65    public struct Person_val

66    {

67        public string name;

68    }

69   

70    public class Person_ref

71    {

72        public string name;

73    }

 

運行結果如下:

 

看起來似乎上面代碼中實驗3實驗4是一樣的,即對于類(class)來說,不管加不加ref或out,都是傳引用調用。

其實,這只是表面的現象,只要稍微改一下代碼,結果就不一樣了。

修改上面代碼,再增加兩個實驗。

 

001  using System;

002 

003  public class ArgsByRefOrValue

004  {

005      public static void Main(string[] args)

006      {

007          // 實驗1. 傳值調用--基元類型

008          int i = 10;

009          Console.WriteLine("before call ChangeByInt: i = " + i.ToString());

010          ChangeByInt(i);

011          Console.WriteLine("after call ChangeByInt: i = " + i.ToString());

012 

013          Console.WriteLine("==============================================");

014          // 實驗2. 傳值調用--結構體

015          Person_val p_val = new Person_val();

016          p_val.name = "old val name";

017          Console.WriteLine("before call ChangeByStruct: p_val.name = " + p_val.name);

018          ChangeByStruct(p_val);

019          Console.WriteLine("after call ChangeByStruct: p_val.name = " + p_val.name);

020 

021          Console.WriteLine("==============================================");

022          // 實驗3. 傳引用調用--類

023          Person_ref p_ref = new Person_ref();

024          p_ref.name = "old ref name";

025          Console.WriteLine("before call ChangeByClass: p_ref.name = " + p_ref.name);

026          ChangeByClass(p_ref);

027          Console.WriteLine("after call ChangeByClass: p_ref.name = " + p_ref.name);

028 

029          Console.WriteLine("==============================================");

030          // 實驗4. 傳引用調用--利用ref

031          Person_ref p = new Person_ref();

032          p.name = "old ref name";

033          Console.WriteLine("before call ChangeByClassRef: p.name = " + p.name);

034          ChangeByClassRef(ref p);

035          Console.WriteLine("after call ChangeByClassRef: p.name = " + p.name);

036 

037          Console.WriteLine("==============================================");

038          // 實驗5. 傳引用調用--類 在調用的函數重新new一個對象

039          Person_ref p_ref_new = new Person_ref();

040          p_ref_new.name = "old new ref name";

041          Console.WriteLine("before call ChangeByClassNew: p_ref_new.name = " + p_ref_new.name);

042          ChangeByClassNew(p_ref_new);

043          Console.WriteLine("after call ChangeByClassNew: p_ref_new.name = " + p_ref_new.name);

044 

045          Console.WriteLine("==============================================");

046          // 實驗6. 傳引用調用--利用ref 在調用的函數重新new一個對象

047          Person_ref p_new = new Person_ref();

048          p_new.name = "old new ref name";

049          Console.WriteLine("before call ChangeByClassRefNew: p_new.name = " + p_new.name);

050          ChangeByClassRefNew(ref p_new);

051          Console.WriteLine("after call ChangeByClassRefNew: p_new.name = " + p_new.name);

052 

053          Console.ReadKey(true);

054      }

055 

056      static void ChangeByInt(int i)

057      {

058          i = i + 10;

059          Console.WriteLine("when calling ChangeByInt: i = " + i.ToString());

060      }

061 

062      static void ChangeByStruct(Person_val p_val)

063      {

064          p_val.name = "new val name";

065          Console.WriteLine("when calling ChangeByStruct: p_val.name = " + p_val.name);

066      }

067 

068      static void ChangeByClass(Person_ref p_ref)

069      {

070          p_ref.name = "new ref name";

071          Console.WriteLine("when calling ChangeByClass: p_ref.name = " + p_ref.name);

072      }

073 

074      static void ChangeByClassRef(ref Person_ref p)

075      {

076          p.name = "new ref name";

077          Console.WriteLine("when calling ChangeByClassRef: p.name = " + p.name);

078      }

079 

080      static void ChangeByClassNew(Person_ref p_ref_new)

081      {

082          p_ref_new = new Person_ref();

083          p_ref_new.name = "new ref name";

084          Console.WriteLine("when calling ChangeByClassNew: p_ref_new.name = " + p_ref_new.name);

085      }

086 

087      static void ChangeByClassRefNew(ref Person_ref p_new)

088      {

089          p_new = new Person_ref();

090          p_new.name = "new ref name";

091          Console.WriteLine("when calling ChangeByClassRefNew: p_new.name = " + p_new.name);

092      }

093  }

094 

095  public struct Person_val

096  {

097      public string name;

098  }

099 

100  public class Person_ref

101  {

102      public string name;

103  }

 

則運行結果為:

 

實驗5的運行結果似乎說明即使參數是類(class),只要不加ref,也是傳值調用。

下面就引出了我的理解。

2. 沒有ref時,即使參數為引用類型(class)時,也可算是一種傳值調用

參數為引用類型時,傳遞的是該引用類型的地址的一份拷貝,“該引用類型的地址的一份拷貝”即為傳值調用的“值”。

注意這里說傳遞的是該引用類型的地址的一份拷貝,而不是引用類型的地址。

下面將用圖的形式來說明以上實驗3,實驗5和實驗6中內存的情況。

2.1 首先是實驗3

實驗3的內存圖如下,實參是函數ChangeByClass外的Person_ref對象,形參是函數ChangeByClass內的Person_ref對象。

捕獲

從圖中我們可以看出實參new出來之后就在托管堆上分配了內存,并且在棧上保存了對象的指針。

調用函數ChangeByClass后,由于沒有ref參數,所以將棧上的實參p_val拷貝了一份作為形參,注意這里p_val(實參)p_val(形參)是指向托管堆上的同一地址。

所以說沒有ref時,即使參數為引用類型(class)時,也可算是一種傳值調用,這里的值就是托管堆中對象的地址(0x1000)。

調用函數ChangeByClass后,通過p_val(形參)修改了name屬性的值,由于p_val(實參)p_val(形參)是指向托管堆上的同一地址,所以函數外的p_val(實參)的name屬性也被修改了。

捕獲

2.2 然后是實驗5

上面的實驗3從執行結果來看似乎是傳引用調用,因為形參的改變導致了實參的改變。

下面的實驗5就可以看出,p_val(形參)p_val(實參)并不是同一個變量,而是p_val(實參)的一個拷貝。

捕獲

從圖中可以看出第一步還是和實驗3一樣,但是在調用函數ChangeByClassNew后,就不一樣了。

函數ChangeByClassNew中,對p_val(形參)重新分配了內存(new操作),使其指向了新的地址(0x1100),如下圖:

捕獲

所以p_val(形參)的name屬性改了時候,p_val(實參)的name屬性還是沒變。

2.3 最后是實驗6

我覺得實驗6是真正的傳引用調用。不廢話了,直接上第一個圖。

捕獲

參數中加了ref關鍵字之后,其實傳遞的不是托管堆中對象的地址(0x1000),而是棧上p_val(實參)的地址(0x0001)。

所以這里實參和形參都是棧上的同一個東西,沒有什么區別了。我覺得這才是真正的傳引用調用。

然后調用了函數ChangeByClassRefNew,函數中對p_val(形參)重新分配了內存(new操作),使其指向了新的地址(0x1100)。

捕獲

由于p_val(形參)就是p_val(實參),所以p_val(形參)的name屬性改變后,函數ChangeByClassRefNew外的p_val(實參)的name屬性也被改變了。

而原先分配的對象(地址0x1000)其實已經沒有被引用了,隨時會被GC回收。

3. 結論

  • 如果傳遞的參數是基元類型(int,float等)或結構體(struct),那么就是傳值調用。

 

 

 

 

 

 

  • 如果傳遞的參數前有ref或者out關鍵字,那么就是傳引用調用。

 

 

 

 

 

 

  • 如果傳遞的參數是類(class)并且沒有ref或out關鍵字:
    1. 如果調用的函數中對參數重新進行了地址分配(new操作),那么執行結果類似傳值調用
    2. 如果調用的函數中沒有對參數重新進行了地址分配,直接就是使用了傳遞的參數,那么執行結果類似傳引用調用

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲成人av在线| 在线日韩日本国产亚洲| 92看片淫黄大片欧美看国产片| 国产综合色香蕉精品| 欧美精品精品精品精品免费| 91夜夜未满十八勿入爽爽影院| 欧美精品在线极品| 国产精品免费一区二区三区都可以| 久久69精品久久久久久久电影好| 国产精品观看在线亚洲人成网| 亚洲无亚洲人成网站77777| 98视频在线噜噜噜国产| 国产日产欧美a一级在线| 国产精品国产三级国产aⅴ浪潮| 国产精品视频不卡| 精品在线观看国产| 国产精品爱久久久久久久| 亚洲最大成人网色| 亚洲综合一区二区不卡| 色阁综合伊人av| 亚洲香蕉成人av网站在线观看| 中文字幕av一区中文字幕天堂| 欧美大肥婆大肥bbbbb| 国产成人精彩在线视频九色| 最新中文字幕亚洲| www.亚洲成人| 91久久精品日日躁夜夜躁国产| 欧美日韩在线观看视频| 国产精选久久久久久| 精品爽片免费看久久| 亚洲大尺度美女在线| 伦理中文字幕亚洲| 亚洲第一综合天堂另类专| 欧美激情中文字幕在线| 欧美做受高潮电影o| 日韩激情视频在线| 亚洲性视频网站| 一区二区三区久久精品| 欧美激情欧美狂野欧美精品| 亚洲va久久久噜噜噜久久天堂| 亚洲精品一区在线观看香蕉| 2025国产精品视频| 在线精品高清中文字幕| 精品亚洲国产成av人片传媒| 久久综合伊人77777| 国产精品日韩在线| 久久在线精品视频| 亚洲美女在线看| 欧美第一黄网免费网站| 亚洲黄色成人网| 久久精品国亚洲| 国产香蕉97碰碰久久人人| 性亚洲最疯狂xxxx高清| 国产欧洲精品视频| 国产精品三级在线| 午夜精品久久久久久久99黑人| 国产啪精品视频网站| 一区二区亚洲精品国产| 国色天香2019中文字幕在线观看| 中文字幕免费精品一区高清| 91a在线视频| 国产一区二区在线免费视频| 最近的2019中文字幕免费一页| 日韩欧美精品网站| 色诱女教师一区二区三区| 久久99精品视频一区97| 91精品久久久久久久久久久久久| 色综合久久88色综合天天看泰| 黑人巨大精品欧美一区免费视频| 亚洲一区二区三区视频播放| 欧美激情精品久久久久久蜜臀| 欧美性xxxxx极品娇小| 亚洲人成免费电影| 国产精品扒开腿做爽爽爽视频| 26uuu亚洲伊人春色| 欧美午夜视频在线观看| 亚洲第一视频在线观看| 国产成人福利夜色影视| 久热精品视频在线免费观看| 亚洲男人天堂久| 欧美在线视频在线播放完整版免费观看| 亚洲欧美一区二区三区在线| 韩曰欧美视频免费观看| 日韩精品在线视频| 亚洲xxxx妇黄裸体| 国产精品嫩草视频| 亚洲视频在线免费观看| 久久久久久18| 国产97在线播放| 日韩免费不卡av| 国产日产亚洲精品| 在线观看免费高清视频97| 4k岛国日韩精品**专区| 欧美精品一区三区| 久久精品国产电影| 国产精品美女在线观看| 福利一区福利二区微拍刺激| 国产成人精品视频| 国产精品爱啪在线线免费观看| 亚洲乱码一区av黑人高潮| 国内精品久久久久久久| 亚洲精品国精品久久99热一| 国产精品美腿一区在线看| 亚洲国产美女精品久久久久∴| 国产精品丝袜久久久久久不卡| 午夜精品免费视频| 亚洲国产小视频| 一区国产精品视频| 国产精品九九久久久久久久| 国产成人+综合亚洲+天堂| 91av在线精品| 日韩视频一区在线| 在线播放日韩欧美| 欧美壮男野外gaytube| 亚洲精品国产免费| 国产精品成人一区| 美女撒尿一区二区三区| 欧美日韩国产一区中文午夜| 欧美激情一二三| 日韩av免费在线| 久久免费视频网站| 粉嫩av一区二区三区免费野| 亚洲欧美日韩综合| 久久久伊人日本| 日韩国产精品一区| 亚洲国产中文字幕久久网| 久久国产精品影视| 国产一区二区久久精品| 午夜精品久久久久久久久久久久| 97超级碰碰碰久久久| 国产精品一区专区欧美日韩| 成人在线激情视频| 成人精品久久av网站| 国产在线精品成人一区二区三区| 91精品久久久久久久久青青| 91亚洲精品久久久| 91高清免费视频| 久久伊人91精品综合网站| 亚洲国产中文字幕久久网| 美日韩丰满少妇在线观看| 日本欧美在线视频| 亚洲精品一区av在线播放| 视频在线观看一区二区| 国产免费一区二区三区在线观看| 久久久久久久97| 亚洲日韩欧美视频一区| 国产性色av一区二区| 欧美日韩在线视频首页| 亚洲男人天堂九九视频| 欧美洲成人男女午夜视频| 91av在线不卡| 国产97在线|日韩| 国产精品青草久久久久福利99| 精品欧美aⅴ在线网站| 57pao成人国产永久免费| 777精品视频| 欧美裸体xxxx极品少妇软件| 欧美精品www| 激情成人在线视频| 亚洲网站视频福利| 国产精品视频色| 亚洲欧美日韩区| 国产精自产拍久久久久久蜜| 国产69精品久久久久久|