官术网_书友最值得收藏!

6.1 定義和使用函數

本節介紹如何將函數添加到應用程序中,以及如何在代碼中使用(調用)它們。首先從基礎知識開始,看看不與調用代碼交換任何數據的簡單函數,然后介紹更高級的函數用法。首先分析一個示例。

試一試:定義和使用基本函數:Ch06Ex01\Program.cs

(1)在C:\BegVCSharp\Chapter06目錄中創建一個新的控制臺應用程序Ch06Ex01。

(2)把下述代碼添加到Program.cs中:

        class Program
        {
          static void Write()
          {
              WriteLine("Text output from function.");
          }
          static void Main(string[] args)
          {
              Write();
              ReadKey();
          }
        }

(3)執行代碼,結果如圖6-1所示。

圖6-1

示例說明

下面的4行代碼定義了函數Write():

          static void Write()
          {
            WriteLine("Text output from function.");
          }

這些代碼把一些文本輸出到控制臺窗口中。但此時這些并不重要,我們更關心定義和使用函數的機制。

函數定義由以下幾部分組成:

● 兩個關鍵字:static和void

● 函數名后跟圓括號,如Write()

● 一個要執行的代碼塊,放在花括號中

注意:一般采用PascalCase形式編寫函數名。

定義Write()函數的代碼非常類似于應用程序中的其他代碼:

        static void Main(string[] args)
        {
          ...
        }

這是因為,到目前為止我們編寫的所有代碼(類型定義除外)都是函數的一部分。函數Main()是控制臺應用程序的入口點函數。當執行C#應用程序時,就會調用它包含的入口點函數,這個函數執行完畢后,應用程序就終止了。所有C#可執行代碼都必須有一個入口點。

Main()函數和Write()函數的唯一區別(除了它們包含的代碼)是函數名Main后面的圓括號中還有一些代碼,這是指定參數的方式,詳見后面的內容。

如上所述,Main()函數和Write()函數都是使用關鍵字static和void定義的。關鍵字static與面向對象的概念相關,本書在后面討論。現在只需要記住,本節的應用程序中所使用的所有函數都必須使用這個關鍵字。

void更容易解釋。這個關鍵字表明函數沒有返回值。本章后面將討論函數有返回值時需要編寫什么代碼。

繼續下去,調用函數的代碼如下所示:

        Write();

鍵入函數名,后跟空括號即可。當程序執行到這行代碼時,就會運行Write()函數中的代碼。

注意:在定義和調用函數時,必須使用圓括號。如果刪除它們,將無法編譯代碼。

6.1.1 返回值

通過函數進行數據交換的最簡單方式是利用返回值。有返回值的函數會最終計算得到這個值,就像在表達式中使用變量時,會計算得到變量包含的值一樣。與變量一樣,返回值也有數據類型。

例如,有一個函數GetString(),其返回值是一個字符串,可以在代碼中使用該函數,如下所示:

        string myString;
        myString = GetString();

還有一個函數GetVal(),它返回一個double值,可在數學表達式中使用它:

        double myVal;
        double multiplier = 5.3;
        myVal = GetVal() * multiplier;

當函數返回一個值時,可以采用以下兩種方式修改函數:

● 在函數聲明中指定返回值的類型,但不使用關鍵字void。

● 使用return關鍵字結束函數的執行,把返回值傳送給調用代碼。

從代碼角度看,對于我們討論的控制臺應用程序函數,其使用返回值的形式如下所示:

        static <returnType> <FunctionName>()
        {
          ...
          return <returnValue>;
        }

這里唯一的限制是<returnValue>必須是<returnType>類型的值,或者可以隱式轉換為該類型。但是,<returnType>可以是任何類型,包括前面介紹的較復雜類型。這段代碼可以很簡單:

        static double GetVal()
        {
          return 3.2;
        }

但是,返回值通常是函數執行的一些處理的結果。上面的結果使用const變量也可以簡單地實現。

當執行到return語句時,程序會立即返回調用代碼。這條語句后面的代碼都不會執行。但這并不意味著return語句只能放在函數體的最后一行。可以在前邊的代碼里使用return,例如放在分支邏輯之后。把return語句放在for循環、if塊或其他結構中會使該結構立即終止,函數也立即終止。例如:

        static double GetVal()
        {
          double checkVal;
          // checkVal assigned a value through some logic (not shown here).
          if (checkVal < 5)
              return 4.7;
          return 3.2;
        }

