C# 入門指南 - Encrypt 類別

利用 C# 寫程式 (program) ,著重在設計類別 (class) ,然後規劃物件 (object) 的使用,以及物件與物件間的互動,同時要注意,一個物件專心做好一件事情就好,類別的設計不應該太過複雜




我們打算開始發展一個 Encrypt 類別,它的主要功能是建立一個英文小寫字母的對換表格,藉由這個表格,我們可以對英文句子中的小寫英文字母進行對換,例如 "There is no spoon." 可能變成以下任一個
Tfqdq ki jo itooj.
Tcnan hf gl fqllg.
Tczmz dn ij nkjji.
Tgfsf pb ir barri.
Tdcpc my fo yxoof.


其中, EncryptDemo 類別為我們在命令列 (command line) 對 Encrypt 進行測試的類別,然後,最重要的是我們還要發展使用者介面 (user interface) 的類別,給使用者輕鬆操作的圖形使用者介面 (graphical user interface, GUI) ,也就是 EncryptGUI 類別,我們會直接在 EncryptGUI 中設置 Main() 方法,因此 EncryptGUI 可直接執行。


這裡把建立對換表格的工作交給 Encrypt ,使用者的 GUI 介面的設計交給 EncryptGUI 。 EncryptDemo 為命令列的測試程式, EncryptGUIDemo 中同樣建立 Encrypt 物件,但有更多功能,外觀如下圖



有兩個可供輸入的文字欄位 (textfield) ,其中一個我們作為輸出的顯示訊息之用,另有三個標籤 (label) ,顯示文字的提示訊息,七個按鈕 (button) ,提供「新建」、「開啟」、「儲存」 Encrypt 物件,與「編碼」、「解碼」所輸入的英文句子,「清除」所有輸入欄位,以及「拷貝」輸出結果等的功能。


為什麼要設計這麼多的類別,不把所有功能塞進一個類別就好了呢?寫個 Main() ,然後把所有程式碼塞進 Main() 當中,同樣可以達到我們想要的程式。理由其實很單純,我們大可發展一個功能完備可在命令列大展神威的 Encrypt ,可是換在 GUI 中,很多細節必須重新設計,幾乎等於重寫,因此我們把功能與使用者介面分開來,實際編碼、解碼的工作交給 Encrypt ,因此命令列使用 EncryptDemo 操作,可直接利用 Encrypt , GUI 由 EncryptGUI 設計,也可重複利用 Encrypt ,這樣一來,我們就不需要為命令列版本與 GUI 版本重複寫進相同的 Encrypt 程式碼。


事實上, .net 中許多類別都是基於這樣的理念設計的,一個類別專心做好一件事情就好 :)


現在我們先來看看 Encryptor 所有功能的核心,也就是 Encrypt 類別,我們的目的是,建立一個小寫英文字母的轉換表格,然後編碼、解碼都可直接依據這個表格。首先,我們先來看看建立表格的數學公式
y = a * x + b
m = y % n
r = m + diff


我們的概念很簡單,假設 x 為字元的原始編碼, Unicode 編碼中 'a' 為 97 ,其後依次遞增 1 ,然後我們將 x 乘上變數 (variable) a , 再加上變數 b ,兩者均是 0 到 9 的隨機整數,這樣便得到 y 的值。


然後將 y 除以 n 取得餘數 m , n 為所要轉換的字元數量,英文小寫字母共有 26 個,所以這裡 n 等於 26 ,因此 m 等於 0 到 25 之間的整數值。最後將 m 加上 diff , diff 也就是編碼系統的差值,由於 Unicode 中 'a' 為 97 ,所以這裡 diff 要以 97 代入。


因此,餘數 0 的字元會替換成 'a' ,餘數 1 的字元會被替換成 'b' ,餘數 2 的字元會被替換成 'c' ,餘下 23 個字元類推。這樣的計算需要進行 n 次,也就是 26 次,我們最後得到一組餘數與相對應字元的表格,這就是我們需要的表格了。


重複 n 次,我們需要一個迴圈 (loop) ,由於重複次數確定,因此 for 迴圈 (for loop) 很適合,那我們要用什麼東西來儲存這個表格呢?嗯, .net 中有許多的資料結構 (data structure) ,可以依資料特性有效率的處理資料,這裡,我們利用字串 (string) 就可以了。


字串是一種字元陣列 (array) ,陣列是固定大小的資料結構,當宣告陣列後,同時需要宣告陣列的型態與大小,也就是說,陣列最多可以放進多少個元素 (element) ,這些元素必須是相同的資料型態,而且,陣列的的索引值由 0 開始,因此完全符合我們計算餘數從 0 開始的需求。


