精品秘无码一区二区三区老师-精品秘一区二三区免费雷安-精品蜜桃秘一区二区三区-精品蜜桃秘一区二区三区粉嫩-精品蜜桃一区二区三区-精品蜜臀国产aⅴ一区二区三区

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

C#.net core 基礎(chǔ) - 值傳遞、引用傳遞

freeflydom
2025年1月2日 8:39 本文熱度 1808

不知道你在開發(fā)過程中有沒有遇到過這樣的困惑:這個變量怎么值被改?這個值怎么沒變?

今天就來和大家分享可能導致這個問題的根本原因值傳遞 vs 引用傳遞。

在此之前我們先回顧兩組基本概念:

值類型 vs 引用類型

值類型: 直接存儲數(shù)據(jù),數(shù)據(jù)存儲在棧上;

引用類型: 存儲數(shù)據(jù)對象的引用,數(shù)據(jù)實際存儲在堆上。

形參 vs 實參

形參: 即形式參數(shù),表示調(diào)用方法時,方法需要你傳遞的值。方法聲明定義了其形參。也就是說在定義方法時,緊跟在方法名后面括號中的參數(shù)列表就是形參。

實參: 即實際參數(shù),表示調(diào)用方法時,你傳遞給方法形參的值。調(diào)用代碼在調(diào)用過程時提供實參。也就是說在調(diào)用方法時,緊跟在方法名后面括號中的參數(shù)列表就是實參。

再來回顧一下值類型和引用類型在內(nèi)存中是怎么存儲的呢?

對于值類型變量的值直接存儲在棧中,如下圖的int a=10,10就直接存在棧空間中,而其棧空間對應的內(nèi)存地址為0x66666668;對于引用類型變量本身存儲的是實例對象的引用,即實例對象在堆中的實際內(nèi)存地址,因此引用類型變量是存儲其實例對象的引用于棧上,如下圖中變量Test a在棧中實際存儲的是實例對象Test a在堆中的內(nèi)存地址0x88888880,而棧空間對應的內(nèi)存地址為0x66666668。

棧也是有內(nèi)存地址的,這一點很重要,無論棧空間上存儲的是值還是引用地址,這個棧空間本身也有自己對應的內(nèi)存地址。

什么是值傳遞?什么是引用傳遞?

值傳遞:如果變量按值傳遞給方法,則會把變量的副本傳遞給方法。對于值類型則把變量的副本傳遞給方法,對于引用類型則把變量的引用的副本傳遞給方法。因此被調(diào)用方法參數(shù)會創(chuàng)建一個新的內(nèi)存地址用于接收存儲變量,因此在方法內(nèi)部對變量修改并不會影響原來的值。

引用傳遞:如果變量按引用傳遞給方法,則會把變量的引用傳遞給方法,對于值類型則把變量的棧空間地址傳遞給方法,對于引用類型則把變量的引用的棧空間地址傳遞給方法。因此被調(diào)用方法參數(shù)不會創(chuàng)建一個新的內(nèi)存地址用于接收存儲變量,意味著形參與實參共同指向相同的內(nèi)存地址,因此在方法內(nèi)部修對變量修改會影響原來的值。

上面的描述可能有點拗口,下面我們在基于值類型、引用類型、值傳遞、引用傳遞各種組合進行一個詳細說明。

01、值類型按值傳遞

當值類型按值傳遞時,調(diào)用者會把值類型變量的副本傳遞給方法,因此被調(diào)用方法參數(shù)會創(chuàng)建一個新的內(nèi)存地址用于接收存儲變量,因此當在方法內(nèi)部對參數(shù)進行修改時并不會影響調(diào)用者調(diào)用處的值類型變量。

傳遞值類型變量的副本就是相當于在棧上,又復制了一個同樣的值,而且內(nèi)存地址還不一樣,所以互不影響。如下圖把a賦值給b,則b直接新開辟了一個棧空間,雖然a和b都是10,但是它們在不同的地址空間中,因此如果他們各自被修改了,也互不影響。

下面我們寫個例子演示一下,這個例子就是定義個變量a并賦值,然后調(diào)用一個方法此方法內(nèi)對傳進來的參數(shù)a進行加1,具體代碼如下:

public static void ValueByValueRun(){
    var a = 10;
    Console.WriteLine($"調(diào)用者-調(diào)用方法前 a 值:{a}");
    ChangeValueByValue(a);
    Console.WriteLine($"調(diào)用者-調(diào)用方法后 a 值:{a}");
}public static void ChangeValueByValue(int a){
    Console.WriteLine($"    被調(diào)用方法-接收到 a 值:{a}");
    a = a + 1;
    Console.WriteLine($"    被調(diào)用方法-修改后 a 值:{a}");
}

運行結(jié)果如下:

通過代碼執(zhí)行結(jié)果可以發(fā)現(xiàn),方法內(nèi)對變量的修改已經(jīng)生效,但是不沒有影響到調(diào)用者調(diào)用處的變量值。

02、引用類型按值傳遞

