在前一篇今晚我想來點基礎的資料結構 中我有提到關於泛型集合與boxing跟unboxing的一些概念,若是你不是很了解,希望你能花一點時間去看看~~
打到一半發現關於多載(overloading)沒有之前沒有提到
所以在本篇也會做補充
我們在方法,那是什麼能吃嗎 提到建立方法時需要宣告回傳類型與傳入參數(可以為空)
如下,我宣告了一個方法
input是int型態 output是string型態
public string EatApple(int num){
return "我吃了"+num+"個蘋果,傳入型態是int";
}
我們將input 與output 稱為signature(方法簽章)
透過不同的方法簽章
我們能夠用相同的方法名稱
執行不同的方法
舉例來說
我"再"新增一個一樣叫做EatApple的方法
input是string型態 output是string型態
public string EatApple(string input){
return "我吃了"+input+"個蘋果,傳入型態是string";
}
void Main(){
Console.WriteLine(EatApple(5));//我吃了5個蘋果,傳入型態是int
Console.WriteLine(EatApple("很多"));//我吃了很多個蘋果,傳入型態是string
}
我們同樣是呼叫EatApple的方法
但是實際上呼叫的方法卻不一樣
這種用法我們稱為多載(overloading)
注意
若是我們的回傳類型不同且傳入參數相同
編譯器會報錯
我再宣告一個方法
public int EatApple(int num){
return num;
}
當我們呼叫EatApple(5)的時候
系統會不知道你想要呼叫的是
public string EatApple(int num)
還是public int EatApple(int num)
我們繼續來吃蘋果
上面我們宣告了兩個方法來處理吃蘋果這件事
如果我們今天想要吃1.5顆蘋果
我們就得多新增一個方法
public string EatApple(float num){
return "我吃了"+input+"個蘋果;
}
所以我若是想吃3000000000(超過int)上限
我們就要再寫一個for long版本的
(當然你也可以一開始就用long 不用int,但依舊得寫浮點數及字串版本)
有沒有一種型別 能抓住所有類別的心~~
有 那就是object妳以為這裡的答案是泛型嗎
正如前篇今晚我想來點基礎的資料結構
所提過的所有型別的物件都是object
所以用object就能抓住所有類型
但是一樣的我們使用object的話
就會有boxing 與 unboxing 的問題
或是動態轉換的問題
除了降低效能還容易出錯
因此就有了泛型的出現
正如昨天所提
我們用Stack<T> 跟 Queue<T> 來使用集合泛型
這是因為我們習慣用T來表示泛型
回傳類型 方法名稱<T>(傳入參數){
//你的程式碼
}
public string EatApple<T>(T num){
return "我吃了"+num+"個蘋果,傳入型態是"+typeof(T);
}
<T>中的T為參數傳入型態
我們能使用typeof(T)來與得T的實際型態
void Main(){
Console.WriteLine(EatApple<int>(5));
//我吃了5個蘋果,傳入型態是System.Int32
Console.WriteLine(EatApple<string>("跟榴槤一樣大的一"));
//我吃了跟榴槤一樣大的一個蘋果,傳入型態是System.String
Console.WriteLine(EatApple<double>(1.5));
//我吃了1.5個蘋果,傳入型態是System.Double
}
如果今天有兩個泛型類別要傳入
public int Test<T1,T2>(T1 obj1,T2 obj2){
//你的程式碼
}
依此類推,當然T1T2只是代號 你也可以
public int Test<Doraemon,HelloKitty>(Doraemon obj1,HelloKitty obj2){
//你的程式碼
}
如果你真的要這麼做
建議使用在他們前面加個"T" //TDoraemon
比較符合命名規範
一樣能使用泛型
public T Example<T>(int input)
{
//code
}
有些時候你會想要你的泛型方法限定傳入某些型態的類別
通常會限制只能傳入某種介面或類別
這樣只有實作介面以及繼承這些類別的才能使用
(因為還沒講到介面跟類別 所以先看看就好)
使用where來限制
public T Example<T>() where T:class
{
//code
}
參閱泛型類型條件約束
泛型方法經常會搭配反射作使用
因為在你實際帶型態給泛型T之前
你並不知道T是什麼類型
假設你想對Int類型的特別作處理
可以用typeof(T) 來取得泛型的type
if(typeof(T)==typeof(int))
** 那今天我有一個 Class Apple **
我想透過泛型方法來取得蘋果熟了沒
//蘋果類別
public class Apple
{
public string Breed { get; set; }
public string Origin { get; set; }
public bool IsMature { get; set; }
}
Main
using System;
namespace GenericFun
{
class Program
{
static void Main(string[] args)
{
Apple apple = new Apple() {Breed = "Fuji", Origin = "Japan", IsMature = true};
//宣告一顆富士蘋果,來自日本,已經成熟
Console.WriteLine(CheckMature(apple));
}
public static bool CheckMature<T>(T input)
{
var value = typeof(T).GetProperty("IsMature").GetValue(input);
//取得input的IsMature欄位(取出來是object)
if (value.GetType() == typeof(bool))//確認取出的資料是bool
{
return (bool) value;
}
return false;
}
}
}
我們使用typeof(T).GetProperty來取得欄位
事實上typeof(T).GetProperties能取到更多的資訊
在反射中也經常使用
我這邊只稍微帶過
有興趣可以參閱[C#.NET] 使用反射(Reflection)對物件的結構進行操作 (一)及反射
事實上反射的效能極差 但是由於其便利性 所以還是經常會使用到
事實上泛型並沒有想像中的方便
由於你系統不知道你傳入的資料型別是什麼
所以該型別的方法屬性都不能使用(除非使用反射,但是反射效能差)
我們拿上方的蘋果作舉例
我們想要取得蘋果成熟沒必須大費周章
那我們今天傳入的是蘋果類別呢
public static bool CheckMature(Apple input)
{
return input.IsMature;
}
一行
沒了