因此, Encrypt 需要以下的屬性 (property)
string cArray;


這裡僅需要建立一個供 Encrypt 裡各個方法 (method) 存取的 cArray , cArray 儲存的正是密碼表。


我們先設置一個 setArray() 方法來測試,公式中需要用到的值,如整數 a 、 b 、 n 與 diff 等,這些都放在 setArray() 方法中當區域變數 (local variable) 。 a 先設成 3 , b 設成 5 好了
public void setArray() {
    int a = 3;
    int b = 5;
    int x, y, m;
    char c = 'a';
    int i;
    System.Text.StringBuilder s = new System.Text.StringBuilder();
    for (i = 0; i < 26; i++) {
        x = c;
        y = x * a + b;
        m = y % 26;
        s.Append((char) (m + 97)); 
        c++; 
    }
    
    this.cArray = s.ToString();   
}


區域變數可以說是計算過程中所需要的暫存變數,也就是我們不會在其他地方存取或使用的變數,這些變數單獨在方法裡宣告、建立即可。


setArray() 有幾個觀念需要解釋,首先
x = c;


屬性 c 是 char 字元型態,而 x 為整數型態,這裡將字元型態的值指派給整數型態的變數,那麼 x 中得到的會是字元的 Unicode 的 Unicode 編碼值,由於 c 已經指派初值為 'a' ,因此 x 會是整數 97 。然後我們用到一個新的類別 StringBuilder
System.Text.StringBuilder s = new System.Text.StringBuilder();


由於字串建立後無法變更其內容,因此我們拿專門用來建立字串的 StringBuilder 當暫存變數,所以
s.Append((char) (m + 97));


計算完成後便用 StringBuilder 的 Append() 方法將字元附加到 s 的最後,這裡要注意小括弧圍著 char 是強制型態轉換的方式,由於 m 及 97 都是整數 int 型態,經過小括弧的強制型態轉換,會將 (m + diff) 轉換為字元 char 型態,例如,如果 m 等於 0 , 0 + 97 等於 97 ,那麼最後 'a' 會附加到 s 之中。


最後用 ToString() 方法,將 s 的字串格式指派給 cArray
this.cArray = s.ToString();


這裡用到 thisthis 為關鍵字 (keyword) 之一,在類別中用來存取屬性與方法。


我們先寫一個簡單的程式來看看轉換後的表格長怎麼樣吧!
// Encrypt 的測試類別
// 使用 y = 3 * x + 5
class Encrypt {
    string cArray;
        
    public void setArray() {
        int a = 3;
        int b = 5;
        int x, y, m;
        char c = 'a';
        int i;
        System.Text.StringBuilder s = new System.Text.StringBuilder();
        for (i = 0; i < 26; i++) {
            x = c;
            y = x * a + b;
            m = y % 26;
            s.Append((char) (m + 97)); 
            c++; 
        }
    
        this.cArray = s.ToString();   
    }
        
    static void Main() {
        Encrypt e = new Encrypt();
        e.setArray();
        System.Console.WriteLine("e: " + e.cArray);
    }
}

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


編譯後執行,結果如下



結果如預期,一個英文小寫字母恰恰好對到另一個英文小寫字母,這樣我們的公式就 ok 了嗎?嗯, a 與 b 都可以是 0 到 9 之間的任意整數,有 100 種組合說,所以,我們需要繼續測試是不是每一種組合都 ok 囉!


中英文術語對照
程式program
類別class
物件object
命令列command line
使用者介面user interface
圖形使用者介面graphical user interface, GUI
textfield文字欄位
標籤label
按鈕button
變數variable
迴圈loop
for 迴圈for loop
資料結構data structure
字串string
陣列array
元素element
屬性property
方法method
區域變數local variable
關鍵字keyword


您可以繼續參考
軟體開發


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


參考資料
http://msdn.microsoft.com/zh-tw/library/ms173109%28v=vs.80%29.aspx
http://msdn.microsoft.com/zh-tw/library/x9afc042.aspx
http://msdn.microsoft.com/zh-tw/library/ms229036%28v=vs.80%29.aspx
http://msdn.microsoft.com/zh-tw/library/0b0thckt.aspx

2 則留言:

Unknown 提到...

謝謝分享!
另外main()裡面e.setcArray應為setArray ^__^

Kaiching Chang 提到...

這邊打錯字了,已修改,感謝指正 ^_^