C# 入門指南 - 類別

典型的類別 (class) 定義,如下圖




整體結構就是宣告類別,類別名稱之後用一對大括弧把定義圍起來,裡頭有屬性 (property) 、建構子 (constructor) 、方法 (method) 的的定義,如果這個類別還需要是可執行的,就得額外定義 main() 方法。我們將上面的定義例子寫成完整的範例程式,如下
// 宣告類別名稱
class Demo2 {
    // 這裡定義屬性
    int a;
    int b;
    
    // 這裡定義建構子
    public Demo2(int a, int b) {
        this.a = a;
        this.b = b;
    }
    
    // 這裡定義方法
    public int do_something() {
        return a + b;
    }

    // 這裡定義 Main() 方法
    static void Main() {
        Demo2 demo = new Demo2(11, 22);
        
        System.Console.WriteLine();
        System.Console.WriteLine("a = " + demo.a + ", b = " + demo.b);
        System.Console.WriteLine("a + b = " + demo.do_something());
        System.Console.WriteLine();
    }
}

/* 《程式語言教學誌》的範例程式
    http://pydoing.blogspot.com/
    檔名:demo2.cs
    功能:示範 C# 程式 
    作者:張凱慶
    時間:西元 2012 年 10 月 */


先來編譯執行,看看執行出什麼結果囉



程式的執行在 Main() 方法的定義地方
// 這裡定義 Main() 方法
static void Main() {
    Demo2 demo = new Demo2(11, 22);
        
    System.Console.WriteLine();
    System.Console.WriteLine("a = " + demo.a + ", b = " + demo.b);
    System.Console.WriteLine("a + b = " + demo.do_something());
    System.Console.WriteLine();
}


我們在 Main() 中宣告 (declare) 並建立相同 Demo2 型態的物件 demo ,為什麼可以這樣做呢?為什麼要在 Main() 中另外建立物件 (object) ,而非直接存取屬性 a 及 b 呢?答案很簡單,首先,因為 a 與 b 沒有給初值,我們當然可以定義後直接給初值,例如
// 這裡定義屬性
int a = 11;
int b = 22;


這樣屬性 a 與 b 就各自擁有初值 11 及 22 ,我們的作法卻是
// 這裡定義屬性
int a;
int b;


屬性通常定義在所有方法之外,通常直接放在大括弧之後,類別宣告的下一行。這裡只有作屬性的宣告,也就是宣告屬性為 int 型態的變數,注意,這裡我們在 int 之後空一格,然後接上屬性的識別字 (identifier) 名稱。


C# 並沒有嚴格限制程式的編排方式,所以只要合乎語法, int 空一格,或空兩格並沒有多大關係,但須小心,如果把 int 跟識別字連結在一起,如 inta 或 intb ,編譯器會認為這是額外的識別字名稱,並非我們想要的 a 或 b 了。


因為我們的目的是宣告類別 Demo2 有哪些屬性,也就是先宣告屬性的資料型態,以及屬性的識別字名稱,屬性的值實際是由建構子所賦予,這也是建構子的主要功能之一。再來看到建構子定義的地方
// 這裡定義建構子
public Demo2(int a, int b) {
    this.a = a;
    this.b = b;
}


建構子如同方法,但不具有回傳值 (return value) 型態 (type) ,因為建構子的目的就是建立物件 (object) ,因此無須指明回傳值的型態。這裡,建構子有兩個參數 (parameter) ,分別是 a 與 b ,由於名稱和屬性相同,在建構子後面的大括弧,也就是建構子的程式區塊 (block) 中,參數 a 與 b 作為此區塊的區域變數 (local variable) ,屬性名稱 a 與 b 便無法使用,因此我們利用關鍵字 (keyword) this 來存取屬性,並把參數指派 (assignment) 給屬性。


當然,若是屬性名稱與建構子的參數名稱不同,我們可以不用 this 便可直接存取屬性名稱,例如
// 這裡定義建構子
public Demo2(int c, int d) {
    a = c;
    b = d;
}


這樣的小程式中,怎麼寫倒是看個人喜好,往後陸續介紹 publicprivate ,也就是權限方面的修飾詞 (modifier) 之後,我們會提供另外一種寫法。附帶一提, public 是公開的意思, private 則是私有的意思。


Demo2 只有定義一個方法,也就是 do_something()
// 這裡定義方法
public int do_something() {
    return a + b;
}


do_something() 回傳兩個屬性的相加值, return 是關鍵字之一,用來在方法中回傳數值。這裡 + 是 C# 的算術運算子 (arithmetic operator) 之一,用來作加法,因此 a + b 構成一個運算式 (expression) ,運算式會有一個計算結果, return 就是回傳這個計算結果。


注意, + 是鍵盤上直接打出來的半形字元,非全形字元 + 。


方法宣告時,除了回傳值型態,就是小括弧中的參數列 (parameter list) ,若無提供參數,小括弧就應該留空,也就是不填進任何東西,類似的,方法若無回傳值,應該宣告為 void ,像是 Main() 就宣告成沒有回傳值的 void