根據checkVal的值,將返回兩個值中的一個。這里的唯一限制是,必須在函數的閉合花括號 }之前處理return語句。下面的代碼是不合法的:

        static double GetVal()
        {
          double checkVal;
          // checkVal assigned a value through some logic.
          if (checkVal < 5)
            return 4.7;
        }

如果checkVal>= 5,就不會執行到return語句,這是不允許的。所有處理路徑都必須執行到return語句。大多數情況下,編譯器會檢查是否執行到return語句,如果沒有,就給出錯誤“并不是所有的處理路徑都返回一個值”。

執行一行代碼的函數可使用C# 6引入的一個功能:表達式體方法(expression-bodied method)。以下函數模式使用= >(Lambda箭頭)來實現這一功能。

        static <returnType> <FunctionName>() => <myVal1 * myVal2>;

例如, C# 6之前的Multiply()函數如下:

        static double Multiply(double myVal1, double myVal2)
        {
              return myVal1 * myVal2;
        }

現在可以使用= >(Lambda箭頭)編寫它。下述代碼用更簡單和統一的方式表達方法的意圖:

        static double Multiply(double myVal1, double myVal2) => mVal1 * MyVal2;

6.1.2 參數

當函數接受參數時,必須指定以下內容:

● 函數在其定義中指定接受的參數列表,以及這些參數的類型。

● 在每個函數調用中提供匹配的實參列表。

注意:仔細閱讀C#規范會發現形參(parameter)和實參(argument)之間存在一些細微的區別:形參是函數定義的一部分,而實參則由調用代碼傳遞給函數。但是,這兩個術語通常被簡單地稱為參數,似乎沒有人對此感到十分不滿。

示例代碼如下所示,其中可以有任意數量的參數,每個參數都有類型和名稱:

        static <returnType> <FunctionName>(<paramType> <paramName>, ...)
        {
          ...
          return <returnValue>;
        }

參數之間用逗號隔開。每個參數都在函數的代碼中用作一個變量。例如,下面是一個簡單的函數,帶有兩個double參數,并返回它們的乘積:

        static double Product(double param1, double param2) => param1 * param2;

下面看一個較復雜的示例:

試一試:通過函數交換數據(1):Ch06Ex02\Program.cs

(1)在C:\BegVCSharp\Chapter06目錄中創建一個新的控制臺應用程序Ch06Ex02。

(2)把下列代碼添加到Program.cs中:

        class Program
        {
          static int MaxValue(int[] intArray)
          {
              int maxVal = intArray[0];
              for (int i = 1; i < intArray.Length; i++)
              {
                if (intArray[i] > maxVal)
                  maxVal = intArray[i];
              }
              return maxVal;
          }
          static void Main(string[] args)
          {
              int[] myArray = { 1, 8, 3, 6, 2, 5, 9, 3, 0, 2 };
              int maxVal = MaxValue(myArray);
              WriteLine($"The maximum value in myArray is {maxVal}");
              ReadKey();
          }
        }

(3)執行代碼,結果如圖6-2所示。

圖6-2

示例說明

這段代碼包含一個函數,它執行的任務就是本章開頭的示例函數所完成的任務。該函數以一個整數數組作為參數,并返回該數組中的最大值。該函數的定義如下所示:

        static int MaxValue(int[] intArray)
        {
          int maxVal = intArray[0];
          for (int i = 1; i < intArray.Length; i++)
          {
              if (intArray[i] > maxVal)
                maxVal = intArray[i];
          }
          return maxVal;
        }

函數MaxValue()定義了一個參數,即int數組intArray,它還有一個int類型的返回值。最大值的計算是很簡單的。局部整型變量maxVal初始化為數組中的第一個值,然后把這個值與數組中后面的每個元素依次進行比較。如果一個元素的值比maxVal大,就用這個值代替當前的maxVal值。循環結束時,maxVal就包含數組中的最大值,用return語句返回。

Main()中的代碼聲明并初始化一個簡單的整數數組,用于MaxValue()函數:

        int[] myArray = { 1, 8, 3, 6, 2, 5, 9, 3, 0, 2 };

