虛擬函式跟一般函式一樣,唯一的差別是在於使用再"多型"上,當一個子類別想要使用和父類別相同名稱的函式時,也就是做覆寫的動作時,必須在父類別中將該函式前面加上 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月29日 星期五
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);
}
}
事件是由物件本身觸發, 而向外界發佈, 所以外界可以使用 += 識別符, 將方法(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日 星期三
自訂類別的陣列排序
我們都知道用int、long等基本形態所建立的一維陣列可以使用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.IComparable <People>
{
//內容完全與前面相同,在此省略
#region IComparable<People> 成員
//多實做了此方法
int IComparable<People>.CompareTo( People other)
{
return this.Age.CompareTo(other.Age); //這裡我們選擇以Age做為排序為基準
}
#endregion
}
今天我們建立了一個如下的類別:
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.IComparable
{
//內容完全與前面相同,在此省略
#region IComparable<People> 成員
//多實做了此方法
int IComparable<People>
{
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訊息"
讓程式有機會去處理
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;
}
{
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;
}
訂閱:
文章 (Atom)