1. 程式人生 > >C#基礎系列——小話泛型

C#基礎系列——小話泛型

前言:前面兩章介紹了C#的兩個常用技術:C#基礎系列——反射筆記 和 C#基礎系列——Attribute特性使用 。這一章來總結下C#泛型技術的使用。據博主的使用經歷,覺得泛型也是為了重用而生的,並且大部分時候會和反射一起使用。這次還是打算圍繞WWH(即What、Why、How)來講解。

1、什麼是泛型:通過引數化型別來實現在同一份程式碼上操作多種資料型別。利用“引數化型別”將型別抽象化,從而實現靈活的複用。怎麼理解呢,其實根據博主的理解,泛型就是將型別抽象化,使用抽象化的型別或物件去實現某些功能和業務,然後所有需要使用這些功能和業務的具體型別去呼叫泛型的方法和委託。呵呵,是不是還是有點暈,彆著急,我們來個例子:

我們首先來定義一種場景:我們通過sql語句使用Ado.Net來查詢預設得到的是弱型別的DataTable、DataReader等,而我們需要對查詢到的結果集使用lamada表示式進行某些複雜的計算,需要將DataTable轉換為對應的List<T>集合,首先來定義一個泛型的方法:

     public static List<T> GetListByDateTable<T>(DataTable dt)
        {
            List<T> modelList = new List<T>();
            
try { //1.如果DataTable沒有資料則直接返回 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; }

有了這個泛型的方法,我們在轉換DataTable和具體的List<Model>的時候是不是就是一個很好的複用。

2、為什麼要使用泛型:博主記得剛參加工作的前兩年有一次面試的時候就被問到“泛型有什麼優勢?”,當時怎麼回答的不記得了,只知道面試不太順利~~為什麼要用泛型呢?博主覺得泛型的主要優勢有以下幾點:

(1)保證了型別的安全性:泛型約束了變數的型別,保證了型別的安全性。例如List<int>和ArrayList。List<int>集合只能加入int型別的變數,ArrayList可以Add任何常用型別,編譯的時候不會提示錯誤。

(2)避免了不必要的裝箱、拆箱操作,提高程式的效能:泛型變數固定了型別,使用的時候就已經知道是值型別還是引用型別,避免了不必要的裝箱、拆箱操作。舉例說明:

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

object a=1;//由於是object型別,會自動進行裝箱操作。

int b=(int)a;//強制轉換,拆箱操作。這樣一去一來,當次數多了以後會影響程式的執行效率。

使用泛型之後

public static T GetValue<T>(T a)

{
  return a;
}

public static void Main()

{
  int b=GetValue<int>(1);//使用這個方法的時候已經指定了型別是int,所以不會有裝箱和拆箱的操作。
}

(3)提高方法、演算法的重用性。上面的例子基本能說明這個優勢。

3、泛型的使用:

(1)泛型方法的使用:這也是博主使用最多的用法之一,像這種泛型方法一般是一些static的通用方法,例如原來專案中用到的一個將DataGridViewRow物件轉換成對應的實體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的重寫是正確的,FuncB的重寫不正確,因為約束被預設繼承,不用再寫。
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)泛型介面以及泛型繼承的使用:

泛型介面的型別引數要麼已例項化,要麼來源於實現類宣告的型別引數

public interface Interface_Base<T>
{}

public class Class_Base<DTO, T> : Interface_Base<DTO>
{}

DTO來源於實現類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

型別引數必須是值型別。可以指定除 Nullable 以外的任何值型別。

T:class

型別引數必須是引用型別,包括任何類、介面、委託或陣列型別。

T:new()

型別引數必須具有無引數的公共建構函式。當與其他約束一起使用時,new() 約束必須最後指定。

T:<基類名>

型別引數必須是指定的基類或派生自指定的基類。

T:<介面名稱>

型別引數必須是指定的介面或實現指定的介面。可以指定多個介面約束。約束介面也可以是泛型的。

T:U

為 T 提供的型別引數必須是為 U 提供的引數或派生自為 U 提供的引數。這稱為裸型別約束.

相關推薦

C#基礎系列——

前言:前面兩章介紹了C#的兩個常用技術:C#基礎系列——反射筆記 和 C#基礎系列——Attribute特性使用 。這一章來總結下C#泛型技術的使用。據博主的使用經歷,覺得泛型也是為了重用而生的,並且大部分時候會和反射一起使用。這次還是打算圍繞WWH(即What、Why、How)來講解。 1、什麼是泛型

C#基礎系列

前言:這一章來總結下C#泛型技術的使用。據博主的使用經歷,覺得泛型也是為了重用而生的,並且大部分時候會和反射一起使用。這次還是打算圍繞WWH(即What、Why、How)來講解。 1、什麼是泛型:通過引數化型別來實現在同一份程式碼上操作多種資料型別。利用“引數化型別

C#基礎知識 簡單說明的優點

操作 自己 進行 ren pos body list() 而且 類型 有關泛型的優缺點在網上有很多篇文章,也足以說明問題,我就不去復制粘貼了(而且內容有些多),由於記性不太好,所以自己做個簡單明了的總結。 泛型的優點主要有兩個: “性能” “安全” 性能從何談起?很簡單

Java基礎系列:Java

一. 泛型概念的提出(為什麼需要泛型)? 首先,我們看下下面這段簡短的程式碼: public class GenericTest { public static void main(String[] args) { Lis

【JavaSE系列-基礎篇6】——方法

泛型方法是引入自己型別引數的方法。和宣告一個泛型型別是相似的,但是這個型別引數的範圍是在宣告的方法體內。靜態的和非靜態的泛型方法都是允許的,以及泛型類建構函式。 泛型方法的語法包括一個在菱形括號內的一個型別引數,並出現在方法返回型別之前。對於靜態方法來說,型別

快速入門系列--CLR--03集合

value mov nts readonly 只有一個 並且 cer view 工作 .NET中的泛型集合 在這裏主要介紹常見的泛型集合,很多時候其並發時的線程安全性常常令我們擔憂。因而簡述下.NET並發時線程安全特性,其詳情請見MSDN。 普通集合都不支持多重並發寫操

C#中Predicate與Func委托的用法實例

public pan html 加水印 pre wid bcf 委托 ora 本文以實例形式分析了C#中Predicate<T>與Func<T, bool>泛型委托的用法,分享給大家供大家參考之用。具體如下: 先來看看下面的例子:static vo

Java總結篇系列:Java

ech internal clone 傳遞 sta 是什麽 dom bar 依然 一. 泛型概念的提出(為什麽需要泛型)? 首先,我們看下下面這段簡短的代碼: 1 public class GenericTest { 2 3 public static

c#中的自定義類、方法和接口

泛型方法 return bsp 其中 tel sts code 方式 void ? 泛型的產生其中一個原因就是為了解決原來集合類中元素的裝箱和拆箱問題: 一、泛型類: /// <summary> /// 返回前臺的消息 /// &

c#基礎系列3---深入理解ref 和out

ref 聲明 函數的參數 .... -- 新增 tel struct 結果 “大菜”:源於自己剛踏入猿途混沌時起,自我感覺不是一般的菜,因而得名“大菜”,於自身共勉。 擴展閱讀 c#基礎系列1---深入理解 值類型和引用類型 c#基礎系列2---深入理解 Str

java基礎(3)-----

1.概述(什麼是泛型?) 泛型,即“引數化型別”,顧名思義,將具體的型別引數化,在呼叫的時候再傳入具體的型別 2.一個簡單的例子 public class GenericTest { public static void main(String[] args) {

java內功系列六(、異常、註解)

泛型: 1.泛型的出現主要解決了容器中元素放進去後直接變成了object型別。在編譯的時候如果傳入錯誤的元素將會出錯。 2.每次傳給泛型類、介面的型別引數不一樣相當於建立了一個新的類。list<string>和list以及list<int>是不一樣的型別。(邏輯上存在物理

Java基礎學習02——通過獲得Class,類似T.class

今天寫程式碼遇到了一個問題,我需要在通過泛型T獲取它的class,但是T.class不能用,所以才有了一下內容。 話不多說直接上程式碼 public abstract class BaseController<S extends BaseService<T> , T ex

C++Primer讀書筆記十——演算法.md

概述 在前一篇我們介紹了容器的基本概念以及使用其成員函式進行增刪改查,但有的時候我們還希望對容器進行更多的操作,比如:查詢特定元素,替換元素等。而標準庫並未給出此類成員函式, 此時需要引入algorithm標頭檔案,其中定義了一系列的操作演算法。 這些演算法不直

java基礎08_Collection集合及

主要內容 Collection 集合、 迭代器、 增強 for、 泛型 1. Collection 集合 集合 :集合是java中提供的一種容器,可以用來儲存多個數據。 集合和陣列既然都是容器,區別為: 陣列的長度是固定的。集合的長度是可變的。 陣列中儲存

Visual Studio2010新特性--C++王者歸來(3-程式設計-轉移建構函式

                泛型程式設計(generic programming)關注於產生通用的軟體元件,讓這些元件在不同的應用場合都能很容易地重用。在c++中,類模板和函式模板是進行泛型程式設計極為有效的機制。什麼是臨時物件?定義:當且僅當離開一段上下文(context)時在物件上執行的僅有的操作是解

C++迭代器和程式設計的理解

今天讀到了primer plus的泛型程式設計迭代器部分(16章第四小節),稍作記錄 前面閱讀的部分,講了很多繼承,友元,還有模板類的概念;這些都有一個共同點,那就是提高程式碼的重用性;當然友元也不僅僅是這一方面,還有在資料方面的保護和隱私許可權考慮等。從提高程式碼的重用性

C++ STL標準庫與程式設計(一)

泛型程式設計,就是使用模板為主要工具來編寫程式。其中沒有太多的面向物件的觀念,不涉及虛擬函式的使用。 使用C++標準庫 C++標準庫:以程式碼形式給出,放於各種標頭檔案( header files )內,經過編譯後才能使用。 所有新式的 headers 內的元件封裝於 namespace

JAVA基礎之集合、

今天我們來聊聊集合; 通常,我們的程式需要根據程式執行時才知道建立多少個物件。但若非程式執行,程式開發階段,我們根本不知道到底需要多少個數量的物件,甚至不知道它的準確型別。為了滿足這些常規的程式設計需要,我們要求能在任何時候,任何地點建立任意數量的物件,而這些物件用什麼來容納呢?我們首先

C#基礎系列:Attribute特性使用

前言:總結了下反射得基礎用法,這章我們來看看C#的另一個基礎技術——特性。 1、什麼是特性:就博主的理解,特性就是在類的類名稱、屬性、方法等上面加一個標記,使這些類、屬性、方法等具有某些統一的特徵,從而達到某些特殊的需要。比如:方法的異常捕捉,你是否還在某些可能出現