20170731-20170806C#工作學習周總結

多態

多態(polymorphism),是指計算機程序運行時,相同的消息可能會送給多個不同的類別之對象,而系統可依據對象所屬類別,引發對應類別的方法,而有不同的行爲。簡單來說,所謂多態意指相同的消息給予不同的對象會引發不同的動作稱之。

C#中主要通過虛方法、抽象方法和接口實現多態。

虛方法

當從父類中繼承的時候,虛函數和被繼承的函數具有相同的簽名。但是在運行過程中,運行系統將根據對象的類型,自動地選擇適當的具體實現運行。虛函數是面向對象編程實現多態的基本手段。在 C# 語言中, 對基類中的任何虛方法必須用 virtual 修飾, 而派生類中由基類繼承而來的重載方法必須用 override 修飾。

重寫:當一個子類繼承一父類,而子類中的方法與父類中的方法的名稱,參數個數、類型都完全一致時,就稱子類中的這個方法重寫了父類中的方法。

重載:一個類中的方法與另一個方法同名,但是參數表不同,這種方法稱之爲重載方法。

抽象方法和抽象類

抽象類被定義爲永遠不會也不能被實例化爲具體的對象。它往往用於定義一種抽象上的概念,在類的繼承關係中它往往被定義在較上層的位置。在程序設計的實踐活動中,抽象類與接口存在類似的地方,即它更偏重於對共通的方法和屬性進行規約。但與接口存在一個非常大的差異則在於,抽象類往往可以規約一個共同的方法和屬性時提供一個對他們的實現。

以現實世界爲例:”水果”可以算作一個抽象類,而”蘋果”,”香蕉”則可以作爲它的派生類。區別在於,”水果”是個概念,它不會有實例,但是”蘋果”和”香蕉”則肯定會有實例。

一個抽象類可以包含抽象和非抽象方法,當一個類繼承於抽象類,那麼這個派生類必須實現所有的
的基類抽象方法。

抽象方法:指一些只有方法聲明,而沒有具體方法體的方法。抽象方法一般存在於抽象類或界面中。

接口

通常可以用isas運算符確定對象實現了哪些接口。

接口的引用方法

爲了引用該接口,可以把對象類型強制轉換爲接口類型。

通常可以用isas運算符確定對象實現了哪些接口。

使用is的方法

這裏is並不是比較相同,是所屬範圍的關係,比如,可以說,img是水果,但img≠水果。這裏要特別注意。

foreach(Animal someAnimal in Zoo)
  {
    if(someAnimal is IHerbivore)
      {
        IHerbivore veggie = (IHebivore) someAnimal;
        veggie.GatherFood;
      }
  }

使用as方法

foreach(Animal someAnimal in Zoo)
{
  IHerbivore veggie = someAnimal as IHerbivore;
  if(veggie!=null)
  {
    veggie.EatPlant();
  }
}

is as操作符

is運行符不能重載,is運行符只考慮引用轉換、裝箱轉換和取消裝箱轉換。不考慮其它轉換,如果用戶定義轉換。

is運算符的左側不允許使用匿名方法。lambda表達式屬於例外。

is運算符通常像下面這樣使用:

if (myObject is Employee)
     {
           Employee myEmployee = (Employee)myObject;
     }

在這段代碼中,CLR實際會檢查兩次對象的類型。is運算符首先覈實myObject是否兼容於Employee類型。如果是,那麼在if語句內部執行轉換型,CLR會再次覈實myObject是否引用一個Employee。CLR的類型檢查增加了安全性,但這樣對性能造成一定影響。這是因爲CLR首先必須判斷變量(myObject)引用的對象的實際類型。然後,CLR必須遍歷繼承層次結構,用每個基類型去核對指定的類型(Employee)。由於這是一個相當常用的編程模式,所以C#專門提供了as運算符,目的就是簡化這種代碼寫法,同時提升性能。

as:用於檢查在兼容的引用類型之間執行某些類型的轉換。

Employee myEmployee = myObject as Employee;
    if (myEmployee != null)
    { }

在這段代碼中,CLR覈實myObject是否兼容於Employee類型;如果是,as會返回對同一個對象的一個非null的引用。如果myObject不兼容於Employee類型,as運算符會返回null。
注意:as運算符造成CLR只校驗一次對象的類型。if語句只是檢查myEmployee是否爲null。這個檢查的速度比校驗對象的類型快得多。

as運算符的工作方式與強制類型轉換一樣,只是它永遠不會拋出一個異常。相反,如果對象不能轉換,結果就是null。所以,正確的做法是檢查最終生成的一引用是否爲null。如果企圖直接使用最終生成的引用,會拋出一個System.NullReferenceException異常。以下代碼對此進行了演示:

Object o = new Object();        //新建一個Object對象。
Employee e = o as Employee;     //將o轉型爲一個Employee
e.ToString();                   //訪問e會拋出一個NullReferenceException異常

as運算符類似於強制轉換操作。但是無法進行轉換,則as返回null而非引發異常。

裝箱和拆箱(目前僅需瞭解)

裝箱和拆箱是值類型和引用類型之間相互轉換是要執行的操作。

