C#快速入門教程(6)——方法
方法(method)是程式設計中重要的一個元素,也是一系列任務執行的關鍵所在,本課,將討論在C#中如何定義方法,包括方法的返回值和不同引數型別的應用;此外,還將討論一些方法的應用特點,主要包括:
- 引數與返回值
- 過載方法
- 建構函式與方法鏈
- 解構函式
首先看一下方法定義的一般形式:
[<訪問修飾符>] <返回型別> <方法名> ([<引數列表>])
{
// 方法體
}
這裡:
<訪問修飾符>可以定義方法的訪問級別,包含常用的private、public、protected、internal,以及定義靜態方法的static關鍵字、抽象方法的abstract關鍵字、虛擬方法的virtual關鍵字和重寫方法的override關鍵字。如果沒有訪問修飾符,則預設為私有的訪問級別。
<返回值型別>是指方法執行後返回的資料型別,如前面使用過的int、string等型別,也可以是後續課程所介紹的各種型別,方法體中可以使用return語句返回執行結果資料。如果方法不需要返回資料,則使用void關鍵字設定。
<方法名>當然是指方法的名稱,如前面使用過的Drive()、Return()、ShowLocalModel()方法等。
<引數列表>用於帶入方法所需要的資料,如果沒有就空著。引數可以有多個,每個引數最少需要定義資料型別和引數名稱,多個引數使用英文半形逗號分隔;稍後,我們會詳細討論引數的應用問題。
方法體是方法真正實現功能的地方,使用一對花括號定義,這也是語句塊的定義方式。
在後續的學習中會逐步瞭解各種軟體功能的實現,而接下來,我們先討論幾種引數的設定和應用。
引數與返回值
普通引數
普通引數只需要指定資料型別和引數變數,如下面的程式碼,我們在ConsoleTest專案中建立一個名為C5的類,其中定義了一個Add()靜態方法。
namespace ConsoleTest
{
public class C5
{
public static int Add(int x, int y)
{
return x + y;
}
}
}
方法功能很簡單,只是用來計算兩個32位整數(int型別)的和(很明顯是把事情搞複雜了!^_^);下面的程式碼,我們在Program.cs檔案中測試C5.Add()方法。
using System;
namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
int x = 10;
int y = 99;
Console.WriteLine("{0} + {1} = {2}", x, y, C5.Add(x, y));
}
}
}
程式碼執行結果如下圖所示。
現在,注意區分Add()方法中的x和y引數,以及Main()方法中的x和y變數,在前面的課程中,我們提到過資料傳遞過程中的訪問級別問題。在Add()的引數中定義的x和y只能在Add()方法體中使用;Main()方法中定義的x和y變數,在呼叫Add()方法時,會將變數的資料傳遞到Add()方法中,然後將計算結果通過return語句返回,整個過程中並沒有產生歧義,所以,程式碼可以正常執行。
引數預設值
使用方法時,有此引數可能會有一些常用值,此時,可以將引數的資料設定預設值,以方便方法的呼叫。需要注意的是,有預設值的引數應放在所有沒有預設值的引數的後面,如下面的程式碼,我們在C5類中新增了一個名為Add1()的引數。
public static int Add1(int x, int y = 0)
{
return x + y;
}
引數中,y設定了預設值0,也就是說,在呼叫Add1()方法時,如果不指定引數y的資料,那麼,它的資料就是0,下面,在Program.cs檔案中測試此方法的使用。
using System;
namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(C5.Add1(10));
Console.WriteLine(C5.Add1(10, 99));
}
}
}
程式碼執行結果如下圖。
引數陣列
前面建立的Add()和Add1()方法,可以使用一個或兩個引數,如果是需要不定數量的引數應該怎麼辦呢?此時,可以使用引數陣列,如下面的程式碼,我們在C5類中定義一個Add2()方法。
public static int Add2(int x, params int[] nums)
{
int sum = x;
for (int i = 0; i < nums.Length; i++)
sum = sum + nums[i];
return sum;
}
本例中,請注意第二個引數,其定義如下:
params int[] nums
這裡使用params關鍵字定義了一個int型別的陣列引數nums。陣列是指一組相同型別資料的集合,定義時在型別名稱後加一對方括號,如本例中的int[]。
引數陣列在應用時有什麼特點呢?它可以使用零或多個引數,如本例中的Add2()方法,除了第一個引數是必須的,我們還可以使用第二個或更多的引數,如下面的程式碼,我們在Program.cs檔案測試Add2()方法的使用。
using System;
namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(C5.Add2(10));
Console.WriteLine(C5.Add2(10, 99));
Console.WriteLine(C5.Add2(1,2,3,4,5));
}
}
}
程式碼執行結果如下圖所示 。
引數陣列應用在引數列表的最後,不應和引數預設值一起使用,那樣會產生歧義。
ref引數
說起ref關鍵字的應用,大家首先還需要理解值型別和引用型別資料的傳遞問題。
C#中的資料型別分為值型別和引用型別,它們在記憶體中的處理方式是不同的,但在直接應用時,表面上可能不太容易看出它們的區別,所在,這裡著重討論按值傳遞和按引用傳遞區別。
在C#中,一些基本的資料型別定義為值型別,如int、long、float、double、decimal等等;值型別的預設傳遞方式是複製其資料,如下面的程式碼。
using System;
namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
int x = 10;
int y = x;
Console.WriteLine("x={0},y={1}",x, y);
x = 99;
Console.WriteLine("x={0},y={1}", x, y);
}
}
}
先看一下執行結果,如下圖所示。
程式碼中,首先將x賦值為10,然後將x的值賦值給y,此時,由於int是值型別,所以是將x的值複製到y中,y和x並沒有什麼關聯;所以,當修改x的值為99後,y的值依然是10,如上圖所顯示的結果一樣。
int是值型別,而各種class型別則是引用型別,下面的程式碼,我們看一下CAuto型別的賦值操作。
static void Main(string[] args)
{
CAuto racer = new CAuto()
{
DoorCount = 2
};
CAuto suv = racer;
Console.WriteLine("{0},{1}", racer.DoorCount, suv.DoorCount);
suv.DoorCount = 5;
Console.WriteLine("{0},{1}", racer.DoorCount, suv.DoorCount);
}
程式碼執行結果如下圖所示。
本例中首先建立了racer物件,然後將其直接賦值給suv物件,此時,實際上是傳遞了racer物件在記憶體中的地址,也就是說suv和racer指向的是同一資料區域;接下來,只修改suv物件的資料,但racer物件的資料也改變了,這一現象證實了,只是通過簡單的賦值,suv和racer物件的確是指向同一資料區域。
使用方法時,值型別的處理方式是複製資料;如果需要在方法中真正地修改原變數的資料,就需要使用ref關鍵字,如下面的程式碼,我們在C5類中定義一個Swap()方法。
public static void Swap(ref int x, ref int y)
{
int z = x;
x = y;
y = z;
}
我們可以看到,Swap()方法的功能是交換兩個int型別變數的值,只是引數定義進使用了ref關鍵字,下面的程式碼,我們在Program.cs檔案中測試這個方法。
static void Main(string[] args)
{
int x = 10;
int y = 99;
Console.WriteLine("x={0},y={1}",x, y);
C5.Swap(ref x, ref y);
Console.WriteLine("x={0},y={1}", x, y);
}
程式碼執行結果如下圖所示。
請注意,當引數使用ref關鍵字時,呼叫方法設定引數時也需要使用ref關鍵字。
動手測試:大家可測試一下Swap()方法的引數沒有使用ref關鍵字時的執行結果。
out引數與返回值
我們知道,方法可以通過return語句返回執行結果,但這只是在方法只有一個返回資料的情況下?那麼,方法還能同時返回多個執行結果嗎?
下面,我們先看一個簡單的例子——如何將字串(string)內容轉換為int型別。
static void Main(string[] args)
{
string s = "123";
int num = 0;
bool result = int.TryParse(s, out num);
Console.WriteLine("轉換結果:{0}", result);
Console.WriteLine("轉換資料:{0}", num);
}
程式碼中,可以修改s的內容來觀察執行結果。變數num用於存放轉換後的資料,如果不能正確轉換,則初始值是0;變數result用於存放轉換結果,成功轉換為True值(true值),失敗為False值(false值)。轉換操作使用了int.TryParse()靜態方法,其中,第一個引數是需要轉換的字串內容,引數二定義為out引數,用於儲存轉換後的資料;方法的返回值表示轉換操作是否成功。
程式碼執行結果如下圖所示。
如果將s的內容修改為abc,則執行結果如下圖所示。
實際上,我們定義的方法也可以使用out引數,其功能就是讓方法返回更多的資料,如下面的程式碼,我們在C5類中定義一下TryToInt()方法。
public static bool TryToInt(object o, out int result)
{
if (o == null)
{
result = 0;
return false;
}
else
{
return int.TryParse(o.ToString(), out result);
}
}
這裡,我們將資料轉換方法進行了擴充套件,可以嘗試將任何型別的資料轉換為int型別,下面的程式碼,我們在Program.cs檔案中測試此方法的使用。
static void Main(string[] args)
{
object obj = "abc";
int num = 0;
bool result = C5.TryToInt(obj, out num);
Console.WriteLine("轉換結果:{0}", result);
Console.WriteLine("轉換資料:{0}", num);
}
大家可以修改obj物件的內容來觀察執行結果,可以嘗試空物件的null值、各種數值等型別的資料。
過載方法
現在,回顧一下Console.WriteLine()方法的呼叫,我們使用了多少種引數的組合?它實際上就是有多個版本的過載方法。
過載方法的基本含義是,定義一系列同名方法,並且可以通過引數的型別、個數等元素有效區別方法的不同版本。
在同一型別中,方法的過載在很多情況下可以使用引數預設值、引數陣列等特性來代替,而過載應用的情景是,方法的引數差異較大,無法通過引數預設值和引數陣列來處理。如下面的程式碼,我們又在C5類中新增了兩個Swap()方法,用於交換long型別和decimal型別變數的資料。
//
public static void Swap(ref long x, ref long y)
{
long z = x;
x = y;
y = z;
}
//
public static void Swap(ref decimal x, ref decimal y)
{
decimal z = x;
x = y;
y = z;
}
我們可以看到,Swap()方法的引數型別是不同的,但有一點,方法中的程式碼邏輯是相同的,實際上,對於這種情況,可以考慮使用泛型來處理,在後續的課程中會有討論;現在,我們需要明白的是,見到同名方法,而引數又有明顯區別時並不需要感到驚訝,它們可能只是同名的過載方法而已。
建構函式與方法鏈
我們提到過,建構函式是一種特殊的方法,那麼,特殊在什麼地方呢?主要表現為不需要設定返回值型別,以及需要和類同名。
如果在類中沒有定義建構函式,則會自動包含一個沒有任何引數的建構函式;但是,如果手工新增了任何建構函式,就不會自動新增建構函式。
建構函式也可以通過引數預設值、引數陣列、過載等形式建立多個版本,不過,我們還可以通過另外一種形式建立多版本的建構函式(或方法),如下面的程式碼,我們在CAuto類中定義了三個建構函式。
public class CAuto
{
// 建構函式
public CAuto(int doorCount,string model)
{
DoorCount = doorCount;
Model = model;
}
//
public CAuto(string model) : this(4, model) { }
//
public CAuto() : this("未知型號") { }
//
}
本例中,我們首先建立了一個比較完整引數的建構函式,用於設定車門數(DoorCount)和型號(Model)。第二個建構函式包含一個引數,但其後使用this關鍵字呼叫了第一個建構函式,其含義是將車門設定為4,並設定型號。第三個建構函式使用this關鍵字呼叫了第二個建構函式,將型號設定為“未知型號”,此時,車門數量預設同樣為4。這樣逐個呼叫的建構函式就形成了方法鏈結構,實際上,不只是建構函式,其他方法同樣可以通過這種形式簡化多版本的定義。
下面的程式碼,我們在Program.cs檔案中測試這三個建構函式的使用。
static void Main(string[] args)
{
CAuto car1 = new CAuto(2, "Coupe");
CAuto car2 = new CAuto("CX5");
CAuto car3 = new CAuto();
Console.WriteLine("{0},{1}", car1.DoorCount, car1.Model);
Console.WriteLine("{0},{1}", car2.DoorCount, car2.Model);
Console.WriteLine("{0},{1}", car3.DoorCount, car3.Model);
}
程式碼執行結果如下圖所示。
解構函式
建構函式在建立物件時呼叫,而解構函式則會在刪除物件時呼叫;在.NET Framework中已經有了很成熟的記憶體回收機制,當物件不再使用時,會自動釋放記憶體,所以,需要手工編寫解構函式的時候並不多,但大家應該知道有這樣一個地方可以用來手工釋放資源。
下面的程式碼,我們在CAuto類中新增一個簡單的解構函式。
// 解構函式
~CAuto()
{
Console.WriteLine("汽車物件回收中");
}
下面的程式碼,在Program.cs檔案中測試解構函式。
static void Main(string[] args)
{
CAuto car = new CAuto();
//car = null;
}
程式碼中,“car= null;”語句用於手工釋放car物件,即將物件設定為null值,大家可測試一下,無論有沒有這條語句,執行結果都如下圖所示。
從本例中,我們可以看到,無論是手工釋放物件,還是.NET Framework自動釋放物件,都會呼叫解構函式。另一種自動釋放資源的機制是通過using語句結構呼叫實現IDisposable介面的物件,後續內容中會有介紹。
CHY軟體小屋原創作品!
相關文章
- 《C#快速入門教程》目錄C#
- C#快速入門教程(16)—— 介面C#
- C#快速入門教程(26)—— 繪圖C#繪圖
- C#快速入門教程(21)—— 泛型C#泛型
- C#快速入門教程(15)—— 繼承C#繼承
- C#快速入門教程(8)——整數C#
- C#快速入門教程(28)—— ADO.NETC#
- C#快速入門教程(25)—— 日期與時間C#
- C#快速入門教程(22)—— 常用集合型別C#型別
- C#快速入門教程(30)—— 繼續學習C#
- C#快速入門教程(18)—— 異常處理C#
- C#快速入門教程(12)—— if語句結構C#
- C#快速入門教程(27)—— SQL Server資料庫C#SQLServer資料庫
- C#快速入門教程(19)—— 索引器與陣列C#索引陣列
- C#快速入門教程(5)——欄位與屬性C#
- C#快速入門教程(11)—— 字元和字串型別C#字元字串型別
- C#快速入門教程(13)—— switch語句結構C#
- C#快速入門教程(7)——資料型別概述C#資料型別
- C#快速入門教程(2)——程式碼與測試C#
- C#快速入門教程(20)—— 字串與正規表示式C#字串
- C#快速入門教程(23)—— using語句和IDisposable介面C#
- C#快速入門教程(14)—— 迴圈語句結構C#
- C#快速入門教程(4)——類成員的作用域C#
- C#快速入門教程(1)——物件導向程式設計C#物件程式設計
- Materialize快速入門教程
- C#快速入門教程(24)—— 路徑、目錄與檔案C#
- C#快速入門教程(17)—— 委託、事件與Lambda表示式C#事件
- C# 12 Blazor入門教程C#Blazor
- Jupyter notebook快速入門教程
- IPv6入門教程
- ES6快速入門(三)
- ES6快速入門(二)
- c#入門教程(菜鳥級)C#
- 快應用快速入門教程
- go語言快速入門教程Go
- 全面的Docker快速入門教程Docker
- Vue3快速入門教程Vue
- C#快速入門教程(10)——布林型別與布林運算C#型別