ASP.NET堆和棧二之值類型和引用類型的參數(shù)傳遞和內(nèi)存分配
".NET的堆和棧"系列:
在" ASP.NET堆和棧一之基本概念和值類型內(nèi)存分配"中,了解了"堆"和"棧"的基本概念,以及值類型的內(nèi)存分配。我們知道:當執(zhí)行一個方法的時候,值類型實例會在"棧"上分配內(nèi)存,而引用類型實例會在"堆"上分配內(nèi)存,當方法執(zhí)行完畢,"棧"上的實例由操作系統(tǒng)自動釋放,"堆"上的實例由.NET Framework的GC進行回收。而本篇的重點要放在:值類型和引用類型參數(shù)的傳遞,以及內(nèi)存分配。
傳遞值類型參數(shù)
class Class1{ public void Go() {int x = 5;AddFive(x); Console.WriteLine(x.ToString()); } public int AddFive(int pValue) {pValue += 5;return pValue; }}
大致過程如下:
1、值類型變量x被放到"棧"上。
2、開始執(zhí)行AddFive()方法,值類型變量pValue被放到"棧"上,并把x的值賦值給pValue,pValue的值變成了5。
3、繼續(xù)執(zhí)行AddFive()方法,pValue的值變成了10。
4、執(zhí)行完AddFive()方法,釋放pValue的內(nèi)存,"棧"指針回到x,線程重新回到Go()方法中。
輸出結果:5
以上,在傳遞值類型參數(shù)x的時候,實際上是把x一個字節(jié)一個字節(jié)地拷貝給pValue。
傳遞容易造成"棧溢出"的值類型參數(shù),在值類型參數(shù)前加關鍵字ref
public struct MyStruct{ long a, b, c, d, e, f, g, h, i, j, k, l, m;}public void Go(){ MyStruct x = new MyStruct(); DoSomething(x);}public void DoSomething(MyStruct pValue){ // DO SOMETHING HERE....}
假設以上的值類型struct足夠大,而x和pValue都會被分配到"棧"上,這時可能造成"棧溢出"。
如何避免呢?
--解決辦法是讓DoSomething傳遞一個ref類型參數(shù)。這樣寫:
public struct MyStruct{ long a, b, c, d, e, f, g, h, i, j, k, l, m;}public void Go(){ MyStruct x = new MyStruct(); x.a = 5; DoSomething(ref x); Console.WriteLine(x.a.ToString()); } public void DoSomething(ref MyStruct pValue){ pValue.a = 12345;}
使用ref后,執(zhí)行DoSomething(ref x),是把x的地址賦值給了pValue,即pValue和x指向了同一個引用地址。當改變pValue的值,變化也會反映到x中。
輸出結果:12345
以上,為了避免"大型"值類型參數(shù)傳遞時造成的"棧溢出",可以在值類型前面加ref關鍵字,于是,在傳遞值類型參數(shù)x的時候,實際上是把x本身的棧地址拷貝給pValue,x和pValue指向同一個棧地址。
傳遞引用類型參數(shù)
傳遞引用類型參數(shù)的道理和在傳遞的值類型參數(shù)前面加ref關鍵字是一樣的。
public class MyInt{ public int MyValue;}public void Go(){ MyInt x = new MyInt(); x.MyValue = 2; DoSomething(x); Console.WriteLine(x.MyValue.ToString());}public void DoSomething(MyInt pValue){ pValue.MyValue = 12345;}
輸出結果:12345
以上大致過程是這樣:
1、在托管堆上創(chuàng)建一個MyInt類型的實例
2、在棧上創(chuàng)建一個MyInt類型的變量x指向堆上的實例
3、把托管堆上的公共字段MyValue賦值為2
4、通過DoSomething(x)方法,把x的引用地址賦值給pValue,即pValue和x指向同一個引用地址
5、改變pValue的值,也會反映到x上
以上,在傳遞引用類型參數(shù)x的時候,實際上是把x指向托管堆實例的引用地址拷貝給pValue,x和pValue指向同一個托管堆實例地址。
傳遞引用類型參數(shù),在引用類型參數(shù)之前加關鍵字ref
public class Thing{ } public class Animal:Thing{ public int Weight;} public class Vegetable:Thing{ public int Length;}public void Go(){ Thing x = new Animal(); Switcharoo(ref x); Console.WriteLine("x is Animal : " + (x is Animal).ToString()); Console.WriteLine("x is Vegetable : " + (x is Vegetable).ToString());} public void Switcharoo(ref Thing pValue){ pValue = new Vegetable();}
輸出結果:
x is Animal : False
x is Vegetable : True
以上大致過程是這樣:
1、在托管堆上創(chuàng)建Animal對象實例。
2、在棧上創(chuàng)建類型為Thing的x變量指向Animal實例的引用地址。
3、通過Switcharoo(ref x)方法把x本身的地址賦值給pValue,至此,pValue和x指向了相同的棧內(nèi)存地址,任何一方的變化都會反映到另外一方。
4、在Switcharoo(ref Thing pValue)內(nèi)部,在托管堆上創(chuàng)建Vegetable對象實例。
5、pValue指向Vegetable實例,也就相當于x指向Vegetable實例。
以上,當在引用類型參數(shù)之前加上關鍵字ref,再傳遞,是把x本身的棧地址拷貝給pValue,x和pValue指向同一個棧地址。
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對的支持。如果你想了解更多相關內(nèi)容請查看下面相關鏈接
相關文章:
1. ASP.NET MVC遍歷驗證ModelState的錯誤信息2. 使用EF Code First搭建簡易ASP.NET MVC網(wǎng)站并允許數(shù)據(jù)庫遷移3. ASP.NET MVC實現(xiàn)本地化和全球化4. ASP.NET MVC使用Identity增刪改查用戶5. ASP.NET MVC限制同一個IP地址單位時間間隔內(nèi)的請求次數(shù)6. asp.net core應用docke部署到centos7的全過程7. ASP.Net Core(C#)創(chuàng)建Web站點的實現(xiàn)8. ASP.NET MVC使用Quartz.NET執(zhí)行定時任務9. JS數(shù)據(jù)類型(基本數(shù)據(jù)類型、引用數(shù)據(jù)類型)及堆和棧的區(qū)別分析10. ASP.NET堆和棧三之引用類型對象拷貝和內(nèi)存分配
