C#基礎(chǔ)系列——小話泛型
前言:前面兩章介紹了C#的兩個常用技術(shù):C#基礎(chǔ)系列——反射筆記 和 C#基礎(chǔ)系列——Attribute特性使用 。這一章來總結(jié)下C#泛型技術(shù)的使用。據(jù)博主的使用經(jīng)歷,覺得泛型也是為了重用而生的,并且大部分時候會和反射一起使用。這次還是打算圍繞WWH(即What、Why、How)來講解。
1、什么是泛型:通過參數(shù)化類型來實現(xiàn)在同一份代碼上操作多種數(shù)據(jù)類型。利用“參數(shù)化類型”將類型抽象化,從而實現(xiàn)靈活的復(fù)用。怎么理解呢,其實根據(jù)博主的理解,泛型就是將類型抽象化,使用抽象化的類型或?qū)ο笕崿F(xiàn)某些功能和業(yè)務(wù),然后所有需要使用這些功能和業(yè)務(wù)的具體類型去調(diào)用泛型的方法和委托。呵呵,是不是還是有點暈,別著急,我們來個例子:
我們首先來定義一種場景:我們通過sql語句使用Ado.Net來查詢默認(rèn)得到的是弱類型的DataTable、DataReader等,而我們需要對查詢到的結(jié)果集使用lamada表達(dá)式進(jìn)行某些復(fù)雜的計算,需要將DataTable轉(zhuǎn)換為對應(yīng)的List<T>集合,首先來定義一個泛型的方法:
public static List<T> GetListByDateTable<T>(DataTable dt) { List<T> modelList = new List<T>(); try { //1.如果DataTable沒有數(shù)據(jù)則直接返回 if (dt == null || dt.Rows.Count == 0) { return modelList; } //2.遍歷DataTable填充實體 var lstCol = dt.Columns; foreach (DataRow dr in dt.Rows) { T model = default(T); //如果是object(這種一般用于一個實體類表示不了的情況),則先拼接json再反序列化為object if (typeof(T).Equals(typeof(object))) { var strJson = "{"; foreach(DataColumn oCol in lstCol) { var oAttrValue = Convert.IsDBNull(dr[oCol.ColumnName]) ? null : dr[oCol.ColumnName]; strJson += "\"" + oCol.ColumnName + "\":\"" + oAttrValue + "\","; } strJson = strJson.ToString().Trim(',') + "}"; model = E2ERes.JavaScriptStrToObj<T>(strJson); } else { model = FillEntityByDT<T>(dt, dr); } modelList.Add(model); } } catch { } return modelList; } //通過DataTable填充實體類 private static T FillEntityByDT<T>(DataTable dt, DataRow dr) { T model = (T)typeof(T).GetConstructor(new System.Type[] { }).Invoke(new object[] { });//反射得到泛型類的實體 PropertyInfo[] pro = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public); Type type = model.GetType(); foreach (PropertyInfo propertyInfo in pro) { if (dt.Columns.Contains(propertyInfo.Name)) { if (Convert.IsDBNull(dr[propertyInfo.Name])) { continue; } if (!string.IsNullOrEmpty(Convert.ToString(dr[propertyInfo.Name]))) { type.GetProperty(propertyInfo.Name).SetValue(model, dr[propertyInfo.Name], null); } } } return model; }
有了這個泛型的方法,我們在轉(zhuǎn)換DataTable和具體的List<Model>的時候是不是就是一個很好的復(fù)用。
2、為什么要使用泛型:博主記得剛參加工作的前兩年有一次面試的時候就被問到“泛型有什么優(yōu)勢?”,當(dāng)時怎么回答的不記得了,只知道面試不太順利~~為什么要用泛型呢?博主覺得泛型的主要優(yōu)勢有以下幾點:
(1)保證了類型的安全性:泛型約束了變量的類型,保證了類型的安全性。例如List<int>和ArrayList。List<int>集合只能加入int類型的變量,ArrayList可以Add任何常用類型,編譯的時候不會提示錯誤。
(2)避免了不必要的裝箱、拆箱操作,提高程序的性能:泛型變量固定了類型,使用的時候就已經(jīng)知道是值類型還是引用類型,避免了不必要的裝箱、拆箱操作。舉例說明:
使用泛型之前,我們使用object代替。
object a=1;//由于是object類型,會自動進(jìn)行裝箱操作。 int b=(int)a;//強(qiáng)制轉(zhuǎn)換,拆箱操作。這樣一去一來,當(dāng)次數(shù)多了以后會影響程序的運(yùn)行效率。
使用泛型之后
public static T GetValue<T>(T a) { return a; } public static void Main() { int b=GetValue<int>(1);//使用這個方法的時候已經(jīng)指定了類型是int,所以不會有裝箱和拆箱的操作。 }
(3)提高方法、算法的重用性。上面的例子基本能說明這個優(yōu)勢。
3、泛型的使用:
(1)泛型方法的使用:這也是博主使用最多的用法之一,像這種泛型方法一般是一些static的通用方法,例如原來項目中用到的一個將DataGridViewRow對象轉(zhuǎn)換成對應(yīng)的實體Model的方法如下:
public static T ToObject<T>(DataGridViewRow item) where T:class { var model = item.DataBoundItem as T; if (model != null) return model; var dr = item.DataBoundItem as System.Data.DataRowView; model = (T)typeof(T).GetConstructor(new System.Type[] { }).Invoke(new object[] { });//反射得到泛型類的實體 PropertyInfo[] pro = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public); Type type = model.GetType(); foreach (PropertyInfo propertyInfo in pro) { if (Convert.IsDBNull(dr[propertyInfo.Name])) { continue; } if (!string.IsNullOrEmpty(Convert.ToString(dr[propertyInfo.Name]))) { var propertytype = propertyInfo.PropertyType; if (propertytype == typeof(System.Nullable<DateTime>) || propertytype == typeof(DateTime)) { type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDateTime(dr[propertyInfo.Name]), null); } else if (propertytype == typeof(System.Nullable<decimal>) || propertytype == typeof(decimal)) { type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDecimal(dr[propertyInfo.Name]), null); } else if (propertytype == typeof(System.Nullable<int>) || propertytype == typeof(int)) { type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToInt32(dr[propertyInfo.Name]), null); } else if (propertytype == typeof(System.Nullable<double>) || propertytype == typeof(double)) { type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDouble(dr[propertyInfo.Name]), null); } else { type.GetProperty(propertyInfo.Name).SetValue(model, dr[propertyInfo.Name], null); } } } return model; }
使用泛型方法的注意事項:
- 泛型方法的重載:public void Fun1<T>(T a);和public void Fun1<U>(U a);是無法重載的,這其實很好理解,因為T和U其實都是泛型的一個代表符號而已;
- 泛型方法的重寫:下面的方法重寫FuncA的重寫是正確的,F(xiàn)uncB的重寫不正確,因為約束被默認(rèn)繼承,不用再寫。
abstract class BaseClass { public abstract T FuncA<T,U>(T t,U u) where U:T; public abstract T FuncB<T>(T t) where T:IComparable; } class ClassA:BaseClass { public override X FuncA<X,Y>(X x,Y y){...} public override T FuncB<T>(T t) where T:IComparable{...} }
(2)泛型類的使用:
public class Class_Base<DTO, T> {}
使用這個類的時候必須要指定兩個泛型類。
(3)泛型接口以及泛型繼承的使用:
泛型接口的類型參數(shù)要么已實例化,要么來源于實現(xiàn)類聲明的類型參數(shù)
public interface Interface_Base<T> {} public class Class_Base<DTO, T> : Interface_Base<DTO> {}
DTO來源于實現(xiàn)類Class_Base
(4)泛型委托的使用:其實這種用法博主真的用得很少,只是原來見到過大牛們類似的代碼。
定義泛型委托:
public delegate void MyDelegate<T>(T obj);
泛型委托的使用:
public delegate void MyDelegate<T>(T obj); static void Main(string[] args) { var method = new MyDelegate<int>(printInt); method(1); Console.ReadKey(); } static void printInt(int i) { Console.WriteLine(i); }
(5)泛型約束:用來約束泛型類型有那些特性。常見的泛型約束也就那么幾類:
泛型約束的格式
public class Imps_Base<DTO, T> : Ifs_Base<DTO> where T : BaseEntity where DTO : class { }
| 約束 | 說明 |
|---|---|
|
T:struct |
類型參數(shù)必須是值類型。可以指定除 Nullable 以外的任何值類型。 |
|
T:class |
類型參數(shù)必須是引用類型,包括任何類、接口、委托或數(shù)組類型。 |
|
T:new() |
類型參數(shù)必須具有無參數(shù)的公共構(gòu)造函數(shù)。當(dāng)與其他約束一起使用時,new() 約束必須最后指定。 |
|
T:<基類名> |
類型參數(shù)必須是指定的基類或派生自指定的基類。 |
|
T:<接口名稱> |
類型參數(shù)必須是指定的接口或?qū)崿F(xiàn)指定的接口。可以指定多個接口約束。約束接口也可以是泛型的。 |
|
T:U |
為 T 提供的類型參數(shù)必須是為 U 提供的參數(shù)或派生自為 U 提供的參數(shù)。這稱為裸類型約束. |

浙公網(wǎng)安備 33010602011771號