不學 JAVA 換學 C# 之覺得心累 - L1:ch7 方法

AdSense

前言

方法 (Method) 是一組封裝的程式碼,用於執行特定的任務或邏輯。C# 中的方法具有靈活性和可重用性,使得程式更易於管理和維護。

特點:

  1. 可以有任意數量的參數與回傳值。
  2. 內部的實作對外部是隱藏的,僅提供接口給使用者呼叫。
  3. 可以重複使用,避免程式碼冗長與重複。

基本結構

每個方法都包含以下幾個部分:

  1. 存取修飾詞 (Accessibility Modifier):決定方法的存取範圍,例:publicprivate
  2. 靜態修飾詞 (Static Modifier):標記方法是靜態方法,或是需要實例化物件才能使用。
  3. 回傳型態 (Return Type):指定方法的回傳值類型。如果不需要回傳值,使用 void。如果需要有回傳值,除了指定回傳型態,方法內還需要用 return 回傳數值,且數值的型態必須跟定義的回傳型態一致。
  4. 方法名稱 (Method Name):方法的名稱。
  5. 參數列表 (Parameter List):接收外部傳入的參數,參數間用逗號分隔。參數型態必須明確列出,例如 intstring,以確保方法簽章的清晰性和正確性。
  6. 方法體 (Body):封裝的程式碼邏輯區塊。
1
2
3
4
5
6
7
8
// 存取修飾詞:public, 回傳型態:int, 方法名稱:Add, 參數列表:(int x, int y)
public int Add(int x, int y)
{
// 方法體 - 開始
int total = x + y;
return total; // 回傳值的型態,必須跟 line 2 定義的回傳型態一致
// 方法體 - 結束
}

簽章 (Signature)

通常指方法的名稱、參數的型態、個數與順序,以及回傳值的型態

它可以視為方法的唯一標識,用來區分不同的方法。例如,具有相同名稱但參數型態、個數與順序不同的方法會被視為不同的方法,這被稱為多載 (Overloading)

使用、呼叫方法

1
方法名稱(傳入的參數列表);

呼叫時

  1. 傳入的參數的數量類型需要與定義方法時的簽章一致
  2. 需要保留回傳值時,指派給某個變數。

範例

1
2
3
4
5
6
7
8
9
// 定義方法
public int Add(int x, int y)
{
int total = x + y;
return total;
}

// 呼叫方法,將回傳值指派給 sum
int sum = Add(1, 2);
  1. 呼叫方法:Add(1, 2) 是對 Add 方法的一次呼叫。12 是我們提供給 Add 方法的實際參數 (Arguments)。方法內部的 xy 是對應的形式參數 (Parameters),它們分別接收 12 的值。方法執行時,會將這兩個數字相加,指派給變數 total,然後回傳 total 的值。total 的型態必須是 int,因為必須跟 line 2 定義的回傳型態一致。
  2. 回傳值指派給某個變數:Add 方法的回傳型態是 int,所以它會將計算結果 total 的值,也就是 3 回傳給呼叫它的程式碼。這裡的 return total; 將計算結果回傳,而 line 9 將回傳結果指派給 int sum

存取修飾詞

類別 (class) 成員有方法 (method)、屬性 (property) 或欄位 (field)。而存取修飾詞是用來控制類別成員的可見性存取範圍。目前先知道 publicprivate 就好。

public 修飾詞

使用 public 宣告的成員可以在程式的任何地方被存取,無論是同一個類別內、還是不同類別,甚至不同的命名空間。因此,需要讓其他類別或外部程式使用該成員時,就用 public

private 修飾詞

使用 private 宣告的成員只能在同一個類別內存取,其他類別無法直接使用。因此,該成員只用於內部邏輯,而不需要對外暴露時,就用 private

靜態修飾詞

靜態修飾詞和類別 class 有密切的關係,如果暫時看不懂,了解完類別再回來這看。

如果方法跟各別物件實例沒有關係,就用靜態方法,否則用非靜態方法。

非靜態方法

非靜態方法屬於物件,必須透過實例化物件來呼叫。使用物件名稱呼叫。

1
物件名稱.方法名();

以下方例子來說,不同的實例化物件 Person 有不同的名字。印出名字 PrintName 這個方法就會跟各別物件有關係,所以必須使用非靜態方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Person
{
private string name;

public Person(string name)
{
this.name = name;
}

public void PrintName()
{
Console.WriteLine(this.name);
}
}

Person person1 = new Person("Jenifer");
Person person2 = new Person("Mary");

// 使用物件名稱呼叫 PrintName
person1.PrintName(); // 輸出: Jenifer
person2.PrintName(); // 輸出: Mary

靜態方法

靜態方法屬於類別本身,不需實例化物件即可呼叫。適合用於不依賴於物件狀態的邏輯。使用類別名稱呼叫。

1
類別名稱.方法名();

