程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> .NET的堆和棧02,值類型和引用類型參數傳遞以及內存分配,.net類型參數

.NET的堆和棧02,值類型和引用類型參數傳遞以及內存分配,.net類型參數

編輯:C#入門知識

.NET的堆和棧02,值類型和引用類型參數傳遞以及內存分配,.net類型參數


在" .NET的堆和棧01,基本概念、值類型內存分配"中,了解了"堆"和"棧"的基本概念,以及值類型的內存分配。我們知道:當執行一個方法的時候,值類型實例會在"棧"上分配內存,而引用類型實例會在"堆"上分配內存,當方法執行完畢,"棧"上的實例由操作系統自動釋放,"堆"上的實例由.NET Framework的GC進行回收。而本篇的重點要放在:值類型和引用類型參數的傳遞,以及內存分配。

 

主要包括:
■  傳遞值類型參數
■  傳遞容易造成"棧溢出"的值類型參數,在值類型參數前加關鍵字ref
■  傳遞引用類型參數
■  傳遞引用類型參數,在引用類型參數之前加關鍵字ref

 

  傳遞值類型參數

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、開始執行AddFive()方法,值類型變量pValue被放到"棧"上,並把x的值賦值給pValue,pValue的值變成了5。

 

3、繼續執行AddFive()方法,pValue的值變成了10。

 

4、執行完AddFive()方法,釋放pValue的內存,"棧"指針回到x,線程重新回到Go()方法中。

 

輸出結果:5


以上,在傳遞值類型參數x的時候,實際上是把x一個字節一個字節地拷貝給pValue。

 

  傳遞容易造成"棧溢出"的值類型參數,在值類型參數前加關鍵字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類型參數。這樣寫:

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後,執行DoSomething(ref x),是把x的地址賦值給了pValue,即pValue和x指向了同一個引用地址。當改變pValue的值,變化也會反映到x中。         

 

輸出結果:12345

 

以上,為了避免"大型"值類型參數傳遞時造成的"棧溢出",可以在值類型前面加ref關鍵字,於是,在傳遞值類型參數x的時候,實際上是把x本身的棧地址拷貝給pValue,x和pValue指向同一個棧地址。

 

  傳遞引用類型參數

傳遞引用類型參數的道理和在傳遞的值類型參數前面加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、在托管堆上創建一個MyInt類型的實例
2、在棧上創建一個MyInt類型的變量x指向堆上的實例
3、把托管堆上的公共字段MyValue賦值為2
4、通過DoSomething(x)方法,把x的引用地址賦值給pValue,即pValue和x指向同一個引用地址
5、改變pValue的值,也會反映到x上

 

以上,在傳遞引用類型參數x的時候,實際上是把x指向托管堆實例的引用地址拷貝給pValue,x和pValue指向同一個托管堆實例地址。

 

  傳遞引用類型參數,在引用類型參數之前加關鍵字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、在托管堆上創建Animal對象實例。
2、在棧上創建類型為Thing的x變量指向Animal實例的引用地址。
3、通過Switcharoo(ref x)方法把x本身的地址賦值給pValue,至此,pValue和x指向了相同的棧內存地址,任何一方的變化都會反映到另外一方。

4、在Switcharoo(ref Thing pValue)內部,在托管堆上創建Vegetable對象實例。
5、pValue指向Vegetable實例,也就相當於x指向Vegetable實例。

 

以上,當在引用類型參數之前加上關鍵字ref,再傳遞,是把x本身的棧地址拷貝給pValue,x和pValue指向同一個棧地址。

 

參考資料:
C# Heap(ing) Vs Stack(ing) in .NET: Part II
《你必須知道的.NET(第2版)》,作者王濤。

 

".NET的堆和棧"系列包括:

.NET的堆和棧01,基本概念、值類型內存分配

.NET的堆和棧02,值類型和引用類型參數傳遞以及內存分配

.NET的堆和棧03,引用類型對象拷貝以及內存分配


NET的方法以什形式進行存儲,如值類型存棧,引用類型存堆,那方法存在什地方?

進程的內存空間分好幾個段的,.net方法都是放在 代碼段 的,這個段裡的內存值是不能改變的。
“值類型存棧,引用類型存堆”這些都在數據段
 

16 在NET中,一個頁面對象是( )類的實例

System.Web.UI.Page
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved