C#值參數和引用參數

来源:http://www.cnblogs.com/liuyoung/archive/2017/11/11/7819052.html
-Advertisement-
Play Games

一、值參數 未用ref或out修飾符聲明的參數為值參數。 使用值參數,通過將實參的值複製到形參的方式,把數據傳遞到方法。方法被調用時,系統做如下操作。 在棧中為形參分配空間。 複製實參到形參。 值參數的實參不一定是變數。它可以是任何能計算成相應數據類型的表達式。 看一個例子: 下麵來調用方法 在把變 ...


一、值參數

未用ref或out修飾符聲明的參數為值參數。

使用值參數,通過將實參的值複製到形參的方式,把數據傳遞到方法。方法被調用時,系統做如下操作。

  • 在棧中為形參分配空間。
  • 複製實參到形參。

值參數的實參不一定是變數。它可以是任何能計算成相應數據類型的表達式。

看一個例子:

float func1(float val)    //聲明方法
{
  float j=2.6F;
  float k=5.1F;
  ....
}

下麵來調用方法

float fValue1=func1(k);        //實參是float類型的變數

float fValue2=func1((k+j)/3);  //實參可以計算成float表達式

在把變數作用於實參之前,變數必須賦值(除非是out參數)。對於引用類型,變數可以被設置為一個實際的引用或null。

下麵的代碼展示了一個名為MyMethod的方法,它有兩個參數,一個是MyClass型變數和一個int。

 1  class MyClass
 2     {
 3         public int Val = 20;
 4     }
 5     class Program
 6     {
 7 
 8         static void MyMethod(MyClass f1, int f2)
 9         {
10             f1.Val = f1.Val + 5;
11             f2 = f2 + 5;
12             Console.WriteLine("f1.Val: {0}, f2: {1}", f1.Val, f2);
13         }
14         static void Main(string[] args)
15         {
16             MyClass a1 = new MyClass();
17             int a2 = 10;
18 
19             MyMethod(a1, a2);
20 
21             Console.WriteLine("f1.Val: {0}, f2: {1}", a1.Val, a2);
22          }
23     }

我們用圖來表示實參和形參在方法執行的不同階段的值。

  • 在方法被調用前,用作實參的a2已經在棧里了。
  • 在方法開始前,系統在棧中為形參分配空間,並從實參複製值。 
  • 因為a1是引用類型,所以引用被覆制,結果實參和形參都引用堆中的同一對象。
  • 因為a2是值類型,所以值被覆制,產生了一個獨立的數據項。
  • 在方法的結尾,f2和對象f1的欄位都被加上了5。
  • 方法執行後,形參從棧中彈出。
  • a2,值類型,它的值不受方法行為的影響。
  • a1,引用類型,但它的值被方法的行為改變了。

 

二、引用參數

使用引用參數時,必須在方法的申明和調用中都使用關鍵字ref修飾符。

實參必須是變數,在用作實參前必須被賦值。如果是引用類型的變數,可以賦值為一個引用或者null值。

下麵的代碼闡明瞭引用參數的聲明和調用的語法:

  void MyMethod(ref int val)  //方法聲明包含ref修飾符
  {
     //your code
   }

 

  int y = 1;
  MyMethod(ref y);   //方法調用

  MyMethod(ref 3+5);  //錯誤,形參必須是變數

在第一小節的內容中我們知道,對於值參數,系統在棧上為形參分配記憶體,相反對於引用參數:

  • 不會為形參在棧上分配記憶體。
  • 實際情況是,形參的參數名將作為實參變數的別名,指向相同的記憶體位置

由於形參名和實參名的行為,就好象指向相同的記憶體位置,所以在方法的執行過程中,對形參作的任何改變,在方法完成後依然有效(表現在實參變數上)。

在方法的聲明和調用上都使用關鍵字ref.

下麵的代碼再次展示了方法MyMethod,但這一次參數是引用參數而不是值參數。

 1  class MyClass
 2     {
 3         public int Val = 20;
 4     }
 5     class Program
 6     {
 7 
 8         static void MyMethod(ref  MyClass f1,ref int f2)
 9         {
10             f1.Val = f1.Val + 5;
11             f2 = f2 + 5;
12             Console.WriteLine("f1.Val: {0}, f2: {1}", f1.Val, f2);
13         }
14         static void Main(string[] args)
15         {
16             MyClass a1 = new MyClass();
17             int a2 = 10;
18 
19             MyMethod(ref a1, ref a2);
20 
21             Console.WriteLine("f1.Val: {0}, f2: {1}", a1.Val, a2);
22 
23         }
24     }

同樣,還是用圖來闡明方法執行的不同階段實參和形參的值。

  • 在方法被調用前,用作實參的a1,a2已經在棧里了。
  • 在方法的開始,形參名被設置為實參的別名。變數a1和f1引用相同的記憶體位置,a2和f2引用相同的記憶體位置。
  • 在方法的結束位置,f2和對象f1的欄位都被加上了5。
  • 方法執行之後,形參的名稱已經失效,但是值類型a2和引用類型a1所指向的對象的值都被方法內的行為改變了。

三、引用類型作為值參數和引用參數

 對於一個引用類型對象,不管是將其作為值參數傳遞還是作為引用參數傳遞,我們都可以在方法成員內部修改它的成員。不過,我們並沒有在方法內部設置形參本身。

 下麵我們就來看看在方法內部設置形參本身時會發生什麼。