特性

  • 靜態方法使用類別名稱後大家都可以呼叫,例:Math.Max(x, y)Math 是 C# 內建類別,Max 是方法名。
  • 靜態方法內部無法存取非靜態成員,因為非靜態成員需要具體的物件實例。

以下方例子來說,Max 方法不會依賴於不同的 Math 物件實例,直接用 Math 類別名呼叫 Max 方法。

1
2
3
// 使用 Math 類別名稱呼叫 Max
var maxNumber = Math.Max(3, 10);
Console.WriteLine(maxNumber); // 輸出:3

回傳型態和參數

無參數、無回傳值

僅執行動作而無需處理輸入或回傳結果。

1
2
3
4
public void SayHello()
{
Console.WriteLine("Hello, World!");
}

有參數

適合需要輸入資料才能執行邏輯的情況。

1
2
3
4
public void PrintMessage(string message)
{
Console.WriteLine(message);
}

有回傳值

適合需要將處理結果回傳給呼叫者的情況。

1
2
3
4
public int Multiply(int x, int y)
{
return x * y;
}

陣列作為參數

在 C# 中,陣列作為參數傳遞時,是以複製記憶體位址的方式傳遞,但是因為實際參數 (Arguments) 和形式參數 (Parameters) 的值一樣,都是同一個記憶體位址,都指向同一個變數,這表示方法中對陣列內容的修改會影響到原始陣列,但重新指派陣列不會影響原始陣列,因為儲存的記憶體位址不一樣了

C# 中沒有 Call/Pass by Address 這種說法,但是實際上 C# 的「陣列作為參數傳遞」就是 Call/Pass by Address,以下方的例子來說,是 copy 實際參數 arr 的位址給形式參數 numbers,而不是 Call/Pass by Reference 取別名。請參考:值 (value)、指標 (pointer/address)、參考 (reference)。取別名的話,重新指派陣列會影響原始陣列。

對陣列內容的修改會影響原始陣列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public void ModifyArray(int[] numbers)
{
for (int i = 0; i < numbers.Length; i++)
{
numbers[i] *= 2; // 每個元素乘以 2
}
}
public void ShowArrayReference()
{
int[] arr = { 1, 2, 3, 4 };
Console.WriteLine("修改前:");

foreach (var num in arr)
{
Console.WriteLine(num);
}

ModifyArray(arr);

Console.WriteLine("修改後:");
foreach (var num in arr)
{
Console.WriteLine(num);
}
}

// 輸出:
// 修改前:
// 1
// 2
// 3
// 4
// 修改後:
// 2
// 4
// 6
// 8

重新指派陣列不會影響原始陣列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public void ReassignArray(int[] numbers)
{
numbers = new int[] { 10, 20, 30, 40 };
}
public void ShowArrayReference()
{
int[] arr = { 1, 2, 3, 4 };
Console.WriteLine("重新指派前:");

foreach (var num in arr)
{
Console.WriteLine(num);
}

ReassignArray(arr);

Console.WriteLine("重新指派後:");
foreach (var num in arr)
{
Console.WriteLine(num);
}
}

// 輸出:
// 重新指派前:
// 1
// 2
// 3
// 4
// 重新指派後:
// 1
// 2
// 3
// 4

注意:

  1. 原始資料的影響:由於陣列是傳址,對方法內的修改會直接反映在原始資料中。
  2. 安全性考量:若不希望原始資料被修改,可以在傳入方法前複製陣列,避免直接影響原始資料。
1
2
int[] copy = (int[])arr.Clone();
ModifyArray(copy);

Main 方法

在 C# 應用程式中,Main 方法是程式的入口點,負責啟動程式執行。它具有一些特別的特性和用法。

  1. 唯一性:每個 C# 應用程式只能有一個 Main 方法作為程式的入口點。
  2. 回傳型別
    • void:不需要回傳值,適用於一般應用程式。
    • int:可以回傳整數值,通常用於指示應用程式的執行狀態。
      • 0:約定俗成的成功碼,表示執行成功,程式正常結束。
      • 非 0 值:表示執行失敗或出現錯誤。
  3. 參數string[] args:允許接收命令列參數,便於應用程式根據啟動時的參數執行不同邏輯。
  4. 靜態方法Main 方法必須是 static,因為它在沒有建立物件的情況下直接由執行環境呼叫。
  5. 多重定義Main 方法可以有多個定義,但只有一個簽章會作為執行入口點。如果有多個定義,必須手動指定入口點。

範例

基本架構

1
2
3
4
5
6
7
8
9
10
11
using System;

namespace HelloWorld;

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}

接收命令列參數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;

namespace HelloWorld;

class Program
{
static int Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("請提供至少一個參數。");
return 1; // 返回錯誤碼
}

Console.WriteLine("命令列參數如下:");
foreach (var arg in args)
{
Console.WriteLine(arg);
}

return 0; // 返回成功碼
}
}

參考資料:
方法 (C# 程式設計手冊)
Main() 和命令列引數