2016年5月8日 星期日

使用16進位(hexadecimals)的時機與好處


寫程式的時候,有各種理由使用 16 進位。
其中一個理由是提醒工程師 - 我們要處理bits(用電腦世界的觀點),而不是處理數字(用人類世界的觀點)。使用 16 進位代表我們要處理相對低階的問題 (例如:記憶體資料的型式是必須被考慮)。

以下2個例子說明 16 進位使用時機:
[範例1]
 使用 bit shift (x << y) 比使用"乘法"更為清楚:
 1 << 10 = KB, 1 << 20 = MB, 1 << 30 = GB
 建立一個 16 KB 的array: var buffer = new byte[16 << 10]

[範例2]
 在 enum 當中, 項目是從 0 開始,而且依序加 1:
 使用 16 進位可以避免'負數'產生
 使用 16 進位可以使用bitwise AND (&) and OR (|) operators

 相關連結:
Why are flag enums usually defined with hexadecimal values: stackoverflow
使用16進位的好處是?? ptt

2016年5月4日 星期三

Implementation Patterns - Kent Beck (Chapter 6 State (狀態))


根據書上的建議,狀態的管理應該:
  1. 把相似的狀態放一起
  2. 把不同的狀態分離
要判斷兩個狀態(或是變數)的相似性,可以用下列方式:
  1. 他們在同一個 method 被用到
  2. 他們出現和消滅的時間相同
就以 MaterdiskAutoTool 的設計來當作例子看看設計...

[Case 1]:
MasterdiskAutoTool 會讀取 CM_Options.xml,來作為建立 Steps 的判斷。設計如下:


可以發現,Form 直接讀取 CM_Options.xml,然後再把 CM_Options.xml 的內容用參數的方式傳遞給其他的class (紅色框框的地方, 以及每個 Step)。
這種設計有下列缺點:
1.) 參數的傳遞路徑太長了,參數也太多了
2.) 這些參數透過Step的constructor傳進去變成step的全域變數,然後被Step裡面的 method 所使用,造成 method 裡面會有'全域變數'與'區域變數'交雜的情況,不容易閱讀
3.) 本質來說,Step 物件本身並不需要擁有這些參數,只有 Step 的 method 需要這些參數。'全域變數'與'區域變數'在此 method 的生命週期不一樣 (違反狀態管理原則)
4.) 因為Step 擁有CM_Options.xml的參數,當Step要增減這些參數時,必須修改很多地方  (:ex: constructor, 全域變數...)
5.) 不容易寫unit test (因為要準備太多參數當作input)

[Case 2]:
比較好的做法,是把讀取CM_Options.xml 的內容交給某個CMOptions class 處理 (橘色框框),然後物件向CMOptions class 要內容。
也就是說,CMOptions class只會出現在Step裡面的 method,Step 不會有CM_Options.xml 的內容變成全域變數 (生命週期一樣)。

修改後的設計如下:


這種設計可以避免上列的缺點,而且還有下列優點:
1.) CMOptions class只只需要出現在Step裡面的 method,Step不會有CM_Options.xml 的內容是全域變數 (因此 method 內的變數生命週期一樣)
2.) 一旦 CM_Options.xml 有變化,則不需要改變 MasterdiskAutoTool 的架構

[Case 3]:
由上面Case 2可以發現,幾乎所有Steps都必須使用 CMOptions class,造成關係太複雜。
可以再進一步改善一下: 讓 BaseStep 使用 CMOptions class,這樣每個Steps 就可以擁有 CMOptions class,免除每個Step自己使用CMOptions class。






Event 的意義

一個物件可以有三種東西:
  1. Property (ex: public string Name = "Peter";)
  2. Method (ex: public void Convert() {...} )
  3. Event (ex: public event EventHandler Click;)
一個物件擁有event, 它便可以通知其他物件"某件事情已經發生了"!
其中,一個 event 可以有多個 handlers 。 利用 event - handler 語法,我們可以建立一個 Notification system 。[註1] [註2]

