- C#入門經典(第7版):C# 6.0 & Visual Studio 2015(.NET開發經典名著)
- (美)Beijamin Perkins Jacob Vibe Hammer Jon D. Reid
- 4084字
- 2021-04-02 21:18:38
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關鍵字一樣。
- Puppet 4 Essentials(Second Edition)
- PyQt從入門到精通
- Java程序員面試算法寶典
- Python零基礎快樂學習之旅(K12實戰訓練)
- 基于差分進化的優化方法及應用
- Linux命令行與shell腳本編程大全(第4版)
- Highcharts Cookbook
- Python機器學習基礎教程
- Android開發案例教程與項目實戰(在線實驗+在線自測)
- Java程序設計案例教程
- C++編程兵書
- OpenCV with Python Blueprints
- Practical Predictive Analytics
- Learning ECMAScript 6
- React and React Native