1. 程式人生 > 實用技巧 >C#中同一函式返回多個值的解決方法總結

C#中同一函式返回多個值的解決方法總結

一、前言

  最近在做專案的過程中,涉及到一個函式要求返回多個值的問題,經過一番折騰,問題得到了解決。但我覺得這個問題具有代表性,以後還會遇到這樣的問題,本著學習的目的,通過查閱一些資料,來對函式返回多個值得解決方法進行一下總結,以期能有所收穫,並與各位園友探討學習。

二、函式返回多個值方法(一)使用out返回多個值

  在介紹out使函式返回多個值之前,我們首先來對C#中的ref關鍵字和out關鍵字進行一下介紹

  我們在呼叫函式時,通常都是向函式中傳遞值,函式獲得的時我們傳遞的這些值的拷貝函式執行時,使用的的是這些拷貝,函式執行結束,這些拷貝也就自然消失,而原來的值不會受到影響。當然這是通常情況,另一種情況是我們向函式傳遞引數

,也就是使用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(值元組)進行了詳細的介紹,大家可以參考。