淺析C#泛型處理的問(wèn)題空間
C#泛型是C# 2.0 的***大的功能。C#泛型處理什么樣子的問(wèn)題呢?我們通過(guò)C# 泛型處理可以定義類(lèi)型安全的數(shù)據(jù)結(jié)構(gòu),而無(wú)須使用實(shí)際的數(shù)據(jù)類(lèi)型。這樣能夠顯著提高性能并得到更高質(zhì)量的代碼,因?yàn)槟梢灾赜脭?shù)據(jù)算法,而無(wú)須復(fù)制類(lèi)型特定的代碼。在概念上,C# 泛型類(lèi)似于 C++ 的模板,但是在實(shí)現(xiàn)和功能方面存在明顯不同。本系列文章將討論C#泛型處理的問(wèn)題空間、它們的實(shí)現(xiàn)方式、該編程模型的好處,以及獨(dú)特的創(chuàng)新(例如,約束、一般方法和委托以及一般繼承)。您還將了解在 .NET Framework 的其他領(lǐng)域(例如,反射、數(shù)組、集合、序列化和遠(yuǎn)程處理)中如何利用泛型,以及如何在所提供的基本功能的基礎(chǔ)上進(jìn)行改進(jìn)。
C#泛型處理的問(wèn)題陳述
考慮一種普通的、提供傳統(tǒng) Push() 和 Pop() 方法的數(shù)據(jù)結(jié)構(gòu)(例如,堆棧)。在開(kāi)發(fā)通用堆棧時(shí),您可能愿意使用它來(lái)存儲(chǔ)各種類(lèi)型的實(shí)例。在 C# 1.1 下,您必須使用基于 Object 的堆棧,這意味著,在該堆棧中使用的內(nèi)部數(shù)據(jù)類(lèi)型是難以歸類(lèi)的 Object,并且堆棧方法與 Object 交互:
- public class Stack
- {
- object[] m_Items;
- public void Push(object item)
- {...}
- public object Pop()
- {...}
- }
C#泛型處理代碼塊
顯示基于 Object 的堆棧的完整實(shí)現(xiàn)。因?yàn)?Object 是規(guī)范的 .NET 基類(lèi)型,所以您可以使用基于 Object 的堆棧來(lái)保持任何類(lèi)型的項(xiàng)(例如,整數(shù)):
- Stack stack = new Stack();
- stack.Push(1);
- stack.Push(2);
- int number = (int)stack.Pop();
基于 Object 的堆棧
- public class Stack
- {
- readonly int m_Size;
- int m_StackPointer = 0;
- object[] m_Items;
- public Stack():this(100)
- {}
- public Stack(int size)
- {
- m_Size = size;
- m_Items = new object[m_Size];
- }
- public void Push(object item)
- {
- if(m_StackPointer >= m_Size)
- throw new StackOverflowException();
- m_Items[m_StackPointer] = item;
- m_StackPointer++;
- }
- public object Pop()
- {
- m_StackPointer--;
- if(m_StackPointer >= 0)
- {
- return m_Items[m_StackPointer];
- }
- else
- {
- m_StackPointer = 0;
- throw new InvalidOperationException(
- "Cannot pop an empty stack");
- }
- }
- }
但是,基于 Object 的解決方案存在兩個(gè)問(wèn)題。***個(gè)問(wèn)題是性能。在使用值類(lèi)型時(shí),必須將它們裝箱以便推送和存儲(chǔ)它們,并且在將值類(lèi)型彈出堆棧時(shí)將其取消裝箱。裝箱和取消裝箱都會(huì)根據(jù)它們自己的權(quán)限造成重大的性能損失,但是它還會(huì)增加托管堆上的壓力,導(dǎo)致更多的垃圾收集工作,而這對(duì)于性能而言也不太好。即使是在使用引用類(lèi)型而不是值類(lèi)型時(shí),仍然存在性能損失,這是因?yàn)楸仨殢?Object 向您要與之交互的實(shí)際類(lèi)型進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換,從而造成強(qiáng)制類(lèi)型轉(zhuǎn)換開(kāi)銷(xiāo):
- Stack stack = new Stack();
- stack.Push("1");
- string number = (string)stack.Pop();
基于 Object 的解決方案的第二個(gè)問(wèn)題(通常更為嚴(yán)重)是類(lèi)型安全。因?yàn)榫幾g器允許在任何類(lèi)型和 Object 之間進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換,所以您將丟失編譯時(shí)類(lèi)型安全。例如,以下代碼可以正確編譯,但是在運(yùn)行時(shí)將引發(fā)無(wú)效強(qiáng)制類(lèi)型轉(zhuǎn)換異常:
- Stack stack = new Stack();
- stack.Push(1);
- //This compiles, but is not type safe, and will throw an exception:
- string number = (string)stack.Pop();
您可以通過(guò)提供類(lèi)型特定的(因而是類(lèi)型安全的)高性能堆棧來(lái)克服上述兩個(gè)問(wèn)題。對(duì)于整型,可以實(shí)現(xiàn)并使用 IntStack:
- public class IntStack
- {
- int[] m_Items;
- public void Push(int item){...}
- public int Pop(){...}
- }
- IntStack stack = new IntStack();
- stack.Push(1);
- int number = stack.Pop();
對(duì)于字符串,可以實(shí)現(xiàn) StringStack:
- public class StringStack
- {
- string[] m_Items;
- public void Push(string item){...}
- public string Pop(){...}
- }
- StringStack stack = new StringStack();
- stack.Push("1");
- string number = stack.Pop();
C#泛型處理問(wèn)題的出現(xiàn)必然性:
遺憾的是,以這種方式解決性能和類(lèi)型安全問(wèn)題,會(huì)引起第三個(gè)同樣嚴(yán)重的問(wèn)題 — 影響工作效率。編寫(xiě)類(lèi)型特定的數(shù)據(jù)結(jié)構(gòu)是一項(xiàng)乏味的、重復(fù)性的且易于出錯(cuò)的任務(wù)。在修復(fù)該數(shù)據(jù)結(jié)構(gòu)中的缺陷時(shí),您不能只在一個(gè)位置修復(fù)該缺陷,而必須在實(shí)質(zhì)上是同一數(shù)據(jù)結(jié)構(gòu)的類(lèi)型特定的副本所出現(xiàn)的每個(gè)位置進(jìn)行修復(fù)。此外,沒(méi)有辦法預(yù)知未知的或尚未定義的將來(lái)類(lèi)型的使用情況,因此還必須保持基于 Object 的數(shù)據(jù)結(jié)構(gòu)。結(jié)果,大多數(shù) C# 1.1 開(kāi)發(fā)人員發(fā)現(xiàn)類(lèi)型特定的數(shù)據(jù)結(jié)構(gòu)不實(shí)用,并且選擇使用基于 Object 的數(shù)據(jù)結(jié)構(gòu),盡管它們存在缺點(diǎn)。
C#泛型處理的問(wèn)題空間等等內(nèi)容的介紹就講述到這里,希望對(duì)你了解和學(xué)習(xí)C#泛型處理問(wèn)題方面有所幫助。
【編輯推薦】