淺談c#的三個高級參數ref out 和Params

来源:https://www.cnblogs.com/yoga21/archive/2018/04/06/8729377.html
-Advertisement-
Play Games

c#的三個高級參數ref out 和Params 前言:在我們學習c#基礎的時候,我們會學習到c#的三個高級的參數,分別是out .ref 和Params,在這裡我們來分別的講解一下,在這裡的我們先不做具體的解釋,我會通過幾個例子來做分別的解釋。 一:out參數 1.首先我先給大家一個題:我們來寫一 ...


c#的三個高級參數ref out 和Params

前言:在我們學習c#基礎的時候,我們會學習到c#的三個高級的參數,分別是out .ref 和Params,在這裡我們來分別的講解一下,在這裡的我們先不做具體的解釋,我會通過幾個例子來做分別的解釋。

一:out參數

 1.首先我先給大家一個題:我們來寫一個方法,來求一個數組中的最大值,最小值,總和,平均值。代碼如下:

 int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 

 通過分析我們會發現如果我們寫一個方法的時候,當我們需要返回值的時候,我們只能返回一個值,這個時候我們需要求最大值,最小值,總和和平均值。我們該如何的寫呢??假如我們沒有學習過out參數,這時候我們可以考慮返回一個數組,並且分別的假定數組中的元素的最大值,最小值,總和。(當總和求出來之後,平均值也就出來了)。詳細的代如下:

複製代碼
            //我們聲明一個長度為4的數組,假設 res[0] 最大值  res[1]最小值  res[2]總和  res[3]平均值
            int[] res = new int[4];
            //假定數組中的第一個元素為最大值
            res[0] = nums[0];//max
            //假定數組中的第二個元素為最小值
            res[1] = nums[0];//min
            //假定數組中的第三個元素為總和
            res[2] = 0;//sum 
複製代碼

註:num[0]:這隻是我們假定的值。

2.這時候我們需要遍歷for迴圈,如果當前迴圈到的元素比我的最大值還要大,就把當前的元素賦值給我的最大值,如果當前迴圈到的元素比我的最小值還要小,就把當前的元素賦值給我的最小值,總和就是所有的元素相加,平均值為總和/數組的長度。詳細的代碼如下:

複製代碼
            for (int i = 0; i < nums.Length; i++)
            {
                //如果當前迴圈到的元素比我假定的最大值還大 
                if (nums[i] > res[0])
                {
                    //將當前迴圈到的元素賦值給我的最大值
                    res[0] = nums[i];
                }
                //如果當前迴圈到的元素比我的最小值還要小
                if (nums[i] < res[1])
                {
                    //就把當前的元素賦值給我的最小值
                    res[1] = nums[i];
                }
                //總和
                res[2] += nums[i];
            }
複製代碼

3.這時候直接返回數組就行了,完整的代碼如下:

複製代碼
 1         /// <summary>
 2         /// 計算一個數組的最大值、最小值、總和、平均值
 3       /// </summary>
 4         /// <param name="nums"></param>
 5         /// <returns></returns>
 6         public static int[] GetMaxMinSumAvg(int[] nums)
 7         {
 8             //我們聲明一個長度為4的數組,假設 res[0] 最大值  res[1]最小值  res[2]總和  res[3]平均值
 9             int[] res = new int[4];
10             //假定數組中的第一個元素為最大值
11             res[0] = nums[0];//max
12             //假定數組中的第二個元素為最小值
13             res[1] = nums[0];//min
14             //假定數組中的第三個元素為總和
15             res[2] = 0;//sum
16             for (int i = 0; i < nums.Length; i++)
17             {
18                 //如果當前迴圈到的元素比我假定的最大值還大 
19                 if (nums[i] > res[0])
20                 {
21                     //將當前迴圈到的元素賦值給我的最大值
22                     res[0] = nums[i];
23                 }
24                 //如果當前迴圈到的元素比我的最小值還要小
25                 if (nums[i] < res[1])
26                 {
27                     //就把當前的元素賦值給我的最小值
28                     res[1] = nums[i];
29                 }
30                 //總和
31                 res[2] += nums[i];
32             }
33             //平均值
34             res[3] = res[2] / nums.Length;
35             return res;
36         }
複製代碼

4.接下來就是在Main方法的中調用,截圖如下:

5.這時候我們需要考慮一個問題,我想要在數組中返回bool類型的值或者返回string類型的值,這時候我們怎麼辦呢??這時候需要我們的out參數登場了,out參數就側重於在一個方法中返回多個不同類型的值,但是它有一個重要的特點是:必須在方法的內部為其賦值。

(1).在我們需要返回的值的前面加out關鍵字。

(2).當我們在Main方法中調用我們的方法的時候,首先需要先聲明變數。

解題的思路跟上面的一樣這裡就不做過多的解釋了,直接完整的代碼奉上:

 

複製代碼
        /// <summary>
        /// 計算一個整數數組的最大值、最小值、平均值、總和
      /// </summary>
        /// <param name="nums">要求值得數組</param>
        /// <param name="max">多餘返回的最大值</param>
        /// <param name="min">多餘返回的最小值</param>
        /// <param name="sum">多餘返回的總和</param>
        /// <param name="avg">多餘返回的平均值</param>
        public static void Test(int[] nums, out int max, out int min, out int sum, out int avg)
        {
            //out參數要求在方法的內部必須為其賦值   
            //我們假設數組中的第一個元素為最大值
            max = nums[0];
            //假設數組中的第一個元素為最小值
            min = nums[0];
            //總和設置為零
            sum = 0;
            for (int i = 0; i < nums.Length; i++)
            {
                if (nums[i] > max)
                {
                    max = nums[i];
                }
                if (nums[i] < min)
                {
                    min = nums[i];
                }
                sum += nums[i];
            }
            avg = sum / nums.Length;
        }
複製代碼

 

 

 

複製代碼
 1         static void Main(string[] args)
 2         {
 3             //寫一個方法 求一個數組中的最大值、最小值、總和、平均值
 4             int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 5             //變數的聲明
 6             int max1;
 7             int min1;
 8             int sum1;
 9             int avg1;
10             //方法的調用
11             Test(numbers, out max1, out min1, out sum1, out avg1);
12             Console.WriteLine(max1);
13             Console.WriteLine(min1);
14             Console.WriteLine(sum1);
15             Console.WriteLine(avg1);
16             Console.ReadKey();
17 
18         }
複製代碼

調試的結果如下:

6.現在我麽用out參數做一個練習:分別的提示用戶輸入用戶名和密碼,寫一個方法來判斷用戶輸入的是否正確,返回給用戶一個登錄的結果,並且還要單獨的返回給用戶一個登錄的信息,如果用戶名錯誤,除了返回登錄結果之外,還要返回一個“用戶名錯誤”,如果密碼錯誤,除了返回登錄結果之外,還要返回一個“密碼錯誤”。

分析的思路:通過上面的題我們分析我們不僅要返回一個登錄結果,還要多餘的返回一個登錄的信息(是登錄名錯誤還是密碼錯誤還是未知的錯誤),這時候我們需要考慮使用out參數了。完整的代碼如下:

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _08使用out參數做登陸
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("請輸入用戶名");
            string userName = Console.ReadLine();
            Console.WriteLine("請輸入密碼");
            string userPwd = Console.ReadLine();
            string msg;
            bool b = IsLogin(userName, userPwd, out msg);
            Console.WriteLine("登陸結果{0}",b);
            Console.WriteLine("登陸信息{0}",msg);
            Console.ReadKey();
        }

        /// <summary>
        /// 判斷登陸是否成功
        /// </summary>
        /// <param name="name">用戶名</param>
        /// <param name="pwd">密碼</param>
        /// <param name="msg">多餘返回的登陸信息</param>
        /// <returns>返回登陸結果</returns>
        public static bool IsLogin(string name, string pwd, out string msg)
        {
            if (name == "admin" && pwd == "888888")
            {
                msg = "登陸成功";
                return true;
            }
            else if (name == "admin")
            {
                msg = "密碼錯誤";
                return false;
            }
            else if (pwd == "888888")
            {
                msg = "用戶名錯誤";
                return false;
            }
            else
            {
                msg = "未知錯誤";
                return false;
            }
        }
    }
}
複製代碼

結果如下截圖:

 

 二:那麼接下來說一下ref參數的使用。

那麼老規矩我們首先來看一下這道題:使用方法來交換兩個int類型的變數。

思路分析:首先這個一道非常經典的面試題,我們可以使用兩種方法來做這道題(這裡不詳細的解釋)。

首先聲明一個方法來實現兩個int類型變數的交換。代碼如下:

複製代碼
 1         /// <summary>
 2         /// 交換兩個int類型的變數
 3       /// </summary>
 4         /// <param name="n1">第一個參數</param>
 5         /// <param name="n2">第二個參數</param>
 6         public static void Test(int n1, int n2)
 7         {
 8             n1 = n1 - n2;//-10 20
 9             n2 = n1 + n2;//-10 10
10             n1 = n2 - n1;//20 10
11         }
複製代碼

在這個時候考慮我們在Main方法中調用就能實現結果嗎??答案是不行的。這就設計到我們的值類型和引用類型(在後面做詳細的解釋)。這個時候我們就要用到我們的ref參數了。這個時候我們給ref參數做一個定義:ref參數可以將一個變數帶入到一個方法中進行改變,改變完成後,在將改變完成後的值帶出方法。特點:ref參數要求在方法的外部必須為其賦值,在方法的內部可以不賦值。

完整的代碼如下:

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _11_ref練習
{
    class Program
    {
        static void Main(string[] args)
        {
            //使用方法來交換兩個int類型的變數
          //必須在方法的外部為其賦值
          int n1 = 10;
            int n2 = 20;
            Test(ref n1, ref n2);
            Console.WriteLine(n1);
            Console.WriteLine(n2);
            Console.ReadKey();
        }

        /// <summary>
        /// 交換兩個int類型的變數
        /// </summary>
        /// <param name="n1">第一個參數</param>
        /// <param name="n2">第二個參數</param>
        public static void Test(ref int n1, ref int n2)
        {
            //方法的內部可以不賦值
            n1 = n1 - n2;//-10 20
            n2 = n1 + n2;//-10 10
            n1 = n2 - n1;//20 10
        }
    }
}
複製代碼

代碼的結果截圖如下:

 三:下麵我們來介紹一下Params可變參數。

這次我們的題目是:求任意長度的數字的和(整數類型的)。

我們直接給出定義:將實參列表跟可變參數類型一致的元素都當作數組中的元素來處理。特點:Params可變參數必須是形參列表中的最後一個元素。

直接看代碼:

 

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _12params可變參數
{
    class Program
    {
        static void Main(string[] args)
        {
            int sum = GetSum(8,9);
            Console.WriteLine(sum);
            Console.ReadKey();
        }

        /// <summary>
        /// 求數組中的和
        /// </summary>
        /// <param name="n">數組</param>
        /// <returns>返回總和</returns>
        public static int GetSum(params int[] n)
        {
            int sum = 0;
            for (int i = 0; i < n.Length; i++)
            {
                sum += n[i];
            }
            return sum;
        }
    }
}
複製代碼

大家看見了嗎,這時候我們將8 ,9 就當作我們形參列表數組中的元素來進行處理,這樣的話我們就是實現了我們的功能了。

 四:這個時候我們該討論一下out參數和ref參數為什麼可以實現這樣的功能。比如拿ref參數做例子:

 在討論這個問題之前我們該說一下值類型和引用類型的區別了。

(1):值類型和引用類型在記憶體中存儲的地方不一樣。

(2):在傳遞值類型和傳遞引用類型的時候,傳遞的方式不一樣,值類型我們稱之為值傳遞,引用類型我們稱之為引用傳遞。

值類型    :int double bool char decimal struct enum

引用類型:string 自定義類 數組 object 集合 介面

在舉例子之前首先請允許我說兩個重要的概念:

(1):值類型在複製的時候,傳遞的是這個值的本身。

(2):引用類型在複製的時候,傳遞的是對這個對象的引用。

首先我們先來句第一個例子,代碼如下:

複製代碼
 //值傳遞和引用傳遞
int n1 = 10;
int n2 = n1;
n2 = 20;
Console.WriteLine(n1);
Console.WriteLine(n2);
Console.ReadKey();
複製代碼

大家猜一下結果是多少??答案是:10 20,因為int是值類型,當我聲明int n1=10,就相當於我們在記憶體的棧中開闢了一塊記憶體地址,那我們如何在我們的記憶體中找到我們的值呢(也就是我們給這塊記憶體區域取一個名字,也就是我們的變數名),ok,當執行int n2=n1的時候,也就是進行了一個賦值的操作,如上所說,值類型在傳遞的時候我們傳遞的是值的本身。所以當我們執行完第二行的代碼的時候,n2=10,當執行完第三行的代碼的時候,又重新給n2賦值n2=20,所有輸出的結果就是:n1=10,n2=20。 畫圖表示如下:

