2019年8月6日 星期二

程式設計師應有的knowledges-物件導向-程式技術

第一部分:物件導向不夠用,泛型寫法就此誕生

要解決的問題:在寫物件導的Class的時候,我在NEW物件的時候才知道要給的資料型別,此時該如何實作?

解法一、寫介面,繼承後寫多形來含蓋所有可能的資料型態

解法二、使用泛型寫法

以下是介紹

泛型、型別參數、建構的型別




泛型可用於類別、介面、委派、和方法。這裡要先介紹的是宣告泛型類別的基本語法:



其中以角括弧 < > 符號包住的 T1, T2,..., Tn 即是所謂的型別參數(type parameters),也就是先前提過的「變動的部分」。型別參數可有一至多個,端看實際的需要而定。慣例上,型別參數的名稱通常以一個大寫英文字母 ‘T’ 開頭,如果只有一個型別參數,通常就單純命名為 “T”,例如:List<T>。若有多個型別參數,則建議使用望文生義的名稱,以便識別。像 .NET 的泛型集合 Dictionary<TKey, TValue> 就很清楚,一看就知道這個泛型集合所要儲存的每一筆資料都包含兩種元素:key(索引鍵)與value(資料值),而這兩種元素的真正型別,則是等到實際要使用集合物件的時候才以參數的方式帶入。

請注意「類別名稱<T>」並不是真正用來建立物件實體的類別,而只是一個類別的樣板(template)──你不能直接用類別樣板來建立物件實體。當我們在程式中使用泛型來建立物件時,必須指定欲帶入的型別參數,例如 Dictionary<int, string> 或 Dictionary<string, Employee> 等等,而這些有帶入型別參數的類別,才是程式執行時真正使用的類別。這些「真正的類別」有個正式稱呼,叫做「建構的型別」(constructed type)或「關閉的型別」(closed type)。相對的,由於在宣告泛型的時候會提供一些型別參數,開放讓外界指定實際要用的型別,故使用泛型語法所宣告的型別又稱為「開放的型別」(open type)。

泛型是型別的樣板

在學習物件導向程式語言的時候,有一個很常見的譬喻:類別就像是一張藍圖,而物件則是根據該藍圖所建立出來的實體(instance);只要先把藍圖設計好,我們就可以在程式中利用該藍圖來建立多個物件實體。同樣的譬喻也可以用來理解泛型。我們可以說,泛型就是類別的藍圖(樣板),根據此藍圖,便能夠產生多種建構的型別;然後,再利用這些建構的型別來建立物件實體。下圖描繪了泛型、建構的型別、與物件實體這三者之間的關係。


雖然您已經知道 .NET Framework 已經有提供現成的泛型串列,現在假設您因為某些特殊原因需要設計自己的泛型串列,並將它命名 MyGenericList。程式碼如下:


其中的 T 即為型別參數。您不妨與前面的 IntList 和 StringList 類別的程式碼對照一下,其中以黃色背景醒目標示者即為差異的部分。你可以看到,只要需要用到型別參數的部分,都是以型別參數名稱 T 來表示。等到真正需要使用建構的型別(constructed type)時,編譯器便會以外部指定的型別帶入參數 T,以產生真正的類別。完整起見,底下再列出實際建立物件時的程式範例:


此例中,根據泛型 MyGenericList 所建構的型別共有兩個,分別是 MyGenericList 和 MyGenericList,而程式便是利用這兩個建構的型別來各自建立不同的物件實體。圖3所示即為編譯器根據我們定義的泛型來產生建構型別的示意圖。圖的左方為泛型類別的宣告,右方為編譯器所產生的建構型別。當編譯器看到 MyGenericList 時,就會將型別 Employee 帶入泛型的型別參數 T,從而產生一個新的型別(即建構的型別),如圖中右邊的區塊所示。


注意:這裡的「編譯器」指的是 .NET 即時編譯器(just-in-time compiler),而非 C# 編譯器;這個部分會在稍後的<泛型與 C++ 樣板的差異>一節中進一步說明。
【資料引用資訊】
google關鍵字:泛型、型別參數、建構的型別




物件導向系列菜單11-『泛型(Generics)』


物件導向系列菜單11-『泛型(Generics)』

歡哥:師父~今天世足開幕囉!!!

陳夢吉:叫什麼~ 明天就看到我在上面踢了! 




什麼是泛型呢
什麼是泛型呢~? 這個東西<T>有看過吧,你有時後會在程式碼裡看到List<T>這種寫法,
這代表List串列中,你可以存放『T』型別,例如List<string>就是裡面存放string,List<int>就是裡面存放int。
<T>也被稱為型別參數。事實上我們可以將範例想成簡單點,那就是一個可以讓你自由決定型別的功能。


為什麼要有泛型呢
假設需要我們建立一個ArrayList,但確發現要取出值都需要轉型,這是因為當你將某個物件存入
ArrayList集合時,它的型別就隱含轉換成object了。只要是命名空間為system.Collection的集合類別都是如何(stack之類的)。


轉型是沒什麼問題,但在處理集合時,常常都會需要使用迴圈來取出或存入集合元素,假設迴歸數很大很大的話,那就會對程式的執行速度產生一定的影響了(不過不嚴重)。
還有一點就是,缺泛編輯時期的安全檢查。


泛型的優點
1.可以用不同的型別去做相同動作的事情
如我上述程式碼的這段

2.可以減少Boxing與UnBoxing,使效能增加。
3.讓程式碼更有彈性、重複使用程式碼。

泛型類別和方法
這邊寫個泛型的小範例,來知道一下大概的寫法。
下列程式碼建立Car類別,其中Class Car<T,T1>被稱為『泛型類別』,而T power 被稱為『泛型參數』。


