C#中同一函式返回多個值的解決方法總結
一、前言
最近在做專案的過程中,涉及到一個函式要求返回多個值的問題,經過一番折騰,問題得到了解決。但我覺得這個問題具有代表性,以後還會遇到這樣的問題,本著學習的目的,通過查閱一些資料,來對函式返回多個值得解決方法進行一下總結,以期能有所收穫,並與各位園友探討學習。
二、函式返回多個值方法(一)使用out返回多個值
在介紹out使函式返回多個值之前,我們首先來對C#中的ref關鍵字和out關鍵字進行一下介紹
我們在呼叫函式時,通常都是向函式中傳遞值,函式獲得的時我們傳遞的這些值的拷貝,函式執行時,使用的的是這些拷貝,函式執行結束,這些拷貝也就自然消失,而原來的值不會受到影響。當然這是通常情況,另一種情況是我們向函式傳遞引數
有時候,我們需要改變原來變數的值,這時候我們就需要向函式傳遞變數引用,引用是一個變數並可以訪問原變數的值,修改引用就修改了原來變數的值。這裡需要對這一原理進行一下解釋,這個原理其實很簡單,即為:變數的值儲存在計算機記憶體中,可以建立一個引用指向計算機中的變數在記憶體中的位置,所以當引用被修改時,其實修改的是記憶體中的值,因此變數的值也就被修改了,但我們呼叫一個含有引用引數的函式時,函式中的引數將指向傳遞給函式的變數,從而導致修改引數變數的值的同時原來變數的值也就被修改。這樣一解釋,是不是現在就更明白了。話不多說,下面,我們將通過例項的方式,來演示ref關鍵字的用法。
使用ref進行引數引用傳遞的例項程式碼如下:
namespace ShareResourceContract { public class MultipleOutPut { public double MathCalculation(ref double x) { x = x * x; return x; } } } static void Main(string[] args) { MultipleOutPut output = new MultipleOutPut();double a = 5; //使用ref傳遞變數引用必須在函式體外初始化,不然編譯不會通過 double b = 3; Console.WriteLine("Before:a={0},b={1}", a, b); output.MathCalculation(ref a); Console.WriteLine("After:a={0},b={1}", a, b); Console.ReadLine(); }
輸出結果如下:
通過以上例項,我們可以看到,原來變數a的值已經被修改了。
通過以上的介紹,相信大家對變數引用傳遞引數已經有了一個更深層次的理解。接下來我們繼續介紹,有時候,我們希望一個函式返回多個值,雖然使用ref也可以實現,但C#專門提供了一個專門out關鍵字來處理函式返回多值得問題,以下我們同樣用例項的方式來對其展開介紹。
使用out關鍵字返回多個值得例項程式碼如下:
namespace ShareResourceContract { public class MultipleOutPut { public double MathCalculation(ref double x) { x = x * x; return x; } public void MulOutPut(double x,out double add,out double subtract,out double squar) { add = x + x; subtract = x - 5; squar = x * x; } } } static void Main(string[] args) { MultipleOutPut output = new MultipleOutPut(); double x = 8; double add = 0; double subtract = 0; double squar = 0; Console.WriteLine("Before:x={0}", x); Console.WriteLine("Before:add={0}", add); Console.WriteLine("Before:subtract={0}", subtract); Console.WriteLine("Before:squar={0}", squar); output.MulOutPut(x, out add, out subtract, out squar); Console.WriteLine("After:x={0}", x); Console.WriteLine("After:add={0}", add); Console.WriteLine("After:subtract={0}", subtract); Console.WriteLine("After:squar={0}", squar); Console.ReadLine(); }
輸出結構如下:
通過以上例項可以看出,使用out關鍵字,使函式返回了多個返回值,
下面我們來對ref和out的區別進行以下說明:
1、用ref引用的變數在呼叫函式之前必須初始化,相當於在函式外部例項化,函式裡面只是對這個物件進行修改的過程;
2、而out指定的引數可以不必在函式呼叫之前初始化,不管有沒有在函式外部被初始化,out指定的引數將在函式內部被清空,並在函式內部進行初始化。
3、二者的區別可以簡單理解為:“ref有進有出,而out只出不進”。
三、函式返回多個值方法(二)使用Tuple返回多個值
通過以上介紹,相信大家已經對C#使用關鍵字ref和out使函式返回多個值有了一個深層次的理解,其實C#中除了使用ref和out關鍵字使函式返回多個值之外,還有一種方法也能實現同樣的目的,那就是使用Tuple(元組)返回多個值。話不多說,接下來,我們將就使用Tuple使函式返回多個值展開詳細介紹。
首先,我們先來回顧一下Tuple,Tuple是C# 4.0時出的新特性,.Net Framework 4.0以上版本可用。元組(Tuple)是一種資料結構,具有特定數量和元素序列。建立對應的元組可以表示一組資料,例如建立具有StudentCode、Name、Age、Sex四元組資料來儲存學生基本資訊。
1、建立元組的方法
(1)利用建構函式建立元組(注意:預設情況.Net Framework元組僅支援1到7個元組元素,如果有8個元素或者更多,需要使用Tuple的巢狀和Rest屬性去實現)
var tupleTest1 = new Tuple<int, int, int, int, int, int,int>(1, 2, 3, 4, 5, 6,7); Console.WriteLine($"Item 1: {tupleTest1.Item1}, Item 7: {tupleTest1.Item7}"); var tupleTest2= new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>>(1, 2, 3, 4, 5, 6, 7, new Tuple<int, int, int>(8, 9, 10)); Console.WriteLine($"Item 1: {tupleTest2.Item1}, Item 10: {tupleTest2.Rest.Item3}");
(2)利用Tuple靜態方法構建元組(最多支援八個元素)
var tupleTest13=Tuple.Create<int, int, int, int, int, int,int>(1, 2, 3, 4, 5, 6,7); Console.WriteLine($"Item 1: {tupleTest1.Item1}, Item 7: {tupleTest1.Item7}"); var tupleTest4=Tuple.Create<int, int, int, int, int, int, int, int>(1, 2, 3, 4, 5, 6, 7, 8); Console.WriteLine($"Item 1: {tupleTest2.Item1}, Item 8: {tupleTest2.Rest.Item1}");
2、代替out從函式中返回多個值
通常,我們需要從函式中返回多個值時,使用out關鍵字輸出引數。學習了Tuple之後,就可以使用Tuple代替out實現從函式中返回多個值了。例如:
using System; namespace ShareResourceContract { public class MultipleOutPut { public Tuple<double ,string> MulOutPutByTuple(double x) { double addResult = 0; var resultMessage = ""; addResult = x + 8; resultMessage = "變數加了8之後的和為"+ addResult; return new Tuple<double, string>(addResult, resultMessage); } } }
static void Main(string[] args) { MultipleOutPut output = new MultipleOutPut(); double x = 5; var tupList=output.MulOutPutByTuple(x); Console.WriteLine("函式返回值:addResult={0},resultMessage={1}", tupList.Item1,tupList.Item2); Console.ReadLine(); }
輸出結果如下:
從上面輸出結果我們可以看到,函式返回了兩數相加之後的結果以及結果提示兩個資訊。元組(Tuple)除了以上所述用途外,還有一個用途作者認為也非常有用,即用於單引數方法的多值傳遞(當函式引數僅是一個Object型別時,可以使用元組實現傳遞多個引數值),下面給出具體例項:
static void WriteStudentInfo(Object student) { var studentInfo = student as Tuple<string,string, int, string>; Console.WriteLine($"Student Information: StudentCode[{studentInfo.Item1}], Name [{studentInfo.Item2}], Age[{studentInfo.Item3}],Sex[{studentInfo.Item4}]"); } static void RunTest() { var t = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(WriteStudentInfo)); t.Start(new Tuple<string,string, int, string>("20200068","lilei", 20, "男")); while (t.IsAlive) { System.Threading.Thread.Sleep(50); } }
Tuple元組的不足之處:
- 問元素的時候只能通過ItemX去訪問,使用前需要明確元素順序,屬性名字沒有實際意義,不方便記憶;
- 最多有八個元素,要想更多隻能通過最後一個元素進行巢狀擴充套件;
- Tuple是一個引用型別,不像其它的簡單型別一樣是值型別,它在堆上分配空間,在CPU密集操作時可能有太多的建立和分配工作。
鑑於以上的不足,C# 7.0中引入了一個新的ValueTuple(值元組)型別,園友北田在這篇(https://www.cnblogs.com/unity3ds/p/11641582.html)博文中對ValueTuple(值元組)進行了詳細的介紹,大家可以參考。