當引用類型按值傳遞時,調(diào)用者會把引用類型變量的引用副本傳遞給方法,因此被調(diào)用方法參數(shù)會創(chuàng)建一個新的內(nèi)存地址用于接收存儲變量,而對于一個引用類型變量來說其本身存儲的就是引用類型實例對象的引用副本,而方法接收到的也是此變量引用的副本,所以調(diào)用者參數(shù)和被調(diào)用方法參數(shù)是引用了同一個實例對象的兩個引用副本。如下圖Test a可以理解為調(diào)用者傳的實參,Test b可以理解為被調(diào)用方法定義的形參,這兩個參數(shù)都只是指向堆中Test a的引用副本。

因此可以得出兩個結(jié)論:

1、變量a和b都是指向?qū)嵗龑ο骉est a的引用,所以無論變量a或b,只要有一個更新了實例成員則另一個變量也會同步發(fā)生變化。

2、雖然變量a和b都是指向?qū)嵗龑ο骉est a的引用,但是他們存儲在棧上的內(nèi)存地址卻不同,因此如果他們各種重新分配實例也就是new一個新對象,則另一個變量無法感知到還是保持原因狀態(tài)不變。

我們先用代碼說明第一個結(jié)論:

public static void ChangeReferenceByValueRun(){
    var a = new Test
    {
        Age = 10
    };
    Console.WriteLine($"調(diào)用者-調(diào)用方法前 a.Age 值:{a.Age}");
    ChangeReferenceByValue(a);
    Console.WriteLine($"調(diào)用者-調(diào)用方法后 a.Age 值:{a.Age}");
}
public static void ChangeReferenceByValue(Test a){
    Console.WriteLine($"    被調(diào)用方法-接收到 a.Age 值:{a.Age}");
    a.Age = a.Age + 1;
    Console.WriteLine($"    被調(diào)用方法-修改后 a.Age 值:{a.Age}");
}

運行結(jié)果如下:

可以看到被調(diào)用方法中a實例對象的Age屬性發(fā)生變化后,調(diào)用者中變量也同步發(fā)生了變化。

對于第二個結(jié)論我們這樣論證,在方法中直接對參數(shù)new一個新對象,看看原變量是否發(fā)生變化,代碼如下:

public static void NewReferenceByValueRun(){
    var a = new Test
    {
        Age = 10
    };
    Console.WriteLine($"調(diào)用者-調(diào)用方法前 a.Age 值:{a.Age}");
    NewReferenceByValue(a);
    Console.WriteLine($"調(diào)用者-調(diào)用方法后 a.Age 值:{a.Age}");
}
public static void NewReferenceByValue(Test a){
    Console.WriteLine($"    被調(diào)用方法-接收到 a.Age 值:{a.Age}");
    a = new Test
    {
        Age = 100
    };
    Console.WriteLine($"    被調(diào)用方法-new后 a.Age 值:{a.Age}");
}

執(zhí)行結(jié)果如下:

可以發(fā)現(xiàn)當在方法中對變量執(zhí)行new操作后,調(diào)用者處的變量并沒有發(fā)生變化。

為什么會這樣呢?因為對于引用類型來說,形參和實參是對引用類型的實例對象引用的兩個副本,而這兩個副本存儲在棧上又分別在不同的內(nèi)存地址空間上,而new主要就是重新分配內(nèi)存,這就導致形參變量a=new后,棧上形參變量a指向了Test新的實例對象的引用,而實參變量a還是保持原有實例對象引用不變。

如下圖所示。

03、值類型按引用傳遞

當值類型按引用傳遞時,調(diào)用者會把值類型變量對應的棧空間地址傳遞給方法,因此被調(diào)用方法參數(shù)不會創(chuàng)建一個新的內(nèi)存地址用于接收存儲變量,因此當在方法內(nèi)部對參數(shù)進行修改時并同樣會影響調(diào)用者調(diào)用處的值類型變量。

傳遞值類型變量對應的棧空間地址就意味著形參與實參共同指向相同的內(nèi)存地址,所以才導致對形參修改時,實參也會同步發(fā)生變化。

我們用一個小例子演示一下:

public static void ValueByReferenceRun(){
    Console.WriteLine($"值類型按引用傳遞");
    var a = 10;
    Console.WriteLine($"調(diào)用者-調(diào)用方法前 a 值:{a}");
    ChangeValueByReference(ref a);
    Console.WriteLine($"調(diào)用者-調(diào)用方法后 a 值:{a}");
}
public static void ChangeValueByReference(ref int a){
    Console.WriteLine($"    被調(diào)用方法-接收到 a 值:{a}");
    a = a + 1;
    Console.WriteLine($"    被調(diào)用方法-修改后 a 值:{a}");
}

執(zhí)行結(jié)果如下:

可以發(fā)現(xiàn)調(diào)用者處的值類型變量已經(jīng)發(fā)生改變。

04、引用類型按引用傳遞