若是要利用 event - handler 設計一個 Notification 機制,必須先確定下列3件事:
1. 誰有event,並且如何raise這個event
2. 誰handle這個event (也就是誰實作 callback function)
3. 兩者如何連結

以下用 Button 與 Form 做個例子:
1. Button 有 Clicked event,並且 user 透過 UI 來 raise 這個 event
2. Form 會 handle這個 Clicked event
3. Form 的 Constructor 實作把 button.Clicked 與 callback function 作連結 ( += )



再舉個 Queue 與 Producer 的例子:
當 Queue 有空位的時候,Queue物件 '通知' Producer物件進行 enqueue。(Queue 發出Event 給 Producer)
仿照 Button 與 Form 的設計如下:



1. Queue 有 NotifyToEnqueu 的 event,並且實作 OnNotifyToEnqueue() 讓 user (client code) 來raise 這個event,如下:

        public event EventHandler NotifyToEnqueue;

        protected virtual void OnNotifyToEnqueue()
        {
            if (NotifyToEnqueue != null) // null means no subscribe
            {
                NotifyToEnqueue(this, EventArgs.Empty); // raise event
                
                // Or, you can write like:
                // NotifyToEnqueue.Invoke(thisEventArgs.Empty);
            }
        }

2. Producer 會 handle 這個 NotifyToEnqueu event,如下:

        private void Queue_NotifyToEnqueue(object sender, EventArgs e)
        {
            m_MyQueue.Enqueue("aaa");
        }

3. Producer 的 Constructor 實作把 queue.NotifyToEnqueue 與 Queue_NotifyToEnqueue 作連結 (+= ), 如下:

public Producer(MyQueue queue)
        {
            m_MyQueue = queue;
            m_MyQueue.NotifyToEnqueue += Queue_NotifyToEnqueue; // subscribe
        }

所以,當呼叫 OnNotifyToEnqueue() 的時候, Producer物件就會進行 enqueue

[註1]
event 這個物件(ex: NotifyToEnqueue)的 invocations list 可用來儲存多種方法 (ex: Queue_NotifyToEnqueue方法)
要使用這些方法,只要呼叫 Invoke() 即可。 ex: NotifyToEnqueue.Invoke();

[註2]
An Event declaration adds a layer of abstraction and protection on the delegate instance.
This protection prevents clients of the delegate from resetting the delegate and its invocation list
and only allows adding or removing targets from the invocation list.

2016年5月1日 星期日

PlatformTarget 不同而造成的 Build warning

[Problem description]
在 Build C# 的過程當中,當某個 Project A 有 reference 到 Project B,如果這兩個 Projects 的 PlatformTarget 不同,則 MS-Build 結果有可能出現下列 Warning 訊息: (此 warning 不會出現在 VS-Build )

"warning MSB3270: There was a mismatch between the processor architecture of the project being build "MSIL" and the processor architecture of the reference "[internal C# dll]", "x86"."


[Root cause]
這是因為 兩個 Projects 的 Debug platform (或是 Release platform) 不相同所造成。如下圖所示:
MasterDiskAutoTool.Test.csproj 是一個 Unit test project,它會 reference 到 AutoTool.csproj。
然而,MasterDiskAutoTool.Test.csproj 的 release build platform 為 Any CPU,與 AutoTool.csproj 的 release build platform 的 x86 不相同。
因此,MS-Build 的結果有可能產生 Warning



[Solution]
把 MasterDiskAutoTool.Test.csproj 的 release build platform 改為 x86 (與 AutoTool.csproj 一樣),然後 MS-Build 就沒有Warning 了。
如下圖所示:




**PS: 另外一個因為Platforms 不同所造成的執行時期錯誤,請參閱 這裡


相關連結:
VS compile warning - “mismatch between processor architecture” stack overflow