1、將引用類型對象作為值參數傳遞

 1  class MyClass
 2     {
 3         public int Val = 20;
 4     }
 5     class Program
 6     {
 7 
 8         static void RefAsParameter(MyClass f1)
 9         {
10             f1.Val = 50;
11             Console.WriteLine("After member assignment:   {0}", f1.Val);
12             f1 = new MyClass();
13             Console.WriteLine("After new object creation: {0}", f1.Val);
14         }
15         static void Main(string[] args)
16         {
17 
18             MyClass a1 = new MyClass();
19             Console.WriteLine("Before method  call:       {0}", a1.Val);
20             RefAsParameter(a1);
21             Console.WriteLine("After method  call:        {0}", a1.Val);
22         }
23     }

這段代碼的輸出如下:

Before method  call:       20
After member assignment:   50
After new object creation: 20
After method  call:        50

同樣,還是用圖來闡明以下幾點。

  • 在方法開始時,實參和形參都指向堆中相同的對象。
  • 在為對象的成員賦值之後,他們仍指向堆中相同的對象。
  • 當方法分配新的對象並賦值給形參時,方法外部的實參仍指向原始對象,而形參指向的是新對象。
  • 在方法調用之後,實參指向原始對象,形參和新對象都會消失。

2、將引用類型對象作為引用參數傳遞

除了在方法聲明和方法調用時使用ref關鍵字之外,與上面的代碼完全一樣。

 1   class MyClass
 2     {
 3         public int Val = 20;
 4     }
 5     class Program
 6     {
 7 
 8         static void RefAsParameter(ref MyClass f1)
 9         {
10             f1.Val = 50;
11             Console.WriteLine("After member assignment:   {0}", f1.Val);
12             f1 = new MyClass();
13             Console.WriteLine("After new object creation: {0}", f1.Val);
14         }
15         static void Main(string[] args)
16         {
17 
18             MyClass a1 = new MyClass();
19             Console.WriteLine("Before method  call:       {0}", a1.Val);
20             RefAsParameter(ref a1);
21             Console.WriteLine("After method  call:        {0}", a1.Val);
22         }
23     }

這段代碼的輸出如下:

Before method  call:       20
After member assignment:   50
After new object creation: 20
After method  call:        20

我們開始說過,引用參數的行為就是將實參作為形參的別名。

  • 在方法開始時,實參和形參都指向堆中相同的對象。
  • 在為對象的成員賦值之後,他們仍指向堆中相同的對象。
  • 當方法分配新的對象並賦值給形參時,形參和實參都指向新對象。
  • 在方法調用之後,實參指向方法內創建的新對象

四、寫在最後

這些都是老生常談的問題,為什麼還要寫?

一是因為今天看書看到了與此相關的內容,回去翻了翻書,然後記錄下來

二是供自己以後查閱,畢竟看博客比翻書來的快。

最後,祝大家周末愉快,玩的開心。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原文:https://blog.markvincze.com/troubleshooting-high-memory-usage-with-asp-net-core-on-kubernetes/ ps:我不是死板翻譯原文的,儘量的通俗一點,如有不對歡迎指出,謝謝哈。 在生產環境中,我們把asp.ne ...
  • 前言:有了NuGet引用什麼的管理起來方便多了,特別是團隊合作的時候,但是在使用過程中發現從遠程還原包的時候真的是等到花都謝了,沒辦法,只好自己搞個NuGet伺服器。 我是分割線 廢話少說,開始正題: 一、部署NugetServer 1、新建一個空的web項目(這裡.NET Framework版本選 ...
  • using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System. ...
  • 一般在介紹一樣新技術之前,我們都要大致講講它的歷史、主要原理等等。當然,這些東西很枯燥,很容易誘發我們的瞌睡蟲。但是不說,又不能讓人理解。好在不是太多。 如果您已經瞭解重構的定義、原理以及如何重構,那麼請跳過本小節。好了,書歸正傳。 返回總目錄 一、何謂重構(What) 視上下文的不同,重構有兩種定 ...
  • 一、入門簡介 在學習之前,要先瞭解ASP.NET Core是什麼?為什麼?很多人學習新技術功利心很重,恨不得立馬就學會了。 其實,那樣做很不好,馬馬虎虎,聯繫過程中又花費非常多的時間去解決所遇到的“問題”,是簡單的問題,對,就是簡單,就是因為覺得簡單被忽略的東西,恰恰這才是最重要的。 1、學習資料 ...
  • 【重構:改善既有代碼的設計】讀書筆記總目錄 1、重構原則 2、代碼的壞味道【1】 3、代碼的壞味道【2】 4、代碼的壞味道【3】 5、代碼的壞味道【4】 6、重構手法之Extrct Method(提煉函數)、Inline Method(內聯函數)、Inline Temp(內聯臨時變數) 7、重構手法 ...
  • 1.阿裡雲伺服器控制台,開啟1433埠(出入方向都要開)。自從微軟發佈linux版本後,控制台常用埠下拉列表也增加了1433. 2.如果你沒配置阿裡雲yum源,可參照配置一下。http://www.cnblogs.com/tdws/p/7183040.html 3.你必須 RHEL 7.3 或 ...
  • 之前寫了一個功能性的文件上傳asp.net core的小程式,加上點七七八八的東西,勉強能夠應付了,打算學習一下微軟的官方.NET CORE微服務示例 "https://github.com/dotnet architecture/eShopOnContainers" 。這個例子很全面地展現了微服務 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...