我們回到最早的問題,也就是為什麼可以在 main() 中建立與 Demo2 相同的物件?其實原因很簡單,就是 static 的緣故。 static 中文稱為靜態的,由於屬性與方法在類別中合稱為成員 (member) ,宣告為 static 的成員被稱為 static 成員, static 成員屬於類別,非 static 成員屬於物件。


成員屬於類別跟屬於物件到底有什麼不同呢?差別在, static 成員可以直接用類別名稱呼叫,我們不是已經用過很多個 System.Console.WriteLine() 的例子了嗎?
System.Console.WriteLine()


所以,我們要讓 Demo2 變成可執行的類別,我們就得替 Demo2 加上 Main() 方法的定義,由於 Main() 屬於 static 的成員, static 成員不能存取非 static 成員,這裡所謂的不能,其實是 C# 的語法規則,所以為了使 Demo2 可以測試 Demo2 自己,語法允許我們可以在 Demo2 的 Main() 中建立 Demo2 的物件。


有很難懂嗎?如果我們不想用 Demo2 測試 Demo2 他自己,那我們就得另寫一個含有 Main() 的類別,如 Demo3 ,然後在 Demo3 中的 Main() 建立 Demo2 的物件
// 測試 Demo2 的類別
public class Demo3 {
    static void Main() {
        Demo2 demo = new Demo2(11, 22);
        
        System.Console.WriteLine();
        System.Console.WriteLine("a = " + demo.a + ", b = " + demo.b);
        System.Console.WriteLine("a + b = " + demo.do_something());
        System.Console.WriteLine();
    }
}

/* 《程式語言教學誌》的範例程式
    http://pydoing.blogspot.com/
    檔名:demo3.cs
    功能:示範 C# 程式 
    作者:張凱慶
    時間:西元 2012 年 10 月 */


Demo3 必須和 Demo2 在相同資料夾內,兩個類別檔案也必須分開編譯 (compile) ...... 嗯, Demo2 、 Demo3 等等講了這麼多名稱,這些都是識別字,我們接下來看看變數命名規則吧!


中英文術語對照
類別class
屬性property
建構子constructor
方法method
宣告declare
物件object
識別字identifier
回傳值return value
型態type
物件object
參數parameter
區塊block
區域變數local variable
關鍵字keyword
指派assignment
修飾詞modifier
算術運算子arithmetic operator
運算式expression
參數列parameter list
成員member
編譯compile


您可以繼續參考
基礎篇


相關目錄
回 C# 入門指南
回 C# 教材
回首頁


參考資料
http://msdn.microsoft.com/zh-tw/library/ms173109(v=vs.80).aspx
http://msdn.microsoft.com/zh-tw/library/ms229036%28v=vs.80%29.aspx
http://msdn.microsoft.com/zh-tw/library/ms173110%28v=vs.80%29.aspx
http://msdn.microsoft.com/zh-tw/library/x9afc042%28v=vs.80%29.aspx
http://msdn.microsoft.com/zh-tw/library/ms173113%28v=vs.80%29.aspx
http://msdn.microsoft.com/zh-tw/library/ms173114%28v=vs.80%29.aspx
http://msdn.microsoft.com/zh-tw/library/ace5hbzh%28v=vs.80%29.aspx

4 則留言:

Unknown 提到...

站長你好,我想問問以下幾個問題:

甚麼是"回傳值"?"
回傳值的型態"?
為甚麼定義main()方法要"void"?
"方法"跟"main()方法"的差別是?
"System.Console.WriteLine()"是甚麼東西?
為甚麼要"Demo2 測試 Demo2 他自己"?

我是個超級新手,幾乎沒甚麼背景,不麻煩的話還請站長教導一下,十分感謝!

Kaiching Chang 提到...

「回傳值」就是方法回傳的數值, C# 規定方法要有回傳數值,像 main() 宣告為 void ,就表示 main() 沒有回傳數值。

main() 是一種預設的方法, C# 規定每個可執行類別都要有個 main() ,因為這是程式執行的起點。

System.Console.WriteLine() 是 .net 程式庫的東西,用來輸出內容到螢幕上。

至於 Demo2 測試 Demo2 ,這個說法是因為我們在 Demo2 的 main() 建立 Demo2 實體物件。

Unknown 提到...
作者已經移除這則留言。
Unknown 提到...

非常讚的站長好

首先致上深深的敬意,感謝您專業又熱心地為程式設計者披荊斬棘。我因想學c#,因緣際會來到 貴網站。昨天也試發了個問題,一來不知有否成功,二來問題不夠完整。所以今天再次請教,感恩。

請教建構子的問題
建構子是否一定要有?
1. 若是,是否一定要跟類別相同名稱?
2. 若非,是否就無需使用到new?我看一本書的例子,作者寫了一個類別Pulse,但內無建構子Pulse(),可是在宣告後引用,仍使用new Pulse()。這是否意味者,即使未特別寫建構子,仍是要new才可以將類別轉為物件來使用。

再者
1. 若有建構子,可否不使用new做初始化,而直接把宣告的類別拿來當物件用(當然可能會有一些初值的問題)?
2. 若無建構子,但類別中另寫的一個程序(或方法)在設定初值,是否應該在使用該類別(物件)前,就應先呼叫該程序做初使化?並請問,樹莓派中所用的GpioController.GetDefault就是這樣的作法。

黃蓮池 敬問