調用MaxValue(),把一個值賦給int變量maxVal:

        int maxVal = MaxValue(myArray);

接著,使用WriteLine()把這個值寫到屏幕上:

        WriteLine($"The maximum value in myArray is {maxVal}");

1.參數匹配

在調用函數時,必須使提供的參數與函數定義中指定的參數完全匹配,這意味著要匹配參數的類型、個數和順序。例如,下面的函數:

        static void MyFunction(string myString, double myDouble)
        {
          ...
        }

不能使用下面的代碼調用:

        MyFunction(2.6, "Hello");

這里試圖把一個double值作為第一個參數傳遞,把string值作為第二個參數傳遞,參數順序與函數聲明中定義的順序不匹配。這段代碼不能編譯,因為參數類型是錯誤的。本章后面的“重載函數”一節將介紹解決這個問題的一個有效技術。

2.參數數組

C#允許為函數指定一個(只能指定一個)特殊參數,這個參數必須是函數定義中的最后一個參數,稱為參數數組。參數數組允許使用個數不定的參數調用函數,可使用params關鍵字定義它們。

參數數組可以簡化代碼,因為在調用代碼中不必傳遞數組,而是傳遞同類型的幾個參數,這些參數會放在可在函數中使用的一個數組中。

定義使用參數數組的函數時,需要使用下列代碼:

        static <returnType> <FunctionName>(<p1Type> <p1Name>, ...,
                                      params <type>[] <name>)
        {
          ...
          return <returnValue>;
        }

使用下面的代碼可以調用該函數:

        <FunctionName>(<p1>, ..., <val1>, <val2>, ...)

其中<val1>和<val2>等都是<type>類型的值,用于初始化<name>數組。可以指定的參數個數幾乎不受限制,但它們都必須是<type>類型。甚至根本不必指定參數。

下面的示例定義并使用帶有params類型參數的函數。

試一試:通過函數交換數據(2):Ch06Ex03\Program.cs

(1)在C:\BegVCSharp\Chapter06目錄中創建一個新的控制臺應用程序Ch06Ex03。

(2)把下述代碼添加到Program.cs中:

        class Program
        {
          static int SumVals(params int[] vals)
          {
              int sum = 0;
              foreach (int val in vals)
              {
                sum += val;
              }
              return sum;
          }
          static void Main(string[] args)
          {
              int sum = SumVals(1, 5, 2, 9, 8);
              WriteLine($"Summed Values = {sum}");
              ReadKey();
          }
        }

(3)執行代碼,結果如圖6-3所示。

圖6-3

示例說明

這個示例用關鍵字params定義函數sumVals(),該函數可以接受任意個int參數(但不接受其他類型的參數):

        static int SumVals(params int[] vals)
        {
          ...
        }

這個函數對vals數組中的值進行迭代,將這些值加在一起,返回其結果。

在Main()中,用5個整型參數調用函數SumVals():

        int sum = SumVals(1, 5, 2, 9, 8);

也可以用0、1、2或100個整型參數調用這個函數—— 參數的數量不受限制。

注意:C# 引入了指定函數參數的新方式,包括用一種可讀性更好的方式來包含可選參數。第13章將介紹這些方法,該章討論C#語言。

3.引用參數和值參數

本章迄今定義的所有函數都帶有值參數。其含義是:在使用參數時,是把一個值傳遞給函數使用的一個變量。在函數中對此變量的任何修改都不影響函數調用中指定的參數。例如,下面的函數使傳遞過來的參數值加倍,并顯示出來:

        static void ShowDouble(int val)
        {
          val *= 2;
          WriteLine($"val doubled = {0}", val);
        }

參數val在這個函數中被加倍,如果按以下方式調用它:

        int myNumber = 5;
        WriteLine($"myNumber = {myNumber}");
        ShowDouble(myNumber);
        WriteLine($"myNumber = {myNumber}", );

輸出到控制臺的文本如下所示:

        myNumber = 5
        val doubled = 10
        myNumber = 5

把myNumber作為一個參數,調用ShowDouble()并不影響Main()中myNumber的值,即使把myNumber賦值給val后將val加倍,myNumber的值也不變。

