电竞比分网-中国电竞赛事及体育赛事平台

分享

C#基礎(chǔ)系列:小話泛型

 weijianian 2016-08-07


來源:懶得安分

鏈接:http://www.cnblogs.com/landeanfen/p/4642820.html


前言:這一章來總結(jié)下C#泛型技術(shù)的使用。據(jù)博主的使用經(jīng)歷,覺得泛型也是為了重用而生的,并且大部分時(shí)候會(huì)和反射一起使用。這次還是打算圍繞WWH(即What、Why、How)來講解。


1、什么是泛型:通過參數(shù)化類型來實(shí)現(xiàn)在同一份代碼上操作多種數(shù)據(jù)類型。利用“參數(shù)化類型”將類型抽象化,從而實(shí)現(xiàn)靈活的復(fù)用。怎么理解呢,其實(shí)根據(jù)博主的理解,泛型就是將類型抽象化,使用抽象化的類型或?qū)ο笕?shí)現(xiàn)某些功能和業(yè)務(wù),然后所有需要使用這些功能和業(yè)務(wù)的具體類型去調(diào)用泛型的方法和委托。呵呵,是不是還是有點(diǎn)暈,別著急,我們來個(gè)例子:


我們首先來定義一種場(chǎng)景:我們通過sql語句使用Ado.Net來查詢默認(rèn)得到的是弱類型的DataTable、DataReader等,而我們需要對(duì)查詢到的結(jié)果集使用lamada表達(dá)式進(jìn)行某些復(fù)雜的計(jì)算,需要將DataTable轉(zhuǎn)換為對(duì)應(yīng)的List集合,首先來定義一個(gè)泛型的方法:


public static List GetListByDateTable(DataTable dt)
        {
            List modelList = new List();
            try
            {
               
//1.如果DataTable沒有數(shù)據(jù)則直接返回
                if (dt == null || dt.Rows.Count == 0)
                {
                    return modelList;
                }           
//2.遍歷DataTable填充實(shí)體
                var lstCol = dt.Columns;
                foreach (DataRow dr in dt.Rows)
                {
                    T model = default(T);
                   
//如果是object(這種一般用于一個(gè)實(shí)體類表示不了的情況),則先拼接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(strJson);
                    }
                    else
                    {
                        model = FillEntityByDT(dt, dr);
                    }
                    modelList.Add(model);
                }
            }
            catch
            {
 
            }
            return modelList;
        }
//通過DataTable填充實(shí)體類
        private static T FillEntityByDT(DataTable dt, DataRow dr)
        {
            T model = (T)typeof(T).GetConstructor(new System.Type[] { }).Invoke(new object[] { });
//反射得到泛型類的實(shí)體
            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;
        }


有了這個(gè)泛型的方法,我們?cè)谵D(zhuǎn)換DataTable和具體的List的時(shí)候是不是就是一個(gè)很好的復(fù)用。


2、為什么要使用泛型:博主記得剛參加工作的前兩年有一次面試的時(shí)候就被問到“泛型有什么優(yōu)勢(shì)?”,當(dāng)時(shí)怎么回答的不記得了,只知道面試不太順利~~為什么要用泛型呢?博主覺得泛型的主要優(yōu)勢(shì)有以下幾點(diǎn):


(1)保證了類型的安全性:泛型約束了變量的類型,保證了類型的安全性。例如List和ArrayList。List集合只能加入int類型的變量,ArrayList可以Add任何常用類型,編譯的時(shí)候不會(huì)提示錯(cuò)誤。


(2)避免了不必要的裝箱、拆箱操作,提高程序的性能:泛型變量固定了類型,使用的時(shí)候就已經(jīng)知道是值類型還是引用類型,避免了不必要的裝箱、拆箱操作。舉例說明:


使用泛型之前,我們使用object代替。


object a=1;
//由于是object類型,會(huì)自動(dòng)進(jìn)行裝箱操作。
 
int b=(int)a;
//強(qiáng)制轉(zhuǎn)換,拆箱操作。這樣一去一來,當(dāng)次數(shù)多了以后會(huì)影響程序的運(yùn)行效率。


使用泛型之后


public static T GetValue(T a)
{
  return a;
}
public static void Main()
{
  int b=GetValue(1);
//使用這個(gè)方法的時(shí)候已經(jīng)指定了類型是int,所以不會(huì)有裝箱和拆箱的操作。
}


(3)提高方法、算法的重用性。上面的例子基本能說明這個(gè)優(yōu)勢(shì)。


3、泛型的使用:


(1)泛型方法的使用:這也是博主使用最多的用法之一,像這種泛型方法一般是一些static的通用方法,例如原來項(xiàng)目中用到的一個(gè)將DataGridViewRow對(duì)象轉(zhuǎn)換成對(duì)應(yīng)的實(shí)體Model的方法如下:


public static T ToObject(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[] { });
//反射得到泛型類的實(shí)體
            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) || propertytype == typeof(DateTime))
                    {
                        type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDateTime(dr[propertyInfo.Name]), null);
                    }
                    else if (propertytype == typeof(System.Nullable) || propertytype == typeof(decimal))
                    {
                        type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToDecimal(dr[propertyInfo.Name]), null);
                    }
                    else if (propertytype == typeof(System.Nullable) || propertytype == typeof(int))
                    {
                        type.GetProperty(propertyInfo.Name).SetValue(model, Convert.ToInt32(dr[propertyInfo.Name]), null);
                    }
                    else if (propertytype == typeof(System.Nullable) || 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;
        }


使用泛型方法的注意事項(xiàng):


  • 泛型方法的重載:public void Fun1(T a);和public void Fun1(U a);是無法重載的,這其實(shí)很好理解,因?yàn)門和U其實(shí)都是泛型的一個(gè)代表符號(hào)而已;


  • 泛型方法的重寫:下面的方法重寫FuncA的重寫是正確的,F(xiàn)uncB的重寫不正確,因?yàn)榧s束被默認(rèn)繼承,不用再寫。



abstract class BaseClass
{
    public abstract T FuncA(T t,U u) where U:T;
    public abstract T FuncB(T t) where T:IComparable;
}
class ClassA:BaseClass
{
    public override X FuncA(X x,Y y){...}
    public override T FuncB(T t) where T:IComparable{...}
}


(2)泛型類的使用:


public class Class_Base
{}


使用這個(gè)類的時(shí)候必須要指定兩個(gè)泛型類。


(3)泛型接口以及泛型繼承的使用:


泛型接口的類型參數(shù)要么已實(shí)例化,要么來源于實(shí)現(xiàn)類聲明的類型參數(shù)


public interface Interface_Base
{}
public class Class_Base : Interface_Base
{}


DTO來源于實(shí)現(xiàn)類Class_Base


(4)泛型委托的使用:其實(shí)這種用法博主真的用得很少,只是原來見到過大牛們類似的代碼。


定義泛型委托:


public delegate void MyDelegate(T obj);


泛型委托的使用:


public delegate void MyDelegate(T obj);
static void Main(string[] args)
{
    var method = new MyDelegate(printInt);
    method(1);
    Console.ReadKey();
}
static void printInt(int i)
{
    Console.WriteLine(i);
}


(5)泛型約束:用來約束泛型類型有那些特性。常見的泛型約束也就那么幾類:

泛型約束的格式


public class Imps_Base : Ifs_Base
        where T : BaseEntity
        where DTO : class
    {
  }




    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多