About

塵世間一位迷途師程工,「三單」(單眼、單車、單身) 幫初階會員。

| Posted by LittleLin

[筆記] 使用 SpecFlow 搭配 Selenium 做 Web API 測試初體驗

其實是這兩天突然有的想法,於是想說來做一下驗証,順便練一下功。

| Posted by LittleLin

[C#][簡報] Delegate Introduction

貼上上週和學長合講的內部 training 課程的簡報檔內容。

Delegate (委派) Introduction from Zheng-Wei Lin
| Posted by LittleLin

[C#][筆記] Delegate 備課漢參考來源整理

最近和部門學長要合講一個部門 training 的 session,主題是 C# 中的 Delegate (委派)。因為議題還牽扯滿多部份的 (事件、匿名方法、Closure 等等),所以看了不少的參考資料,想說整理成篇方便自己未來查詢吧。

主要參考來源,分為 MSDN、書籍與論壇、Blog 文章,分列如下︰

| Posted by LittleLin

[Tip] 改善 Google Docs 在 Windows 環境下的中文顯示字型

這也是困擾我一陣子的小問題。

之前我在考慮使用 Google Docs 來做分享,英文簡報其實沒什麼問題,但中文簡報,就會醜醜的。原因是 Google Docs 會取用新細明體來做為中文顯示字型,如下︰

調整前,中文以新細明體顯示

| Posted by LittleLin

[Tip] 使 Windows 下的熱鍵 (Hotkey) 行為與 Mac OS X 達成一致 (使用 AutoHotkey)

標題很奇怪,其實是我一直以來的小小困擾 XD

我自己是 Mac/Windows 兩個 OS 平台的使用者與開發者,平常會視狀況決定開哪個 NB 來做事。

在兩個平台切換其實是習慣問題,但我到現在仍然不習慣的,就是兩個平台在鍵盤上對應的不同,讓我在切換平台工作時,常會有按錯鍵的問題。比方說,Mac 下的 「Command + 左鍵」 = Windows 的 「Home 鍵」這件事,就常會讓我在兩平台寫 code 時按錯鍵,讓我分心。

我常會在 Windows 下一直按 Alt + 左鍵或是 Ctrl + 左鍵,然後期待編輯器的游標切換,結果都會失敗……。類似的還有 Command + 右鍵、Command + 上鍵、Command + 下鍵等 hot key。

今天總算克服了懶病,在 Windows 下使用 AutoHotkey,加上了以下對應,讓 Windows 下的 Alt + * 和 Mac 下的 Command + * 熱鍵可以對齊,來解決這個困擾。

設定內容如下,只要加到自己的 AutoHotkey 設定檔的最下面,再重新載入設定,就可以享用此熱鍵設定了。

!Left::Send {Home} ; Alt + 左鍵 = Home
!Right::Send {End} ; Alt + 右鍵 = End
!Up::Send {PgUp}   ; Alt + 上鍵 = Page Up
!Down::Send {PgDn} ; Alt + 下鍵 = Page Down

如果有不清楚 AutoHotkey 設定的朋友,可以閱讀以下這篇介紹文︰


當然還有其他熱鍵,應該也可以用這種方式進行設定。有興趣的朋友們可以再進行擴充,謝謝!

| Posted by LittleLin

[C#][筆記] foreach、Iterator 與 yield

一、foreach 與 IEnumerable、IEnumerable<T> 的關係

上篇文章中,和大家說明了 IEnumerable、IEnumerator 與它們的泛型版本 IEnumerable<T>、IEnumerator<T> 的原理與作用後,本篇將再進一步說明,IEnumerable 與 IEnumerator 的用途。

| Posted by LittleLin

[C#][筆記] IEnumerable、IEnumerator 與 IEnumerable、IEnumerator

一、前言

在寫程式上,我們常會使用到許多資料結構,如串列 (List)HashTable (C# 下的 Dicationay)堆疊 (Stack)樹 (Tree),僅管這些資料結構在內部實作與外部使用的界面上,各有不同,但基本上我們都會希望這些資料結構,能提供有巡訪 (Traverse) 資料的能力。

所謂的巡訪,就是逐一走過資料結構下,內含的所有的資料元素 (Element) 或節點 (Node),當然在不同的資料結構上,依照內部實作不同,巡訪的演算法也會各有不同。

| Posted by LittleLin

[C#] 使用 IEnumerable 與 yield 機制,實作樹狀結構巡訪 (Tree Traversal)

其實我原本是想要測試 yield 可不可以在適用在遞迴的情形下,順手就做了一版簡單的 Preorder Tree Traversal。

我們先定義樹的節點 class,其中我們加上 Preorder 屬性,會回傳針對樹狀資料結構,做 preorder 巡訪的結果︰

/// <summary>
/// 樹狀結構節點
/// </summary>
/// <typeparam name="T">節點資料型別</typeparam>
public class Node<t>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="Node&lt;T&gt;"></see> class.
    /// </summary>
    /// <param name="data">節點資料
    public Node(T data)
    {
        this.Data = data;
    }
 
    /// <summary>
    /// 節點資料
    /// </summary>
    public T Data { get; set; }
 
    /// <summary>
    /// 左子樹
    /// </summary>
    public Node<t> Left { get; set; }
 
    /// <summary>
    /// 右子樹
    /// </summary>
    public Node<t> Right { get; set; }
 
    /// <summary>
    /// Preorder Traversal
    /// </summary>
    public IEnumerable<t> Preorder
    {
        get
        {
            return this.ScanPreorder(this);
        }
    } 
 
    /// <summary>
    /// Preorder Traversal Algorithm
    /// </summary>
    /// <param name="tree">巡訪的樹
    /// <returns>巡訪結果</returns>
    private IEnumerable<t> ScanPreorder(Node<t> tree)
    {
        // 節點不得為 null
        if (tree == null)
        {
            throw new ArgumentNullException();
        }
 
        // preorder Traversal
        yield return tree.Data;
 
        if (tree.Left != null)
        {
            foreach (var node in this.ScanPreorder(tree.Left))
            {
                yield return node;
            }
        }
 
        if (tree.Right != null)
        {
            foreach (var node in this.ScanPreorder(tree.Right))
            {
                yield return node;
            }
        }
    }
}

我們要巡訪的樹,長相如下︰ image

使用方式如下︰

// 建立樹狀結構
Node tree = new Node(0);
tree.Left = new Node(1);
tree.Right = new Node(2);
tree.Left.Left = new Node(3);
tree.Left.Right = new Node(4);
tree.Right.Right = new Node(5);
tree.Left.Left.Left = new Node(6);
tree.Left.Left.Right = new Node(7);
tree.Right.Right.Left = new Node(8);
tree.Right.Right.Right = new Node(9);

// Preorder,印出上圖中,Preorder 的順序
Console.Write("Preorder:\t");
foreach (var node in tree.Preorder)
{
    Console.Write(node);
}

因為這篇算倉促為之,後續再補上原理和完整程式 XD

| Posted by LittleLin

[羅漢咖的灶咖] 秀珍菇炒飯

因為最近搬到一間有廚房的租處 (和其他房客共用),所以總算可以開始學習自己做菜給自己吃,做一位稱職的羅漢咖了!

於是再來也會開始在 blog 上,放一下做菜成果的照片,順便記錄一下步驟。當然身為初學者,做菜難吃是很正常的,也算是幫自己記錄一下歷程。

今晚做的菜是「秀珍菇炒飯」,故名思義就是秀珍菇 + 炒飯啦,沒什麼學問,所以簡單來記錄一下材料和成果︰

材料


  • 白飯一碗︰份量依自己的食量做調整


  • 蛋兩顆

  • 秀珍菇 1/3 盒︰秀珍茹我是到全聯去買的,一盒 $32

  • 葱一小把


成果照如下︰

| Posted by LittleLin

[C#][筆記] Value Type (實值型別) vs. Reference Type (參考型別)

MSDN 上,將 .Net 下所有的類別,分為以下三類︰

  • Value Type (實值型別)︰常見的型別是 structintchardouble
  • Reference Type (參考型別)︰典型的例子就是使用 class 關鍵字定義的型別
  • Pointer Type (指標型別)

其中 Pointer Type,主要是像 C/C++ 語言,可以用來對記憶體直接操作。但因為使用到它的機會比較特別,我自己使用到它的機會也沒有太多,所以本文暫時先不針對此型別做討論。

而 Value Type 與 Reference Type 兩者在 .Net 下的型別階層體系的差別上,最明顯的差別與區別方式在於,Value Type 皆繼承自 System.ValueType,因此如果不是繼承自 System.ValueType 的型別,都不是 Value Type。

針對 Value Type 與 Reference Type 的差異,我們可以先以以下兩句 ,簡單做個說明︰

  • 在 Value Type 變數中,儲存的值是「實值」(Value),像是整數、浮點數、布林、字元等
  • 在 Reference Type 變數中,儲存的值是「參考」(Reference),也就是記憶體的位址 ,指向儲存真實內容的記憶體區塊的開始位置。

也就是說,Value Type 與 Reference Type 最顯著的差別,是他們在記憶體中,儲存其變數值的方式。再往下繼續探討前,我覺得我們可以先建立以下的基礎觀念︰

  • C# (或說大部份的程式語言) 會將記憶體分為兩大用途︰Stack 與 Heap。
  • C# 中所有的區域變數 (不管是 Value Type 或是 Reference Type),其內容 (變數名稱、型別與與值) 都是儲存在 Stack 中。Value Type 變數儲存的內容是「實值」,Reference Type 變數儲存的內容是「參考」。
  • 使用 new 關鍵字實體化類別的物件,其物件內容,是儲存在 Heap 中。Reference Type 變數中所儲存的參考,其實是指向 Heap 中的記憶體位址。

墳墓大大之前有寫了一系列關於 C/Java 在記憶體管理上,非常好的系列文章,大家可以閱讀第一、二篇 (其實全系列 8 篇都很推薦閱讀),以建立上述觀念︰

為了測試兩者在記憶體儲存上的差別,我建立測試用 Reference Type 型別與 Value Type 型別如下︰

/// 
/// 測試用 Reference Type 型別
/// 
public class DemoReferenceType
{
    int _field;
    public int Field
    {
        get
        {
            return this._field;
        }

        set
        {
            this._field = value;
        }
    }

    public DemoReferenceType(int val)
    {
        this._field = val;
    }

    public override string ToString()
    {
        return "Field=" + this._field.ToString();
    }
}

另外建立測試用 Value Type 如下︰


/// 測試用 Value Type 型別
/// 
public struct DemoValueType
{
    int _field;
    public int Field 
    { 
        get
        {
            return this._field;
        }
        
        set
        {
            this._field = value;
        }
    }

    public DemoValueType(int val)
    {
        this._field = val;
    }

    public override string ToString()
    {
        return "Field=" + this._field.ToString();
    }
}

我們以下列程式進行測試︰

// 建立 Reference Type 與 Value Type 的測試物件
DemoReferenceType refVar1 = new DemoReferenceType(1);
DemoValueType valVar1 = new DemoValueType(1);

// 將變數 1 的值,指派給變數 2
DemoReferenceType refVar2 = refVar1;
DemoValueType valVar2 = valVar1;

// 變更變數 2 的欄位值
refVar1.Field = 2;
valVar2.Field = 2;

// 印出︰
// refVar1 Field=2
// refVar2 Field=2
// valVar1 Field=1
// valVar2 Field=2
Console.WriteLine("refVar1 " + refVar1);
Console.WriteLine("refVar2 " + refVar2);
Console.WriteLine("valVar1 " + valVar1);
Console.WriteLine("valVar2 " + valVar2);

可以發現,在 Reference Type 與 Value Type 下,我們都是將變數一 (refVar1、valVar1) 的值指派給變數二 (refVar2、valVar2),並修改變數二的欄位值。但最後的結果卻不相同。

直覺上我們會變得 Reference Type 的情形不符直覺,明明我已經變數一(refVar1) 「指派」(assign) 給變數二 (refVar2) 了,為什麼我修改變數二的欄位值,看起來好像也影響到變數一的欄位值了呢?

這是因為兩者間所交換儲存的值不同,在「指派」這件事上,其實兩種型別做的事都是相同的,即「將變數一儲存的值,建立一個複本並儲存到變數二中」,只是 Value Type 建立的複本是資料本身,而 Reference Type 的複本是參考。

我們以圖例來進行說明,下圖是我們建立變數一時,記憶體中的配置方式︰

image

可以看到,所有的區域變數,都是儲存於 Stack 中,Reference Type 中儲存的是參考,內含是 Heap 中的記憶體位址。

而在建立變數二,並將變數一 (refVar1、valVar1) 的值指派給變數二 (refVar2、valVar2)後,記憶體中的配置如下︰

image

如同先前所說,「指派」這件事,不管在 Reference Type 或是 Value Type 都是一樣的,都是將變數一的儲存的值,複製一份到變數二。

而因為 Reference Type 中儲存的是參考,因此變數二 (refVar2) 會儲存與變數一 (refVar2) 相同的記憶體位址。也就是其實 refVar2 與 refVar1 指向的是在 Heap 上,相同的物件內容。

最後我們可以看看,同樣針對變數二的欄位做變更,在記憶體上的儲存情形︰

image

在此我們可以看到,由於 Reference Type 中的 refVar1 與 refVar2 中儲存的參考值相同,也就是兩者背後指向的是相同的物件內容。因此使用 refVar2 變數修改 Field 欄位內容,再取出 refVar1.Field 欄位值,兩者數值是相等的也就是完全合理的了。

最後整理一下重點如下︰

  • 在 C# 中,記憶體用途分為 Stack 與 Heap 兩種,所有的區域變數 (不管是 Value Type 或是 Reference Type) 都儲存於 Stack 下,使用 new 關鍵字實體化的類別實體,則儲存於 Heap 中
  • Value Type 儲存的是內容 (實值),Reference 儲存的是位址 (參考)
  • 由於 Value Type 與 Reference Type 在記憶體儲存值上的差異,在使用上若不理解,有時會造成意料外的問題

References