接下來我們看第二個例子(Person預設是自定義類):

1 Person p1 = new Person();
2 p1.Name = "張三";
3 Person p2 = p1;
4 p2.Name = "李四";
5 Console.WriteLine(p1.Name);
6 Console.ReadKey();

這個時候p1.Name為多少呢?? 答案是:李四。首先我們肯定是Person是自定義類,因為我們的自定義類為引用類型,所以在進行引用傳遞的時候傳遞的是對這個對象的引用(記憶體地址)。當我們聲明第一行的代碼的時候,會在記憶體的堆中開闢一塊區域存儲new Person();當為其Name賦值的時候,張三也存儲在堆中,這時候也會在棧中開闢一塊空間存儲了指向這塊區域的地址。所以當執行Person p2=p1,是把P1在棧中的引用複製了一份給了P2,所以現在他們兩指向的是堆中同一塊區域。所以當給Name重新賦值的時候,p1的值也會改變。截圖如下:

畫圖表示如下:

 

 第三個例子:,代碼如下:

1 string s1 = "張三";
2 string s2 = s1;
3 s2 = "李四";
4 Console.WriteLine(s1);
5 Console.WriteLine(s2);
6 Console.ReadKey();

這個時候我們的s1和s2是多少呢??經過我的測試答案是:s1:張三,s2:李四。為什麼呢?要是按照我們上面的過程應該都是李四呀?因為我們的字元串是非常特殊的,正是由於字元串的不可變性,沒每當我們在為其賦值的時候都會在堆中重新開闢一塊區域進行存儲。所以兩塊區域沒有任何的關係。

思考的問題:這時候我們回顧在一個方法中實現交換變數的例子,當我們沒有用ref的時候,傳遞的是值的本身,當我們使用ref參數的時候傳遞的是這個對象的引用(記憶體地址)。所以我們總結出來ref的作用是:ref參數可以將一個值傳遞改變為引用傳遞。

好的,我們c#中的三個高級參數在這裡就介紹完了,如果大家有什麼疑問的話,可以留言,大家共同進步。

原文地址:http://www.cnblogs.com/MoRanQianXiao/p/7702587.html。 


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

-Advertisement-
Play Games
更多相關文章
  • 1、兩種種註釋符: //單行註釋; /* */多行註釋 2、幾種常見的變數: int (整數) double(浮點數)decimal(常用於金錢這個精密計算) string(字元串 )char(單個字元) 變數的命名:以字母或者下劃線開始,但命名要有意義,便於理解;Pascal規範用於類或方法的命名 ...
  • UWP Community Toolkit 中有一個為圖片或磁貼提供輪播效果的控制項 - RotatorTile,本篇我們結合代碼詳細講解 RotatorTile 的實現。 RotatorTile 提供了一種類似 Windows 10 磁貼的輪播方式,可以輪流播放開發者設置的內容序列,支持設置輪... ...
  • 最近工作業務要是用伺服器證書驗證,在這裡記錄下一. 1.添加伺服器角色 [證書服務] 2.一路下一步直到證書服務安裝完成; 3.選擇圈選中的伺服器證書 4.點擊[創建證書申請] 5.填寫信息 6.下一步,直到保存文件位置 7.搜索證書頒發機構,打開 8.右鍵樹型,選擇[提交一個新的申請] 9.選擇之 ...
  • 主題 賬戶管理一個比較常見的功能就是密碼強度策略,Identity已經內置了一個通用的可配置的策略,我們一般情況下可以直接拿來用即可。本篇我會介紹一些Identity內置的密碼策略類:PasswordValidator,並且簡單介紹一下源碼。最好我們還會自定義一個密碼策略類的實現。https://w ...
  • sing System;using System.Collections.Generic;using System.Linq;using System.Text; namespace ConsoleApplication1{ class Program { static void Main(stri ...
  • C#情懷與未來,怨天尤人還是抓住機會,能否跟上dnc新時代浪潮? 經常看到有.NET圈子在討論是否應該轉其它語言 C#情懷是一方面,如果覺得C#未來沒前途,光靠情懷是撐不住的, 建議對C#未來沒信心的朋友,轉go、rust、py、TS、JS、java …… 常常看到有人抱怨.NET,抱怨好幾年了,卻 ...
  • using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplicationl { class Program { static void Main ...
  • 本文介紹了 C# 如何使用 GDI+ 為圖片添加文字,並且可以使文字字體大小自適應。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...