4.3 獲取泛型的類型
問題
您需要在運行時獲得一個泛型類型實例的Type對象。
解決方案
在使用typeof操作符時提供類型參數;使用類型參數實例化的泛型類型,用GetType()方法。
聲明一個一般類型和一個泛型類型如下:
public class Simple
{
public Simple()
{
}
}
public class SimpleGeneric<T>
{
public SimpleGeneric()
{
}
}
使用typeof操作符和簡單類型的名稱就可以在運行時獲得簡單類型的類型。對於泛型類型來說,在調用typeof時類型參數必須要提供,但是簡單類型實例和泛型類型實例都可以使用相同的方式來調用GetType()。
Simple s = new Simple();
Type t = typeof(Simple);
Type alsoT = s.GetType();
//提供類型參數就才可以獲得類型實例
Type gtInt = typeof(SimpleGeneric<int>);
Type gtBool = typeof(SimpleGeneric<bool>);
Type gtString = typeof(SimpleGeneric<string>);
// 當有一個泛型類實例時,您也可以使用GetType的舊的方式去調用一個實例。.
SimpleGeneric<int> sgI = new SimpleGeneric<int>();
Type alsoGT = sgI.GetType();
討論
不能直接獲取泛型類的類型,因為如果不提供一個類型參數,泛型類將沒有類型(參考秘訣4.2獲得更多信息)。只有通過類型參數實例化的泛型類才有Type。
如果您在使用typeof操作符時,只提供泛型類型定義而不提供類型參數,將得到下面的錯誤:
// 這產生一個錯誤:
// Error 26 Using the generic type 'CSharpRecipes.Generics.SimpleGeneric<T>'
// requires '1' type arguments
Type gt = typeof(SimpleGeneric);
閱讀參考
查看秘訣4.2;參考MSDN文檔中的“typeof”主題。
4.4 使用相應的泛型版本替換ArrayList
問題
您希望通過將所有ArrayList對象替換為相應的泛型版本以提高應用程序的效率,並使得代碼更易於使用。當結構體或其他值類型存儲在這些數據結構中時,會導致裝箱/拆箱操作,這時就需要這麼做。
解決方案
使用更有效率的泛型類System.Collections.Generic.List來替換已存在的System.Collection.ArrayList類。
下面是使用System.Collection.ArrayList對象的簡單例子:
public static void UseNonGenericArrayList()
{
// 創建一個ArrayList.
ArrayList numbers = new ArrayList();
numbers.Add(1); // 導致裝箱操作
numbers.Add(2); // 導致裝箱操作
// 顯示ArrayList內的所有整數
// 每次迭代都導致拆箱操作
foreach (int i in numbers)
{
Console.WriteLine(i);
}
numbers.Clear();
}
相同的代碼使用了System.Collections.Generic.List對象
public static void UseGenericList()
{
// 創建一個List.
List<int> numbers = new List<int>();
numbers.Add(1);
numbers.Add(2);
// 顯示List中的所有整數.
foreach (int i in numbers)
{
Console.WriteLine(i);
}
numbers.Clear();
}
討論
因為所有的應用程序幾乎都會使用ArrayList,從提升您的應用程序的執行效率開始是一個不錯的選擇。對於應用程序中簡單使用ArrayList的地方來說,這種替代是非常容易的。但有些地方需要注意,例如,泛型List類未實現Icloneable接口而ArrayList實現了它。
表4-1顯示了兩個類中的等價成員。
ArrayList類成員 等價的泛型List類成員 Capacity 屬性 Capacity屬性 Count屬性 Count屬性 IsFixedSize屬性 ((IList)myList).IsFixedSize IsReadOnly屬性 ((IList)myList).IsReadOnly IsSynchronized屬性 ((IList)myList).IsSynchronized Item屬性 Item屬性 SyncRoot屬性 ((IList)myList).SyncRoot Adapter 靜態方法 N/A Add 方法 Add方法 AddRange方法 AddRange方法 N/A AsReadOnly方法 BinarySearch方法 BinarySearch方法 Clear方法 Clear方法 Clone方法 Getrange(0, numbers.Count) Contains方法 Contains方法 N/A ConvertAll方法 CopyTo方法 CopyTo方法 N/A Exists方法 N/A Find方法 N/A FindAll方法 N/A FindIndex方法 N/A FindLast方法 N/A FindLastIndex方法 N/A ForEach方法 FixedSize 靜態方法 N/A Getrange方法 Getrange方法 IndexOf方法 IndexOf方法 Insert方法 Insert方法 InsertRange方法 InsertRange方法 LastIndexOf方法 LastIndexOf方法 ReadOnly 靜態方法 AsReadOnly方法 Remove方法 Remove方法 N/A RemoveAll方法 RemoveAt方法 RemoveAt方法 RemoveRange方法 RemoveRange方法 Repeat 靜態方法 使用for循環和Add方法 Reverse方法 Reverse方法 SetRange方法 InsertRange方法 Sort方法 Sort方法 Synchronized 靜態方法 lock(myList.SyncRoot) {…} ToArray方法 ToArray方法 N/A trimExcess方法 TRimToSize方法 trimToSize方法 N/A trueForAll方法
表4-1中的幾個ArrayList的成員和泛型List的成員並非一一對應。從屬性開始說,只有Capacity,Count和Item屬性兩個類中都存在。為了彌補List類中的幾個缺失的屬性,可以把它顯式轉換為Ilist接口。下面的代碼演示了如何使用這些顯式轉換以獲得缺失的屬性。
List<int> numbers = new List<int>();
Console.WriteLine(((IList)numbers).IsReadOnly);
Console.WriteLine(((IList)numbers).IsFixedSize);
Console.WriteLine(((IList)numbers).IsSynchronized);
Console.WriteLine(((IList)numbers).SyncRoot);
注意,由於缺少返回同步版本的泛型List代碼和缺少返回固定尺寸的泛型List代碼,IsFixedSize和IsSynchronized屬性將總是返回false。SyncRoot屬性被調用時將總是返回相同的對象,本質上這個屬性返回this指針。微軟已經決定從所有泛型集合類中去除創建同步成員的功能。做為代替,他們推薦使用lock關鍵字去鎖住整個集合或其他類型的同步對象來滿足您的需要。
靜態的ArrayList.Repeat在泛型List中沒有對應的方法。做為代替,您可以使用下面的泛型方法:
public static void Repeat<T>(List<T> list, T obj, int count)
{
if (count < 0)
{
throw (new ArgumentException(
"參數count 必須大於或等於零"));
}
for (int index = 0; index < count; index++)
{
list.Add(obj);
}
}
這個泛型方法有三個參數:
list
泛型List對象
obj
將被以指定次數添加進泛型List中的對象
count
把obj添加進泛型類中的次數
因為Clone方法也沒有出現在泛型List類中(因為這個類並沒有實現Icloneable接口),您可以使用泛型List類的GetRange方法做為替代。
List<int> oldList = new List<int>();
// 給oldList添加元素…
List<int> newList = oldList.GetRange(0, oldList.Count);
GetRange方法對List對象中一個范圍的元素執行淺拷貝(跟ArrayList中的Clone方法接近)。在此例中這個范圍是所有元素。
提示:ArrayList默認的初始容量是16個元素,而List<T>的默認初始容量為4個元素。這意味著當添加第17個元素時,List<T>不得不改變尺寸(重新分配內存)3次,而ArrayList只重新分配一次。這一點在評估應用程序性能時需要被考慮。