這很不錯,但如果要改變myNumber的值,就會有問題。可以使用一個為myNumber返回新值的函數:

        static int DoubleNum(int val)
        {
          val *= 2;
          return val;
        }

并使用下面的代碼調用它:

        int myNumber = 5;
        WriteLine($"myNumber = {myNumber}");
        myNumber = DoubleNum(myNumber);
        WriteLine($"myNumber = {myNumber}");

但這段代碼一點也不直觀,且不能改變用作參數的多個變量值(因為函數只有一個返回值)。

此時可以通過“引用”傳遞參數。即函數處理的變量與函數調用中使用的變量相同,而不僅僅是值相同的變量。因此,對這個變量進行的任何改變都會影響用作參數的變量值。為此,只需使用ref關鍵字指定參數:

        static void ShowDouble(ref int val)
        {
          val *= 2;
          WriteLine($"val doubled = {val}");
        }

在函數調用中再次指定它(這是必需的):

        int myNumber = 5;
        WriteLine($"myNumber = {myNumber}", );
        ShowDouble(ref myNumber);
        WriteLine($"myNumber = {myNumber}");

輸出到控制臺的文本如下所示:

        myNumber = 5
        val doubled = 10
        myNumber = 10

用作ref參數的變量有兩個限制。首先,函數可能會改變引用參數的值,所以必須在函數調用中使用“非常量”變量。所以,下面的代碼是非法的:

        const int myNumber = 5;
        WriteLine($"myNumber = {myNumber}", );
        ShowDouble(ref myNumber);
        WriteLine($"myNumber = {myNumber}");

其次,必須使用初始化過的變量。C#不允許假定ref參數在使用它的函數中初始化,下面的代碼也是非法的:

        int myNumber;
        ShowDouble(ref myNumber);
        WriteLine("myNumber = {myNumber}");

4.輸出參數

除了按引用傳遞值外,還可以使用out關鍵字,指定所給的參數是一個輸出參數。out關鍵字的使用方式與ref關鍵字相同(在函數定義和函數調用中用作參數的修飾符)。實際上,它的執行方式與引用參數幾乎完全一樣,因為在函數執行完畢后,該參數的值將返回給函數調用中使用的變量。但是,二者存在一些重要區別:

● 把未賦值的變量用作ref參數是非法的,但可以把未賦值的變量用作out參數。

● 另外,在函數使用out參數時,必須把它看成尚未賦值。

即調用代碼可以把已賦值的變量用作out參數,但存儲在該變量中的值會在函數執行時丟失。

例如,考慮前面返回數組中最大值的MaxValue()函數,略微修改該函數,獲取數組中最大值的元素索引。為簡單起見,如果數組中有多個元素的值都是這個最大值,只提取第一個最大值的索引。為此,修改函數,添加一個out參數,如下所示:

        static int MaxValue(int[] intArray, out int maxIndex)
        {
          int maxVal = intArray[0];
          maxIndex = 0;
          for (int i = 1; i < intArray.Length; i++)
          {
              if (intArray[i] > maxVal)
              {
                maxVal = intArray[i];
                maxIndex = i;
              }
          }
          return maxVal;
        }

可采用以下方式使用該函數:

        int[] myArray = { 1, 8, 3, 6, 2, 5, 9, 3, 0, 2 };
        int maxIndex;
        WriteLine($"The maximum value in myArray is
                  {MaxValue(myArray, out maxIndex)}");
        WriteLine($"The first occurrence of this value is at element
                  {maxIndex + 1}");

結果如下:

        The maximum value in myArray is 9
        The first occurrence of this value is at element 7

注意,必須在函數調用中使用out關鍵字,就像ref關鍵字一樣。

主站蜘蛛池模板: 岑巩县| 辽源市| 屯留县| 沧源| 聊城市| 永平县| 萨迦县| 乐东| 高台县| 县级市| 垣曲县| 宜昌市| 大竹县| 道孚县| 惠东县| 吉水县| 建湖县| 延边| 莎车县| 南充市| 新乡县| 赤城县| 景宁| 城口县| 田林县| 睢宁县| 织金县| 大关县| 井冈山市| 通州市| 通海县| 盘山县| 邻水| 博客| 洪江市| 麟游县| 右玉县| 遂川县| 五指山市| 九寨沟县| 阳高县|