2009年10月28日 星期三

C# Bitwise -- How to get status of bit in a value

C# Bitwise
一般看到Bitwise的文章裡都會提到 “位元(Bit)操作運算方式”會有:
1. OR ( | )
2. AND ( & )
3. XOR ( ^ )
4. NOT ( ~ )

基本的原則在此就不多提了,有興趣的人可以直接上Google上查Bitwise operator
這篇的主題主要是要針對 “對某個位元進行操作”, 白話文的意思是就, 有時可能會遇到必須對1個byte裡頭的某個bit行操作. 用以判斷該bit的狀態為 0 或是 1.
由於過去大多數在寫應用程式時都用不太上這種位元操作,所以搞了好一段時間才明白,並做一個簡單的整理.

1 Byte = 8 Bits

所以在二進位裡看到的樣子會像是以下的結構
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0


但由於我們在程式中取得到的值大多都是10進制 or 16進制. 所以在這個主題中我們先假設有一個值為 0x32 (16進制) 00110010 (2進制)
放到上述的表格裡頭之後會是以下的格式:
128 64 32 16 8 4 2 1
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
0 0 1 1 0 0 1 0

所以, 如果要將某個Bit的狀態取出的方法就會是:

int x = 0x32 & 128 (檢查bit 7的狀態)
if( x ==0 )
// 如果x 值為0時表示該bit 狀態為 0
else
// 如果 x 值不為0時表示該bit 狀態為 1

當然,也可以簡化一下寫成下面這個樣子
int x = 0x32 & 128
int y = (x==0)? 0 : 1;
這樣就可以輕鬆的取到期望的bit狀態.
最後, 如果要取其他bit怎麼辦? 答案是… 一個一個作唄.
int x = 0x32 & 1 //取bit 0的狀態
int x = 0x32 & 2 //取bit 1的狀態
int x = 0x32 & 4 //取bit 2的狀態
int x = 0x32 & 8 //取bit 3的狀態
int x = 0x32 & 16 //取bit 4的狀態
int x = 0x32 & 32 //取bit 5的狀態
int x = 0x32 & 64 //取bit 6的狀態
int x = 0x32 & 128 //取bit 7的狀態

這樣就可以把從 bit 0 ~ bit 7的狀態一一的取出進行判斷了.

2009年10月17日 星期六

C# 找零錢範例程式

這程式內容沒有什麼特別的地方,但是如果用一堆if判斷式會寫到瘋掉.. :P

if (iReturn < 0)
{
MessageBox.Show("錢不夠! 再給我");
return;
}

if (iReturn == 0) //如果應找的錢等於0的話,就不需藥再繼續執行,直接結束
{
MessageBox.Show("不用找錢");
return;
}

int[] MoneyTypes = { 2000,1000,500,200, 100 ,50,10,5,1};
string strMessage = "找零數目:";

iReturn = Math.Abs(iReturn); //這是確保找錢金額為 正數..沒有負數

for (int i = 0; i < MoneyTypes.Length; i++)
{
int iNum = 0;

while ((iReturn - MoneyTypes[i]) >= 0)
{
iNum = iNum + 1;
iReturn = iReturn - MoneyTypes[i];
}
switch (i)
{
case 0: // 2000
strMessage = strMessage + " 2000元 x " + iNum + "張\t";
break;
case 1: //1000
strMessage = strMessage + " 1000元 x " + iNum + "張\t";
break;
case 2: // 500
strMessage = strMessage + " 500元 x " + iNum + " 張\t";
break;
case 3: // 200
strMessage = strMessage + " 200元 x " + iNum+ " 張\t";
break;
case 4: // 100
strMessage = strMessage + " 100元 x " + iNum+ " 張\t";
break;
case 5: // 50
strMessage = strMessage + " 50元 x " + iNum + " 張\t";
break;
case 6: // 10
strMessage = strMessage + " 10元 x " + iNum+ " 張\t";
break;
case 7: // 5
strMessage = strMessage + " 5元 x " + iNum+ " 張\t";
break;
case 8: // 1
strMessage = strMessage + " 1元 x " + iNum+ " 張\t";
break;
}
}
strMessage 就是找錢出來的結果.

