C#的泛型方法解析
C#2.0引入了泛型這個(gè)特性,由于泛型的引入,在一定程度上極大的增強(qiáng)了C#的生命力,可以完成C#1.0時(shí)需要編寫(xiě)復(fù)雜代碼才可以完成的一些功能。但是作為開(kāi)發(fā)者,對(duì)于泛型可謂是又愛(ài)又恨,愛(ài)的是其強(qiáng)大的功能,以及該特性帶來(lái)的效率的提升,恨的是泛型在復(fù)雜的時(shí)候,會(huì)呈現(xiàn)相當(dāng)復(fù)雜的語(yǔ)法結(jié)構(gòu)。這種復(fù)雜不僅是對(duì)于初學(xué)者,對(duì)于一些有開(kāi)發(fā)經(jīng)驗(yàn)的.NET開(kāi)發(fā)者,也是一個(gè)不那么容易掌握的特性。
接下來(lái)我們來(lái)了解一下C#2.0加入的特性:泛型。
一.泛型的基本特性概述:
在實(shí)際項(xiàng)目開(kāi)發(fā)中,任何API只要將object作為參數(shù)類(lèi)型和返回類(lèi)型使用,就可能在某個(gè)時(shí)候涉及強(qiáng)類(lèi)型轉(zhuǎn)換。提到強(qiáng)類(lèi)型轉(zhuǎn)換,估計(jì)很多開(kāi)發(fā)者第一反應(yīng)就是“效率”這個(gè)次,對(duì)于強(qiáng)類(lèi)型的利弊主要看使用者使用的環(huán)境,天底下沒(méi)有絕對(duì)的壞事,也沒(méi)有絕對(duì)的好事,有關(guān)強(qiáng)類(lèi)型的問(wèn)題不是本次的重點(diǎn),不做重點(diǎn)介紹。
泛型是CLR和C#提供的一種特殊機(jī)制,支持另一種形式的代碼重用,即“算法重用”。泛型實(shí)現(xiàn)了類(lèi)型和方法的參數(shù)化,泛型類(lèi)型和方法也可以讓參數(shù)告訴使用者使用什么類(lèi)型。
泛型所帶來(lái)的好處:更好的編譯時(shí)檢查,更多在代碼中能直接表現(xiàn)的信息,更多的IDE支持,更好的性能??赡苡腥藭?huì)疑問(wèn),為什么泛型會(huì)帶來(lái)這么多好處,使用一個(gè)不能區(qū)分不同類(lèi)型的常規(guī)API,相當(dāng)于在一個(gè)動(dòng)態(tài)環(huán)境中訪問(wèn)那個(gè)API。
CLR允許創(chuàng)建泛型引用和泛型值類(lèi)型,但是不允許創(chuàng)建泛型枚舉,并且CLR允許創(chuàng)建泛型接口和泛型委托,CLR允許在引用類(lèi)型、值類(lèi)型或接口中定義泛型方法。定義泛型類(lèi)型或方法時(shí),為類(lèi)型指定了任何變量(如:T)都稱(chēng)為類(lèi)型參數(shù)。(T是一個(gè)變量名,在源代碼中能夠使用一個(gè)數(shù)據(jù)類(lèi)型的任何位置,都可以使用T)在C#中泛型參數(shù)變量要么成為T(mén),要么至少一大寫(xiě)T開(kāi)頭。
二.泛型類(lèi)、泛型接口和泛型委托概述:
1.泛型類(lèi):
泛型類(lèi)型仍然是類(lèi)型,所以可以從任何類(lèi)型派生。使用一個(gè)泛型類(lèi)型并指定類(lèi)型實(shí)參時(shí),實(shí)際是在CLR中定義一個(gè)新類(lèi)型對(duì)象,新類(lèi)型對(duì)象是從泛型派生自的那個(gè)類(lèi)型派生的。使用泛型類(lèi)型參數(shù)的一個(gè)方法在基尼險(xiǎn)那個(gè)JIT編譯時(shí),CLR獲取IL,用指定的類(lèi)型實(shí)參進(jìn)行替換,然后創(chuàng)建恰當(dāng)?shù)谋镜卮a。
如果沒(méi)有為泛型類(lèi)型參數(shù)提供類(lèi)型實(shí)參,那就么就是未綁定泛型類(lèi)型。如果指定了類(lèi)型實(shí)參,該類(lèi)型就是已構(gòu)造類(lèi)型。已構(gòu)造類(lèi)型可以是開(kāi)發(fā)或封閉的,開(kāi)發(fā)類(lèi)型還包含一個(gè)類(lèi)ixngcanshu,而封閉類(lèi)型則不是開(kāi)發(fā)的,類(lèi)型的每個(gè)部分都是明確的。所有代碼實(shí)際都是在一個(gè)封閉的已構(gòu)造類(lèi)型的上下文中執(zhí)行。
泛型類(lèi)在.NET的應(yīng)用主要在集合類(lèi)中,大多數(shù)集合類(lèi)在System.Collections.Generic和System.Collections.ObjectModel類(lèi)中。下面簡(jiǎn)單的介紹一種泛型集合類(lèi):
(1).SynchronizedCollection:提供一個(gè)線程安全集合,其中包含泛型參數(shù)所指定類(lèi)型的對(duì)象作為元素.
[ComVisible(false)]
public class SynchronizedCollection<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
{
/// <summary>
/// 初始化 <see cref="T:System.Collections.Generic.SynchronizedCollection`1"/> 類(lèi)的新實(shí)例。
/// </summary>
public SynchronizedCollection();
/// <summary>
/// 通過(guò)用于對(duì)線程安全集合的訪問(wèn)進(jìn)行同步的對(duì)象來(lái)初始化 <see cref="T:System.Collections.Generic.SynchronizedCollection`1"/> 類(lèi)的新實(shí)例。
/// </summary>
/// <param name="syncRoot">用于對(duì)線程安全集合的訪問(wèn)進(jìn)行同步的對(duì)象。</param><exception cref="T:System.ArgumentNullException"><paramref name="syncRoot"/> 為 null。</exception>
public SynchronizedCollection(object syncRoot);
/// <summary>
/// 使用指定的可枚舉元素列表和用于對(duì)線程安全集合的訪問(wèn)進(jìn)行同步的對(duì)象來(lái)初始化 <see cref="T:System.Collections.Generic.SynchronizedCollection`1"/> 類(lèi)的新實(shí)例。
/// </summary>
/// <param name="syncRoot">用于對(duì)線程安全集合的訪問(wèn)進(jìn)行同步的對(duì)象。</param><param name="list">用于初始化線程安全集合的元素的 <see cref="T:System.Collections.Generic.IEnumerable`1"/> 集合。</param><exception cref="T:System.ArgumentNullException"><paramref name="syncRoot"/> 或 <paramref name="list"/> 為 null。</exception>
public SynchronizedCollection(object syncRoot, IEnumerable<T> list);
/// <summary>
/// 使用指定的元素?cái)?shù)組和用于對(duì)線程安全集合的訪問(wèn)進(jìn)行同步的對(duì)象來(lái)初始化 <see cref="T:System.Collections.Generic.SynchronizedCollection`1"/> 類(lèi)的新實(shí)例。
/// </summary>
/// <param name="syncRoot">用于對(duì)線程安全集合的訪問(wèn)進(jìn)行同步的對(duì)象。</param><param name="list">用于初始化線程安全集合的 <paramref name="T"/> 類(lèi)型元素的 <see cref="T:System.Array"/>。</param><exception cref="T:System.ArgumentNullException"><paramref name="syncRoot"/> 或 <paramref name="list"/> 為 null。</exception>
public SynchronizedCollection(object syncRoot, params T[] list);
/// <summary>
/// 將項(xiàng)添加到線程安全只讀集合中。
/// </summary>
/// <param name="item">要添加到集合的元素。</param><exception cref="T:System.ArgumentException">設(shè)置的值為 null,或者不是集合的正確泛型類(lèi)型 <paramref name="T"/>。</exception>
public void Add(T item);
/// <summary>
/// 從集合中移除所有項(xiàng)。
/// </summary>
public void Clear();
/// <summary>
/// 從特定索引處開(kāi)始,將集合中的元素復(fù)制到指定的數(shù)組。
/// </summary>
/// <param name="array">從集合中復(fù)制的 <paramref name="T "/>類(lèi)型元素的目標(biāo) <see cref="T:System.Array"/>。</param><param name="index">復(fù)制開(kāi)始時(shí)所在的數(shù)組中的從零開(kāi)始的索引。</param>
public void CopyTo(T[] array, int index);
/// <summary>
/// 確定集合是否包含具有特定值的元素。
/// </summary>
///
/// <returns>
/// 如果在集合中找到元素值,則為 true;否則為 false。
/// </returns>
/// <param name="item">要在集合中定位的對(duì)象。</param><exception cref="T:System.ArgumentException">設(shè)置的值為 null,或者不是集合的正確泛型類(lèi)型 <paramref name="T"/>。</exception>
public bool Contains(T item);
/// <summary>
/// 返回一個(gè)循環(huán)訪問(wèn)同步集合的枚舉數(shù)。
/// </summary>
///
/// <returns>
/// 一個(gè) <see cref="T:System.Collections.Generic.IEnumerator`1"/>,用于訪問(wèn)集合中存儲(chǔ)的類(lèi)型的對(duì)象。
/// </returns>
public IEnumerator<T> GetEnumerator();
/// <summary>
/// 返回某個(gè)值在集合中的第一個(gè)匹配項(xiàng)的索引。
/// </summary>
///
/// <returns>
/// 該值在集合中的第一個(gè)匹配項(xiàng)的從零開(kāi)始的索引。
/// </returns>
/// <param name="item">從集合中移除所有項(xiàng)。</param><exception cref="T:System.ArgumentException">設(shè)置的值為 null,或者不是集合的正確泛型類(lèi)型 <paramref name="T"/>。</exception>
public int IndexOf(T item);
/// <summary>
/// 將一項(xiàng)插入集合中的指定索引處。
/// </summary>
/// <param name="index">要從集合中檢索的元素的從零開(kāi)始的索引。</param><param name="item">要作為元素插入到集合中的對(duì)象。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的項(xiàng)數(shù)。</exception><exception cref="T:System.ArgumentException">設(shè)置的值為 null,或者不是集合的正確泛型類(lèi)型 <paramref name="T"/>。</exception>
public void Insert(int index, T item);
/// <summary>
/// 從集合中移除指定項(xiàng)的第一個(gè)匹配項(xiàng)。
/// </summary>
///
/// <returns>
/// 如果從集合中成功移除了項(xiàng),則為 true;否則為 false。
/// </returns>
/// <param name="item">要從集合中移除的對(duì)象。</param>
public bool Remove(T item);
/// <summary>
/// 從集合中移除指定索引處的項(xiàng)。
/// </summary>
/// <param name="index">要從集合中檢索的元素的從零開(kāi)始的索引。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的項(xiàng)數(shù)。</exception>
public void RemoveAt(int index);
/// <summary>
/// 從集合中移除所有項(xiàng)。
/// </summary>
protected virtual void ClearItems();
/// <summary>
/// 將一項(xiàng)插入集合中的指定索引處。
/// </summary>
/// <param name="index">集合中從零開(kāi)始的索引,在此處插入對(duì)象。</param><param name="item">要插入到集合中的對(duì)象。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的項(xiàng)數(shù)。</exception><exception cref="T:System.ArgumentException">設(shè)置的值為 null,或者不是集合的正確泛型類(lèi)型 <paramref name="T"/>。</exception>
protected virtual void InsertItem(int index, T item);
/// <summary>
/// 從集合中移除指定 <paramref name="index"/> 處的項(xiàng)。
/// </summary>
/// <param name="index">要從集合中檢索的元素的從零開(kāi)始的索引。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的項(xiàng)數(shù)。</exception>
protected virtual void RemoveItem(int index);
/// <summary>
/// 使用另一項(xiàng)替換指定索引處的項(xiàng)。
/// </summary>
/// <param name="index">要替換的對(duì)象的從零開(kāi)始的索引。</param><param name="item">要替換的對(duì)象。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的項(xiàng)數(shù)。</exception>
protected virtual void SetItem(int index, T item);
/// <summary>
/// 返回一個(gè)循環(huán)訪問(wèn)同步集合的枚舉數(shù)。
/// </summary>
///
/// <returns>
/// 一個(gè) <see cref="T:System.Collections.Generic.IEnumerator`1"/>,用于訪問(wèn)集合中存儲(chǔ)的類(lèi)型的對(duì)象。
/// </returns>
IEnumerator IEnumerable.GetEnumerator();
/// <summary>
/// 從特定索引處開(kāi)始,將集合中的元素復(fù)制到指定的數(shù)組。
/// </summary>
/// <param name="array">從集合中復(fù)制的 <paramref name="T"/> 類(lèi)型元素的目標(biāo) <see cref="T:System.Array"/>。</param><param name="index">復(fù)制開(kāi)始時(shí)所在的數(shù)組中的從零開(kāi)始的索引。</param>
void ICollection.CopyTo(Array array, int index);
/// <summary>
/// 向集合中添加一個(gè)元素。
/// </summary>
///
/// <returns>
/// 新元素的插入位置。
/// </returns>
/// <param name="value">要添加到集合中的對(duì)象。</param>
int IList.Add(object value);
/// <summary>
/// 確定集合是否包含具有特定值的元素。
/// </summary>
///
/// <returns>
/// 如果在集合中找到元素 <paramref name="value"/>,則為 true;否則為 false。
/// </returns>
/// <param name="value">要在集合中定位的對(duì)象。</param><exception cref="T:System.ArgumentException"><paramref name="value"/> 不是集合所含類(lèi)型的對(duì)象。</exception>
bool IList.Contains(object value);
/// <summary>
/// 確定集合中某個(gè)元素的從零開(kāi)始的索引。
/// </summary>
///
/// <returns>
/// 如果在集合中找到,則為 <paramref name="value"/> 的索引;否則為 -1。
/// </returns>
/// <param name="value">集合中要確定其索引的元素。</param>
int IList.IndexOf(object value);
/// <summary>
/// 將某個(gè)對(duì)象插入到集合中的指定索引處。
/// </summary>
/// <param name="index">從零開(kāi)始的索引,將在該位置插入 <paramref name="value"/>。</param><param name="value">要在集合中插入的對(duì)象。</param><exception cref="T:System.ArgumentOutOfRangeException">指定的 <paramref name="index"/> 小于零或大于集合中的項(xiàng)數(shù)。</exception><exception cref="T:System.ArgumentException">設(shè)置的 <paramref name="value"/> 為 null,或者不是集合的正確泛型類(lèi)型 <paramref name="T"/>。</exception>
void IList.Insert(int index, object value);
/// <summary>
/// 從集合中移除作為元素的指定對(duì)象的第一個(gè)匹配項(xiàng)。
/// </summary>
/// <param name="value">要從集合中移除的對(duì)象。</param>
void IList.Remove(object value);
}
(2).KeyedByTypeCollection:提供一個(gè)集合,該集合的項(xiàng)是用作鍵的類(lèi)型。
[__DynamicallyInvokable]
public class KeyedByTypeCollection<TItem> : KeyedCollection<Type, TItem>
{
/// <summary>
/// 初始化 <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1"/> 類(lèi)的新實(shí)例。
/// </summary>
public KeyedByTypeCollection();
/// <summary>
/// 根據(jù)指定的對(duì)象枚舉初始化 <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1"/> 類(lèi)的新實(shí)例。
/// </summary>
/// <param name="items">泛型類(lèi)型 <see cref="T:System.Object"/> 的 <see cref="T:System.Collections.Generic.IEnumerable`1"/>,用于初始化集合。</param><exception cref="T:System.ArgumentNullException"><paramref name="items"/> 為 null。</exception>
public KeyedByTypeCollection(IEnumerable<TItem> items);
/// <summary>
/// 返回集合中第一個(gè)具有指定類(lèi)型的項(xiàng)。
/// </summary>
///
/// <returns>
/// 如果為引用類(lèi)型,則返回類(lèi)型 <paramref name="T"/> 的對(duì)象;如果為值類(lèi)型,則返回類(lèi)型 <paramref name="T"/> 的值。 如果集合中不包含類(lèi)型 <paramref name="T"/> 的對(duì)象,則返回類(lèi)型的默認(rèn)值:如果是引用類(lèi)型,默認(rèn)值為 null;如果是值類(lèi)型,默認(rèn)值為 0。
/// </returns>
/// <typeparam name="T">要在集合中查找的項(xiàng)的類(lèi)型。</typeparam>
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public T Find<T>();
/// <summary>
/// 從集合中移除具有指定類(lèi)型的對(duì)象。
/// </summary>
///
/// <returns>
/// 從集合中移除的對(duì)象。
/// </returns>
/// <typeparam name="T">要從集合中移除的項(xiàng)的類(lèi)型。</typeparam>
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public T Remove<T>();
/// <summary>
/// 返回 <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1"/> 中包含的類(lèi)型 <paramref name="T"/> 的對(duì)象的集合。
/// </summary>
///
/// <returns>
/// 一個(gè)類(lèi)型 <paramref name="T"/> 的 <see cref="T:System.Collections.ObjectModel.Collection`1"/>,包含來(lái)自原始集合的類(lèi)型 <paramref name="T"/> 的對(duì)象。
/// </returns>
/// <typeparam name="T">要在集合中查找的項(xiàng)的類(lèi)型。</typeparam>
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public Collection<T> FindAll<T>();
/// <summary>
/// 從集合中移除所有具有指定類(lèi)型的元素。
/// </summary>
///
/// <returns>
/// <see cref="T:System.Collections.ObjectModel.Collection`1"/>,包含來(lái)自原始集合的類(lèi)型 <paramref name="T"/> 的對(duì)象。
/// </returns>
/// <typeparam name="T">要從集合中移除的項(xiàng)的類(lèi)型。</typeparam>
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public Collection<T> RemoveAll<T>();
/// <summary>
/// 獲取集合中包含的某個(gè)項(xiàng)的類(lèi)型。
/// </summary>
///
/// <returns>
/// 集合中指定的 <paramref name="item"/> 的類(lèi)型。
/// </returns>
/// <param name="item">集合中要檢索其類(lèi)型的項(xiàng)。</param><exception cref="T:System.ArgumentNullException"><paramref name="item"/> 為 null。</exception>
[__DynamicallyInvokable]
protected override Type GetKeyForItem(TItem item);
/// <summary>
/// 在集合中的特定位置插入一個(gè)元素。
/// </summary>
/// <param name="index">從零開(kāi)始的索引,應(yīng)在該位置插入 <paramref name="item"/>。</param><param name="item">要在集合中插入的對(duì)象。</param><exception cref="T:System.ArgumentNullException"><paramref name="item"/> 為 null。</exception>
[__DynamicallyInvokable]
protected override void InsertItem(int index, TItem item);
/// <summary>
/// 使用一個(gè)新對(duì)象替換指定索引處的項(xiàng)。
/// </summary>
/// <param name="index">要替換的 <paramref name="item"/> 的從零開(kāi)始的索引。</param><param name="item">要添加到集合中的對(duì)象。</param><exception cref="T:System.ArgumentNullException"><paramref name="item"/> 為 null。</exception>
[__DynamicallyInvokable]
protected override void SetItem(int index, TItem item);
}
2.泛型接口和泛型委托:
泛型的主要作用就是定義泛型的引用類(lèi)型和指類(lèi)型。一個(gè)引用類(lèi)型或值類(lèi)型可通過(guò)指定類(lèi)型實(shí)參的方式實(shí)現(xiàn)泛型接口,也可以保持類(lèi)型實(shí)參的未指定狀態(tài)實(shí)現(xiàn)一個(gè)泛型接口。
具體看一下泛型接口IEnumerable:公開(kāi)枚舉數(shù),該枚舉數(shù)支持在非泛型集合上進(jìn)行簡(jiǎn)單迭代。
[ComVisible(true)]
[Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
[__DynamicallyInvokable]
public interface IEnumerable
{
/// <summary>
/// 返回一個(gè)循環(huán)訪問(wèn)集合的枚舉數(shù)。
/// </summary>
///
/// <returns>
/// 一個(gè)可用于循環(huán)訪問(wèn)集合的 <see cref="T:System.Collections.IEnumerator"/> 對(duì)象。
/// </returns>
/// <filterpriority>2</filterpriority>
[DispId(-4)]
[__DynamicallyInvokable]
IEnumerator GetEnumerator();
}
CLR支持泛型委托,目的是保證任何類(lèi)型的對(duì)象都能以一種類(lèi)型安全的方式傳給一個(gè)回調(diào)方法。泛型委托允許一個(gè)孩子類(lèi)型實(shí)例在傳給一個(gè)回調(diào)方法時(shí)不執(zhí)行任何裝箱處理。委托時(shí)機(jī)只提供了4個(gè)方法:一個(gè)構(gòu)造器,一個(gè)Invlke方法,一個(gè)BeginInvoke方法和一個(gè)EndInvoke方法。如果定義的一個(gè)委托類(lèi)型指定了類(lèi)型參數(shù),編譯器會(huì)定義委托類(lèi)的方法,用指定的類(lèi)型參數(shù)替換方法的參數(shù)類(lèi)型和值類(lèi)型。
以上是對(duì)泛型類(lèi)、泛型接口和泛型委托的簡(jiǎn)單了解,本文的目的主要是講解泛型方法,下面我們具體了解一些泛型泛型的知識(shí)。
三.泛型方法解析:
1.泛型方法概述:
定義泛型類(lèi)、結(jié)構(gòu)或接口時(shí),類(lèi)型中定義的任何方法都可引用類(lèi)型指定的一個(gè)類(lèi)型參數(shù)。類(lèi)型參數(shù)可以作為方法的參數(shù),作為方法的返回值,或者作為方法內(nèi)部定義的一個(gè)局部變量來(lái)使用。CLR允許一個(gè)方法指定它獨(dú)有的類(lèi)型參數(shù),這些類(lèi)型參數(shù)可用于參數(shù)、返回值、或者局部變量。
C#編譯器支持在調(diào)用一個(gè)泛型方法時(shí)進(jìn)行類(lèi)型推斷。執(zhí)行類(lèi)型推斷時(shí),C#使用變量的數(shù)據(jù)類(lèi)型,而不是由變量引用的對(duì)象的實(shí)際類(lèi)型。一個(gè)類(lèi)型可以定義多個(gè)方法,讓其中一個(gè)方法接受具體的數(shù)據(jù)類(lèi)型,讓另一個(gè)方法接受泛型類(lèi)型參數(shù)。
泛型方法示例:
List<TOutput> ConverAll<TOutput>(Conver<T,TOutput> conv) List<TOutput>:返回類(lèi)型(一個(gè)泛型列表)。 ConverAll:方法名。 <TOutput>:類(lèi)型參數(shù)。 Conver<T,TOutput>:參數(shù)類(lèi)型(泛型委托)。 conv:參數(shù)名。
對(duì)以上的示例代碼分析,需要掌握:為每個(gè)類(lèi)型參數(shù)使用一個(gè)不同的類(lèi)型,在整體應(yīng)用這些類(lèi)型參數(shù)。
(1).首先替換包含方法(List<T>的T部分)的那個(gè)類(lèi)型的類(lèi)型參數(shù),如將T替換為string:
List<TOutput> ConverAll<TOutput>(Conver<string,TOutput> conv)
(2).處理完T后,再需要處理的就是TOutput,可以看出它是一個(gè)方法類(lèi)型參數(shù),這里采用guid替換TOutput。
List<Guid> ConverAll(Conver<string,Guid> conv)
對(duì)TOutput賦予類(lèi)型實(shí)參后,可以移除生命中的類(lèi)型參數(shù)<TOutput>,將方法堪稱(chēng)非泛型方法,如上。以上的示例可以處理一個(gè)字符串列表,用一個(gè)轉(zhuǎn)換器來(lái)生成一個(gè)Guid列表。
將原始列表中的每個(gè)元素都轉(zhuǎn)換成目標(biāo)類(lèi)型,將轉(zhuǎn)換后的元素添加到一個(gè)列表中,最后返回這個(gè)列表。以上的處理方式,主要將其泛型方法的參數(shù)進(jìn)行逐一的細(xì)化,無(wú)論在什么學(xué)科,都需要將復(fù)雜的問(wèn)題進(jìn)行簡(jiǎn)單化,將抽象的問(wèn)題具體化,這也是一種常用的處理方式。
2.類(lèi)型約束:
約束的作用是限制能指定成泛型實(shí)參的類(lèi)型數(shù)量。通過(guò)限制類(lèi)型的數(shù)量,我們可以對(duì)那些類(lèi)型執(zhí)行更多的操作。約束可以應(yīng)用于一個(gè)泛型類(lèi)型的類(lèi)型參數(shù),也可以應(yīng)用于一個(gè)泛型方法的類(lèi)型參數(shù)。CLR不允許基于類(lèi)型參數(shù)名稱(chēng)或約束進(jìn)行重載,只能基于元數(shù)對(duì)類(lèi)型或方法進(jìn)行重載。不允許為重寫(xiě)方法的類(lèi)型參數(shù)指定任何約束,但是類(lèi)型實(shí)參的名稱(chēng)是可以改變的。
泛型約束的操作,約束要放到泛型方法或泛型類(lèi)型聲明的末尾,并由上下文關(guān)鍵where引入。
(1).引用類(lèi)型約束:
引用類(lèi)型約束:用于確保使用的類(lèi)型實(shí)參是引用類(lèi)型。(表示為:T:class,且必須為類(lèi)型參數(shù)指定的第一個(gè)約束。)
(2).值類(lèi)型約束:
值類(lèi)型約束:用于確保使用的類(lèi)型參數(shù)是指類(lèi)型。(表示為:T:struct,可空類(lèi)型不包含在內(nèi))
(3).構(gòu)造函數(shù)類(lèi)型約束:
構(gòu)造函授類(lèi)型約束:指定所有類(lèi)型參數(shù)的最后一個(gè)約束,它檢查類(lèi)型實(shí)參是否有一個(gè)可用于創(chuàng)建實(shí)例的無(wú)參構(gòu)造函數(shù)。(表示為:T:new())適用于所有值類(lèi)型,所有沒(méi)有顯示聲明構(gòu)造函數(shù)的非靜態(tài)、非抽象類(lèi),所有顯示聲明了一個(gè)公共無(wú)參構(gòu)造函數(shù)的非抽象類(lèi)。
(4).轉(zhuǎn)換類(lèi)型約束:
轉(zhuǎn)換類(lèi)型約束:允許你指定另一個(gè)類(lèi)型,類(lèi)型實(shí)參必須可以通過(guò)一致性、引用或裝箱轉(zhuǎn)換隱式地轉(zhuǎn)換為該類(lèi)型。還可以規(guī)定類(lèi)型實(shí)參必須可以轉(zhuǎn)換為另一個(gè)類(lèi)型實(shí)參。(例:class Sample<T> where T:Stream)
(5).組合約束:
組合約束:所個(gè)約束組合在一起的約束,但是組合約束也有限制條件。因?yàn)闆](méi)有任何類(lèi)型即是引用類(lèi)型,又是值類(lèi)型。由于每一個(gè)值都有一個(gè)無(wú)參構(gòu)造函數(shù),所以假如已經(jīng)有一個(gè)值類(lèi)型約束,就不允許再指定一個(gè)構(gòu)造函數(shù)約束。如果存在多個(gè)類(lèi)型約束,并且其中一個(gè)為類(lèi),那么它應(yīng)該出現(xiàn)在接口的前面,而且我們不能多次指定同一個(gè)接口。不同的類(lèi)型參數(shù)可以用不同的約束,分別由一個(gè)where引入。
備注:類(lèi)型推斷只適用于泛型方法,不適用于泛型類(lèi)型。
以上是對(duì)泛型方法的相關(guān)概念和約束做了簡(jiǎn)單的解析,接下來(lái)看一下.NET中一些發(fā)行方法的具體實(shí)現(xiàn):
/// <summary>
/// 封裝一個(gè)方法,該方法具有四個(gè)參數(shù)并且不返回值。
/// </summary>
/// <param name="arg1">此委托封裝的方法的第一個(gè)參數(shù)。</param><param name="arg2">此委托封裝的方法的第二個(gè)參數(shù)。</param><param name="arg3">此委托封裝的方法的第三個(gè)參數(shù)。</param><param name="arg4">此委托封裝的方法的第四個(gè)參數(shù)。</param><typeparam name="T1">此委托封裝的方法的第一個(gè)參數(shù)類(lèi)型。</typeparam><typeparam name="T2">此委托封裝的方法的第二個(gè)參數(shù)類(lèi)型。</typeparam><typeparam name="T3">此委托封裝的方法的第三個(gè)參數(shù)類(lèi)型。</typeparam><typeparam name="T4">此委托封裝的方法的第四個(gè)參數(shù)類(lèi)型。</typeparam><filterpriority>2</filterpriority>
[TypeForwardedFrom("System.Core, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089")]
[__DynamicallyInvokable]
public delegate void Action<in T1, in T2, in T3, in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
/// <summary> /// 表示比較同一類(lèi)型的兩個(gè)對(duì)象的方法。 /// </summary> /// /// <returns> /// 一個(gè)有符號(hào)整數(shù),指示 <paramref name="x"/> 與 <paramref name="y"/> 的相對(duì)值,如下表所示。 值 含義 小于 0 <paramref name="x"/> 小于 <paramref name="y"/>。 0 <paramref name="x"/> 等于 <paramref name="y"/>。 大于 0 <paramref name="x"/> 大于 <paramref name="y"/>。 /// </returns> /// <param name="x">要比較的第一個(gè)對(duì)象。</param><param name="y">要比較的第二個(gè)對(duì)象。</param><typeparam name="T">要比較的對(duì)象的類(lèi)型。</typeparam><filterpriority>1</filterpriority> [__DynamicallyInvokable] public delegate int Comparison<in T>(T x, T y);
四.泛型方法應(yīng)用代碼示例:
以上講解的有關(guān)泛型方法的內(nèi)容,這里提供一個(gè)有關(guān)泛型方法操作XML的代碼:
/// <summary>
/// 泛型方法:編譯器能夠根據(jù)傳入的方法參數(shù)推斷類(lèi)型參數(shù);它無(wú)法僅從約束或返回值推斷類(lèi)型參數(shù)
/// </summary>
public class ObjectXmlSerializer
{
/// <summary>
/// 文件的反序列化
/// </summary>
/// <typeparam name="T">返回值類(lèi)型</typeparam>
/// <param name="fileName"></param>
/// <returns>
/// 如果日志啟用,則發(fā)生異常時(shí),異常寫(xiě)入日志,若日志沒(méi)有開(kāi)啟,則直接拋出異常信息
/// loggingEnabled==true: Null is returned if any error occurs.
/// loggingEnabled==false: throw exception
/// </returns>
public static T LoadFromXml<T>(string fileName) where T : class
{
return LoadFromXml<T>(fileName, true);
}
/// <summary>
/// 文件反序列化,若發(fā)生異常,異常信息寫(xiě)入日志
/// </summary>
/// <typeparam name="T">加載類(lèi)的類(lèi)型</typeparam>
/// <param name="fileName">文件名字</param>
/// <param name="loggingEnabled">啟用日志記錄</param>
/// <returns></returns>
public static T LoadFromXml<T>(string fileName, bool loggingEnabled) where T : class
{
FileStream fs = null;
try
{
var serializer = new XmlSerializer(typeof(T));
fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
//反序列化對(duì)象
return (T)serializer.Deserialize(fs);
}
catch (Exception e)
{
if (loggingEnabled)
{
//文件異常,寫(xiě)入日志
LogLoadFileException(fileName, e);
return null;
}
else
{
throw new Exception(e.Message);
}
}
finally
{
if (fs != null) fs.Close();
}
}
/// <summary>
/// 序列化一個(gè)對(duì)象到文件中.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fileName">文件名</param>
/// <param name="data">待序列化的數(shù)據(jù)</param>
/// <returns>
/// 如果日志啟用,則發(fā)生異常時(shí),異常寫(xiě)入日志,若日志沒(méi)有開(kāi)啟,則直接拋出異常信息
/// loggingEnabled==true: log exception
/// loggingEnabled==false: throw exception
/// </returns>
public static void SaveToXml<T>(string fileName, T data) where T : class
{
SaveToXml(fileName, data, true);
}
/// <summary>
/// 文件反序列化,若發(fā)生異常,異常信息寫(xiě)入日志
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fileName">文件名</param>
/// <param name="data">發(fā)序列化對(duì)象</param>
/// <param name="loggingEnabled">是否啟用日志</param>
public static void SaveToXml<T>(string fileName, T data, bool loggingEnabled) where T : class
{
FileStream fs = null;
try
{
var serializer = new XmlSerializer(typeof(T));
fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
//序列化對(duì)象
serializer.Serialize(fs, data);
}
catch (Exception e)
{
if (loggingEnabled) LogSaveFileException(fileName, e);
else
{
throw new Exception(e.Message);
}
}
finally
{
if (fs != null) fs.Close();
}
}
/// <summary>
/// 序列化
/// XML & Datacontract Serialize & Deserialize Helper
/// </summary>
/// <typeparam name="T">T指定必須為class類(lèi)型</typeparam>
/// <param name="serialObject"></param>
/// <returns></returns>
public static string XmlSerializer<T>(T serialObject) where T : class
{
var ser = new XmlSerializer(typeof(T));
//MemoryStream實(shí)現(xiàn)對(duì)內(nèi)存的讀寫(xiě),而不是對(duì)持久性存儲(chǔ)器進(jìn)行讀寫(xiě)
//MemoryStream封裝以無(wú)符號(hào)字節(jié)數(shù)組形式存儲(chǔ)的數(shù)據(jù),該數(shù)組在創(chuàng)建MemoryStream對(duì)象時(shí)被初始化,
//或者該數(shù)組可創(chuàng)建為空數(shù)組。可在內(nèi)存中直接訪問(wèn)這些封裝的數(shù)據(jù)。
//內(nèi)存流可降低應(yīng)用程序中對(duì)臨時(shí)緩沖區(qū)和臨時(shí)文件的需要。
var mem = new MemoryStream();
var writer = new XmlTextWriter(mem, UTF8);
ser.Serialize(writer, serialObject);
writer.Close();
return UTF8.GetString(mem.ToArray());
}
/// <summary>
/// 反序列化
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param>
/// <returns></returns>
public static T XmlDeserialize<T>(string str) where T : class
{
var mySerializer = new XmlSerializer(typeof(T));
var mem2 = new StreamReader(new MemoryStream(UTF8.GetBytes(str)), UTF8);
return (T)mySerializer.Deserialize(mem2);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="xmlData"></param>
/// <returns>返回值類(lèi)型為傳入的類(lèi)型</returns>
public static T DataContractDeserializer<T>(string xmlData) where T : class
{
var stream = new MemoryStream(UTF8.GetBytes(xmlData));
var reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
var ser = new DataContractSerializer(typeof(T));
var deserializedPerson = (T)ser.ReadObject(reader, true);
reader.Close();
stream.Close();
return deserializedPerson;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="myObject"></param>
/// <returns></returns>
public static string DataContractSerializer<T>(T myObject) where T : class
{
var stream = new MemoryStream();
var ser = new DataContractSerializer(typeof(T));
ser.WriteObject(stream, myObject);
stream.Close();
return UTF8.GetString(stream.ToArray());
}
/// <summary>
/// 序列化時(shí)異常日志
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="ex">異常</param>
[Conditional("TRACE")]
private static void LogLoadFileException(string fileName, Exception ex)
{
var sb = new StringBuilder();
sb.Append("Fail to load xml file: ");
sb.Append(fileName + Environment.NewLine);
sb.Append(ex);
//寫(xiě)入日志記錄中方法
// Logger.LogEvent(LogCategory, LogEventLoadFileException, sb.ToString());
}
/// <summary>
/// 反序列化時(shí)異常日志
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="ex">異常</param>
[Conditional("TRACE")]
private static void LogSaveFileException(string fileName, Exception ex)
{
var sb = new StringBuilder();
sb.Append("Fail to save xml file: ");
sb.Append(fileName + Environment.NewLine);
sb.Append(ex);
}
/// <summary>
/// 將xml字符串序列化為數(shù)據(jù)流(數(shù)據(jù)流編碼為ASCII,UTF8)
/// </summary>
/// <returns>字符串轉(zhuǎn)換到流</returns>
public static MemoryStream StringXmlToStream(string strXml,Encoding encod)
{
MemoryStream memoryStream = null;
try
{
Encoding encoding;
if (Equals(encod, ASCII))
{
encoding = new ASCIIEncoding();
}
else
{
encoding = new UTF8Encoding();
}
var byteArray = encoding.GetBytes(strXml);
memoryStream = new MemoryStream(byteArray);
memoryStream.Seek(0, SeekOrigin.Begin);
return memoryStream;
}
catch (IOException ex)
{
throw new IOException(ex.Message);
}
finally
{
if (memoryStream != null) memoryStream.Close();
}
}
}
以上的代碼就不做贅述,需要次代碼的可以使用。
五.總結(jié):
本文講解了C#2.0引入的泛型知識(shí),主要包含泛型類(lèi)、泛型接口、泛型委托,并且重點(diǎn)講解了泛型方法,已經(jīng)泛型的約束分類(lèi)。最后給了一些利用泛型方法操作xml的方法。希望以上的講解可以幫助到正在想學(xué)習(xí)的人。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家有所幫助,同時(shí)也希望多多支持我們!
上一篇:基于c#實(shí)現(xiàn)的九九乘法表(簡(jiǎn)單實(shí)例)
欄 目:C#教程
下一篇:C#程序(含多個(gè)Dll)合并成一個(gè)Exe的簡(jiǎn)單方法
本文標(biāo)題:C#的泛型方法解析
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/6117.html
您可能感興趣的文章
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并打開(kāi)的方法
- 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開(kāi)的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#停止線程的方法
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新聞效果的方法
- 01-10C#通過(guò)重寫(xiě)Panel改變邊框顏色與寬度的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已安裝軟件變化的方法
- 01-10C#實(shí)現(xiàn)多線程下載文件的方法


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開(kāi)的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過(guò)重寫(xiě)Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實(shí)例總結(jié)