輸出結果


類別參數的條件約束
既然泛型可以讓我們自由決定型別,但有時候太自由也不太好,那我們要著麼樣來約束呢??
如下,其中『where』就是就是限制的關鍵字,而『T』就是受限制的型別參數,最後Class就是限制的內容。


下列程式碼這為多參數限定


其中限制的內容請參考下圖,來源為MSDN。


我們這邊在來寫個簡單的小程式。
建立個Car類別,並且有二個型別參數T與T1,並限制T型別參數必預是『參數型別』。


然後我們用兩種寫法來測試看看,第一種為T指定為 string型別,第二種為T指定為int型別。
其中,string型別會正確執行,這邊別忘了string為參考型別喔! 而第二種int則會出錯。

注:如果不知道實值與參考是啥的可以參考一下小弟寫的這篇

未繫結的型別參數有幾項注意事項
1.無法使用!= 和 == 運算子,因為不能確定實體的型別引數是否會支援這些運算子。

2.這些參數可與System.Object相互轉換或明確轉換成任何介面型別。
3.你可以與NULL比較。如果將未繫結的型別參數與NULL比較,
那麼當型別引數為實值型別時一定會傳FALSE。



參考資料

 



小弟才書學淺~請各位客官指教指教~~~

小弟日 : 你要知道自已不是帥哥,你才有可能變成帥哥 。

【資料引用資訊】
google關鍵字:物件導向系列菜單11-『泛型(Generics)』

第二部分:物件導向概念-介面



[心得整理] c# 物件導向程式 - 3.抽象與interface


[心得整理] c# 物件導向程式 - 3.抽象與interface 

前言

物件導向程式加入了抽象的觀念,但我對於抽象觀念也卡關很久,想如何寫抽象文章也苦惱許久
本篇文章是我非常主觀的認知,若有偏差請多包含

現實生活中的抽象

最近喜歡用手機來比喻抽象,因為很貼近生活化
想請問讀者,若您要跟朋友借手機打電話時是用下列哪種說法
1. 小明,你的手機可以借我打回家一下嗎?
2. 小明,你的iphone 7S 可以借我打回家一下嗎?

應該大家都是選1吧,因為手機是抽象的,重點是在於我要可以打電話的裝置
至於是啥型號不是重點,用抽象的句子還可以用在任何人
另一種場合,當在電影院或是上課聽演講通常都會說
請將手機調靜音或轉振動,
而不會是說請iphone 4,4s,5,5s,hTC m8,m9........等調靜音或轉振動
一樣的理由,手機是抽象的
手機這抽象的詞表達的共識上一般包含打電話,可以開機關機,可以調整音量,轉振動
不需要指定到型號才會知道

程式的抽象

物件導向程式的抽象,我用interface (介面) 來說明
interface  快速記憶法
interface  就像是只會出一張嘴的老闆
只會簽合約自己卻不做事
先簡單介紹一下interface ,先看一下interface 的code
public interface I手機
{
    string 打電話(string 電話號碼);
    string 掛電話();
    string 調音量(int 音量大小);
    string 轉振動();
}
我建立一個interface 叫做I手機
我定義 四個方法簽章 (注意沒有實做的程式碼)msdn方法簽章
這樣就是interface 程式碼,只要繼承的class就必須要實做那四個方法
沒有實做的話,會建置失敗 (build不過,編譯器會去檢查)

那來完成一下class的程式碼

public class iphone4S : I手機
{
    public string 打電話(string 電話號碼)
    {
        Console.WriteLine("iOS打電話給"+ 電話號碼);
        return "撥號中";
    }

    public string 掛電話()
    {
        Console.WriteLine("iOS將通話中電話結束");
        return "結束通話";
    }

    public string 調音量(int 音量大小)
    {
        Console.WriteLine("iOS將音量調整為" + 音量大小);
        return "完成調音量";
    }

    public string 轉振動()
    {
        Console.WriteLine("iOS將轉振動");
        return "完成轉振動";
    }

    public string 呼叫Siri(string 指令)
    {
        //注意這是I手機沒有定義的方法
        return "Siri說" + 指令;
    }
}
來寫一段code說明差別
iphone4S myPhone = new iphone4S();
I手機 phone = new iphone4S();
比較兩個物件能使用的方法 會發現到new給I手機的phone沒有呼叫Siri方法對吧
因為I手機沒有定義呼叫Siri的方法簽章,所以不會出現,這樣可以理解嗎

這樣我在建立其他手機型號時,一樣去繼承I手機 就一定會有那四個方法(沒實做會build不過,編譯器會去檢查)
所以 我要打電話 或是 轉振動 就可以透過I手機 不一定要透過class(實際型號)

這樣介面就達到抽象的目的,我只需要I手機,不需要知道每一支手機型號
還記得三大特性嗎 介面用到繼承與多型結合起來,抽象的觀念應用

interface 可以當參數傳,可以當回傳值,可以用來當泛型約束條件,
一定要多使用或多寫練習code,這樣就容易上手了

PS.C#中不只介面可以抽象,還有一個更難以理解的抽象類別Abstract class,恩下次有時間聊

結語

還記得快速記憶法嗎?形容是否大家覺得貼切呢
介面在物件導向程式非常重要,接下來寫的物件導向介紹文章
會不停提到介面,希望這邊文章能夠先讓大家理解抽象觀念與介面
如果內容有誤請多鞭策謝謝
【資料引用資訊】
google關鍵字:[心得整理] c# 物件導向程式 - 3.抽象與interface

沒有留言:

張貼留言