C# --Experience Timer Control Logic

最近正在進行一個RS-485通信資料的專案, 其中用到了常用到的Timer Control
原來的流程是這樣的..
step 1. 開啟Timer --> Timer1.Enabled = true
step 2. 等待Timer1_Tick事件觸發
step 3. 進入到Timer1_Tick事件後,由於不確定在這個Timer1_Tick事件中程式要執行多久才能將所有工作執行完畢. 所以在進入 Timer1_Tick事件後的第一件事就是將 Timer1.Enable 設定成false. 這樣理論上就能確保在Timer1_Tick 事件程序中的工作能完整的被執行完後才離開.
step 4. 當在Timer1_Tick事件程序中的工作都完成後便再將Timer1.Enable 設定成true. 這是為了讓Timer繼續的去執行. 之後程式就等待下一個Timer1_Tick事件被觸發.

當然,為了能夠提供使用者能自己控制這個 Timer1是否要開始計時, 所以在UI上得要提供一個Button來讓使用者操作Timer的Enable 為 true 或是false.
簡單的做法便是在Button_Click事件程序中寫一行:
Timer1.Enabled = !Timer1.Enabled.

經過以上的邏輯之後會發現好像就如同這麼簡單的方式可以被完成...

但, 是不幸的是... 上述的邏輯是不能被正常的完成上述所期望的工作.
經過執行的結果會發現.. 這個程式的Timer能被Enable 但無法被Disable.

原因是因為, Windows的程式是事件觸發.基本上,程式是一直處在一個等待的狀態. 而這個狀態除了是等待Timer1_Tick事件觸發之外,也等待使用者操作Button.. 等待Button_Click事件觸發.

在這個例子裡頭,原先所假定的Timer的Enable 或是 Disable 是透過Button_Click裡的 Timer1.Enabled = !Timer1.Enabled.
但是, 當使用者按下Button的同時, 程式有可能正在執行Timer1_Tick程序中的程式. 在這個Timer1_Tick程序中的最後一個步驟是... Timer1.Enabled = true;
所以會發生:

1.當使用者按下Button後, Button_Click程序中執行了Timer1.Enabled = !Timer1.Enabled (這裡先假定原先Timer1.Enabled = true 而在經過 !Timer1.Enable 則 Timer1.Enabled = false) 這表示.. 當使用者按下後Timer會被停止.
2. 在此同時,Timer1_Click事件被觸發了.. 第一個步驟是Timer1.Enabled = false (停止Timer)
3. 執行Timer1_Tick中所有的程式.
4.執行到最後一個步驟時.. Timer1.Enabled 要被設定成為true. (可是.. 之前我們明明己經按下Button讓Timer設定為停止的狀態) 這會兒又被設定為true. 結果就是真的執行的沒完沒了的.. 程式打死都停不下來.

要解決這個問題需要做些邏輯上的調整:
step 1. 開啟Timer --> Timer1.Enabled = true
step 2. 等待Timer1_Tick事件觸發
step 3. 進入到Timer1_Tick事件後,由於不確定在這個Timer1_Tick事件中程式要執行多久才能將所有工作執行完畢. 所以在進入 Timer1_Tick事件後的第一件事就是將 Timer1.Interval 設定成一個超長的時間. 這樣理論上就能確保在Timer1_Tick 事件程序中的工作能完整的被執行完後才會觸發下一個Timer_Tick事件.
step 4. 當在Timer1_Tick事件程序中的工作都完成後便再將Timer1.Interval 設定成一個超長的時間設定為"一個原先正常的時間" e.g 1000. 這是為了讓Timer繼續的去執行. 之後程式就等待下一個Timer1_Tick事件被觸發.

經過上述的邏輯調整後就能維持原來在Button_Click中的Timer.Enable=!Timer1.Enabled 的Timer啟停控制的機制.

這算是... 邏輯上的"誤區"吧...