裝箱:裝箱在值類型向引用類型轉換時發生;

拆箱:拆箱在引用類型向值類型轉換時發生。

C# Timer控件

Timer控件:Timer控件只有綁定了Tick事件,和設置Enabled=True後纔會自動計時,停止計時可以用Stop()控制,通過Stop()停止之後,如果想重新計時,可以用Start()方法來啓動計時器。Timer控件和它所在的Form屬於同一個線程。

Timer控件嵌入到用戶組件中後,在WinForm中調用不可使用,貌似是Timer控件的bug,需進一步驗證。

關於窗體

Program.cs

Application.Run(new Form1());

會將該窗體註冊爲主窗體,如果不Close()而只是Hide(),則會一直存在於內存中。

在使用C#進行Winform編程時,我們經常需要使用一個登錄框來進行登錄,一旦輸入的用戶名密碼登錄成功,這時登錄窗口應該關閉,而且同時打開主程序窗口。該如何來實現呢?

乍一想,很簡單啊,打開主窗口就用主窗口的Show()方法,而關閉登錄窗口就用登錄窗口的Close()方法即可。即代碼如下:

//Program.cs中代碼:
Application.Run(new FormLogin());
//登錄窗口(FormLogin)代碼:
private void button1_Click(object sender, EventArgs e)
{
    if (textBox1.Text == "a") {  //驗證用戶名密碼
        FormMain fm = New FormMain();
        fm.Show(); //打開主窗口
        this.Close();    //關閉登錄窗口
    }
}

事實證明,這種辦法是行不通的。因爲主窗口是由登錄窗口打開的,所以我們在關閉登錄窗口時,主窗口也會被一起關閉。這是一個線程樹,或者窗口樹的關係,即一個窗口關閉時,由它打開的新窗口都將被關閉。

所以,對於登錄窗體和主窗體之間的問題,可以利用以下方式來解決:

////Program.cs中代碼:
FormLogin fl = new FormLogin();
fl.ShowDialog();
if (fl.DialogResult == DialogResult.OK)
{
    Application.Run(new FormMain());
}
else
{
    return;
}
//登錄窗口(FormLogin)代碼:
private void button1_Click(object sender, EventArgs e)
{
    if (textBox1.Text == "aaa") {  //驗證用戶名密碼成功
        this.DialogResult = DialogResult.OK;    //返回一個登錄成功的對話框狀態
        this.Close();    //關閉登錄窗口
    }
}

獲取(重寫)上下左右鍵的點擊事件

private void Form3_KeyDown(object sender, KeyEventArgs e)
{
//this.KeyPreview = true;
//處理啓動窗體的 KeyPress 或 KeyDown 事件,將窗體的 KeyPreview 屬性設置爲 true,使鍵盤消息在到達窗體上的任何控件之前先被窗體接收。

this.Text = string.Format("e.KeyCode:{0}   e.KeyData: {0}   e.KeyValue: {1}", e.KeyCode, e.KeyData, e.KeyValue);

switch (e.KeyCode)
{
case Keys.W:
this.button1.Location = new Point(button1.Location.X, button1.Location.Y - 2); break;
case Keys.S:
this.button1.Location = new Point(button1.Location.X, button1.Location.Y + 2); break;
case Keys.A:
this.button1.Location = new Point(button1.Location.X - 2, button1.Location.Y); break;
case Keys.D:
this.button1.Location = new Point(button1.Location.X + 2, button1.Location.Y); break;

//方向鍵不能在KeyDown事件中被觸發,以下代碼無效。
//case Keys.Left:
//    this.button1.Location = new Point(this.button1.Location.X - 2, this.button1.Location.Y); break;
//case Keys.Right:
//    this.button1.Location = new Point(this.button1.Location.X + 2, this.button1.Location.Y); break;
default:
break;
}
}
protected override bool ProcessDialogKey(Keys keyData)
{
switch (keyData)
{
case Keys.Up: Move(0); break;
case Keys.Left: Move(1); break;
case Keys.Down: Move(2); break;
case Keys.Right: Move(3); break;
}

return true; //返回 true 以指示已處理該鍵。
//return base.ProcessDialogKey(keyData);
}

void Move(int i)
{
switch (i)
{
case 0: this.button1.Location = new Point(this.button1.Location.X, this.button1.Location.Y - 2); break;
case 1: this.button1.Location = new Point(this.button1.Location.X - 2, this.button1.Location.Y); break;
case 2: this.button1.Location = new Point(this.button1.Location.X, this.button1.Location.Y + 2); break;
case 3: this.button1.Location = new Point(this.button1.Location.X + 2, this.button1.Location.Y); break;
default: break;
}
}

在一個事件中調用另一個事件

//比如要在button_click事件中獲取pictureBox1_click事件,可以在button_click事件中寫入如下語句
pictureBox1_Click(pictureBox1, null);

播放聲音

SoundPlayer player = new SoundPlayer(@"C:\Users\M0015\Desktop\pic\sound\7 B.wav");
player.Play();

可以用上述方式播放wav格式的聲音文件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章