當引用類型按引用傳遞時,調(diào)用者會把引用類型變量對應的棧空間地址傳遞給方法,因此被調(diào)用方法參數(shù)不會創(chuàng)建一個新的內(nèi)存地址用于接收存儲變量,因此當在方法內(nèi)部對參數(shù)進行修改時并同樣會影響調(diào)用者調(diào)用處的引用類型變量。

傳遞引用類型變量對應的棧空間地址就意味著形參與實參共同指向相同的內(nèi)存地址,因此對形參修改時,實參也會同步發(fā)生變化,而且這個里的修改不單單指修改實例成員,還包括new一個新實例對象。

下面我們看一個修改實例成員的例子:

public static void ChangeReferenceByReferenceRun(){
    Console.WriteLine($"引用類型按引用傳遞 - 修改實例成員");
    var a = new Test
    {
        Age = 10
    };
    Console.WriteLine($"調(diào)用者-調(diào)用方法前 a.Age 值:{a.Age}");
    ChangeReferenceByReference(ref a);
    Console.WriteLine($"調(diào)用者-調(diào)用方法后 a.Age 值:{a.Age}");
}
public static void ChangeReferenceByReference(ref Test a){
    Console.WriteLine($"    被調(diào)用方法-接收到 a.Age 值:{a.Age}");
    a.Age = a.Age + 1;
    Console.WriteLine($"    被調(diào)用方法-修改后 a.Age 值:{a.Age}");
}

執(zhí)行結(jié)果如下:

再看看new一個新對象的例子:

public static void NewReferenceByReferenceRun(){
    Console.WriteLine($"引用類型按引用傳遞 - new 新實例");
    var a = new Test
    {
        Age = 10
    };
    Console.WriteLine($"調(diào)用者-調(diào)用方法前 a.Age 值:{a.Age}");
    NewReferenceByReference(ref a);
    Console.WriteLine($"調(diào)用者-調(diào)用方法后 a.Age 值:{a.Age}");
}
public static void NewReferenceByReference(ref Test a){
    Console.WriteLine($"    被調(diào)用方法-接收到 a.Age 值:{a.Age}");
    a = new Test
    {
        Age = 100
    };
    Console.WriteLine($"    被調(diào)用方法-new后 a.Age 值:{a.Age}");
}

執(zhí)行結(jié)果如下:

另外string是一個特殊的引用類型,string類型變量的按值傳遞和按引用傳遞和值類型是一致的,也就是要把string類型當值類型一樣看待就行。string類型的特殊性我們后面會單獨具體介紹。

在C#中以下修飾符可應用與參數(shù)聲明,并且會使得參數(shù)按引用傳遞:ref、out、readonly ref、in。對于每個修飾符具體怎么使用就不再這里細說了。

相信到這里你應該就可以回答我之前在《LeetCode題集-2 - 兩數(shù)相加》最后提的問題了。

:測試方法代碼以及示例源碼都已經(jīng)上傳至代碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner

轉(zhuǎn)自https://www.cnblogs.com/hugogoos/p/18419656


該文章在 2025/1/2 8:42:30 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運作、調(diào)度、堆場、車隊、財務費用、相關(guān)報表等業(yè)務管理,結(jié)合碼頭的業(yè)務特點,圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點晴WMS倉儲管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

主站蜘蛛池模板: 国产精品无码翘臀在线观看 | 午夜福利影院私人爽爽免费 | 欧美乱妇无码毛片 | 黑人巨茎大战欧美白妇免费 | 久久久久国产精品夜夜夜夜夜 | 精品国产鲁一鲁一区二区红桃影视:全新上线 | 麻豆国产伦理在线手机伦理片 | 亚洲首页国产精品丝袜 | 日本巨大的奶头在线观看 | 国产亚洲av手机在线观看 | 综合久久国产久久久久av综合网成人 | 亚洲一区国产美女在线速度快 | 99久久无码一区人妻A片蜜桃 | 亚洲av无码一区二区三区电影 | 欧美一区视频 | 亚洲精品无码色午夜 | 日韩做a爰片久久毛片a片 | 精品无码国产污污污免费网站 | 亚洲国产av无码精品 | 自拍视频亚洲综合在线精品 | 台湾无码av一区二区三区 | 另类国产ts人妖视频网站 | 国产日韩综合一区二区性色av | 欧美精品久久96人妻无码 | 性色a∨人人爽网站hdkp885 | 美国一级天天操夜夜操 | 国产v视频一区二区三区亚洲不卡在线网站天堂 | 国产av演绎护士的遭遇 | 日日草夜夜操 | 亚洲福利在线一区二区三区18videos下载丰满老师 | 亚洲AV无码色情第一综合网 | 欧美老人操小孩黄色一级视频 | 欧美日本亚欧在线观看 | 高清中文字幕不卡 | 日韩欧美国产师生制服 | 欧美不卡一区二区 | 熟女人妻 中文字幕在线 | 日韩不卡在线播放 | 国产a三级久久精品 | 国产三级精品三级 | 国产精品美女久久久久久久久 |