2008年8月29日 星期五

虛擬函式 與 純虛擬函式

虛擬函式跟一般函式一樣,唯一的差別是在於使用再"多型"上,當一個子類別想要使用和父類別相同名稱的函式時,也就是做覆寫的動作時,必須在父類別中將該函式前面加上 virtual 用以辨識,表示該函式是可以被覆寫的,而此函式稱為虛擬函式。
class A
{
 public:
  //一般函式
 int Test()
 {
  return 0;
 }
}

class A
{
 public:
  //虛擬函式
 virtual int Test()
 {
  return 0;
 }
}

另外有另一種不提供函式內容的虛擬函式稱為"純"虛擬函式,只要類別中有一個以上的純虛擬函式,該類別就稱為抽象類別,抽象類別只能被拿來繼承,無法直接產生物件(實例),而繼承該類別的類別,必須實做那些函式。純虛擬函式與虛擬函式的宣告方式差異,除了純虛擬含是不定義函式內容以外,必須在函式後面加上=0
class A
{
 public:
 
//純虛擬函式
 virtual int Test()=0;
}

2008年8月22日 星期五

事件(Event) 與 回呼( Callback ) 的不同

事件(Event) 與 回呼( Callback ) 看起來很像, 運作的方式也差不多,都是透過 deleget 來進行委派, 但使用的方式有一點點不同. 其實應該說是角度上的不同.

事件是由物件本身觸發, 而向外界發佈, 所以外界可以使用 += 識別符, 將方法(method)的委派掛上去, 而事件一般來說都是public的.

回呼並不向外界發佈, 而是透過其他方式取得要處理的方法的委派, 通常一個回呼指向一個方法

以下是"事件"的使用方式:
public delegate void TestHandler(string msg);
class ClassA
{
  public event TestHandler TestEvent;  
  public void Invoke()
  {
    TestEvent("Hello Event");
  }
}

class MainClass
{
  public ClassA cA = new ClassA();
  public MainClass()
  {
    this.cA += new TestHandler(cA_TestEvent);
  }
  private void cA_TestEvent(string msg)
  {
    Message.Show(msg);
  }
}

以下是"回呼"的使用方式
public delegate void TestHandler(string msg);
class ClassA
{
  private TestHandler TestCallback;
  public void RegistCallback(TestHandler callback)
  {
   this.TestCallback = callback;
  }
  public void Invoke()
  {
    TestCallback("Hello Callback");
  }

}

class MainClass
{
  public ClassA cA = new ClassA();
  public MainClass()
  {
    TestHandler myCallback = new TestHandler(cA_Callback);
    this.cA.RegistCallback(myCallback );
  }
  private void cA_Callback(string msg)
  {
    Message.Show(msg);
  }
}

2008年8月20日 星期三

自訂類別的陣列排序

我們都知道用intlong等基本形態所建立的一維陣列可以使用Array.Sort()來做排序的動作
今天我們建立了一個如下的類別:
public class People
{
  private int age = 0;
  private string name = string.Empty;
  public int Age
  {
   get { return age; }
   set
   {
    if ( age >= 0 )
     age = value;
   }
  }
  public string Name
  {
   get { return name; }
   set { name = value; }
  }
}
如果我們希望能夠使用Array.Sort()來排序此資料形態的陣列
ex:
People[] peoples = new People[5];
Array.Sort(peoples);

此時會出現 "無法比較陣列中的兩個元素。" 的例外
因為我們並沒有指定要排序的條件
例如是照age或是name來排序
因此我們必須讓People類別實做 System.IComparable 介面
實作後的類別如下:
public class People:System.IComparablePeople
{
  //內容完全與前面相同,在此省略

  #region IComparable<People> 成員

  
//多實做了此方法
  int IComparable
People.CompareTo(People other)
  {
   return this.
Age.CompareTo(other.Age); //這裡我們選擇以Age做為排序為基準
  }

  #endregion
}

使用"介面"限制那麼多 為何還要使用?

我們都知道一旦某個類別實做了某個介面,那就必須實做那個介面的所有方法及屬性,等於是強迫中獎,而且也無法少寫一些程式碼。
那介面的好處倒底何在?

其實介面只是提供一種方式,讓你可以控制一群做相同事情(方法),但類型不同的物件

2008年8月19日 星期二

使用Application.DoEvents(); 處理windows訊息佇列

此程式片段用來呈現Form背景七彩閃爍的效果

while (this.Visible)

{
  for (int c = 0; (c <>this.Visible); c++)
  {
    this.BackColor = Color.FromArgb(c, 255 - c, c);
    Application.DoEvents();
    System.Threading.Thread.Sleep(1);
  }

  for (int c = 254; (c >= 0) && (this.Visible); c--)
  {
    this.BackColor = Color.FromArgb(c, 255 - c, c);
    Application.DoEvents();
    System.Threading.Thread.Sleep(3);
  }
}

若沒有加上 Application.DoEvents();
則會發現不但背景顏色不會改變
且整個視窗都會當掉, 那是因為進入無線迴圈
視窗根本沒機會去處理重繪的訊息
因為所有關於視窗控制觸發的訊息, 都堆在windows訊息佇列上
所以我們必須在回圈內加上"處理windows訊息"
讓程式有機會去處理

2008年8月4日 星期一

Byte Array 與 Structure 互轉

static void ByteArrayToStructure(byte[] bytearray, ref object obj)
{
int len = Marshal.SizeOf(obj);
IntPtr i = Marshal.AllocHGlobal(len);
Marshal.Copy(bytearray, 0, i, len);
obj = Marshal.PtrToStructure(i, obj.GetType());
Marshal.FreeHGlobal(i);
}

static byte[] StructureToByteArray(object obj)
{
int len = Marshal.SizeOf(obj);
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}