C#泛型类容器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#泛型类容器相关的知识,希望对你有一定的参考价值。
非泛型容器的缺点:
(1) 性能问题。
在使用值类型时,必须将值类型装箱(Boxing)以便推送和存储,并且在将值类型从容器中取出时将其取消装箱(Unboxing)。装箱和取消装箱都会根据值类型的权限造成重大的性能损失。而且,装箱和取消装箱操作还会增加托管堆上的压力,导致更多的垃圾收集工作,这对于性能而言也不好。即使是在使用引用类型而不是值类型时,仍然存在性能损失,因为必须强制地将 object转换为需要的实际类型进行类型,造成强制类型转换开销,代码如下:
Container c = new Container();
c.Insert("1");
string number = (string)c.Delete();
(2) 类型安全。
类型转换难以保证每次转换都是成功的,这将导致某些错误在编译时无法被检查出来,而在运行时发生异常。因为编译器允许在任何类型和object 之间进行强制类型转换,所以将丢失编译时类型安全。
(3) 工作效率。
编写类型特定的数据结构是一项乏味、重复且易于出错的工作。并且,无法预知未知类型的使用情况,因此还必须保持基于 object 的数据结构。
泛型容器的完整 C#实现
1 public class Container<T> 2 { 3 readonly int m_Size; 4 private int m_ContainerPointer; 5 T[] m_Items; 6 7 public Container() 8 : this(100) 9 { 10 m_ContainerPointer = -1; 11 m_Items = new T[100]; 12 } 13 public Container(int size) 14 { 15 m_Size = size; 16 m_ContainerPointer = -1; 17 m_Items = new T[size]; 18 } 19 public int Count 20 { 21 get { return m_ContainerPointer; } 22 } 23 public bool IsEmpty 24 { 25 get { return m_ContainerPointer == -1; } 26 } 27 public bool IsFull 28 { 29 get { return m_ContainerPointer == m_Size - 1; } 30 } 31 public void Insert(object item)//在容器的尾部插入一个元素 32 { 33 if (IsFull) 34 { 35 Console.WriteLine("Container is full!"); 36 return; 37 } 38 m_Items[++m_ContainerPointer] = (T)item; 39 } 40 public object Delete()//从容器的尾部删除一个元素 41 { 42 if (m_ContainerPointer >= 0) 43 { 44 return m_Items[m_ContainerPointer--]; 45 } 46 return null; 47 } 48 }
表面上,C# 泛型的语法看起来与 C++模板类似,但是编译器实现和支持它们的方式存在重要差异。与 C++模板相比,C#泛型可以提供增强的安全性,但是在功能方面也受到某种程度的限制。 在一些 C++编译器中, 在通过特定类型使用模板类之前,编译器甚至不会编译模板代码。当确实指定了类型时,编译器会以内联方式插入代码,并且将每个出现一般类型参数的地方替换为指定的类型。此外,每当使用特定类型时,编译器都会插入特定于该类型的代码,而不管是否已经在应用程序中的其他某个位置为模板类指定了该类型。C++链接器负责解决该问题,并且并不总是有效。这可能会导致代码膨胀,从而增加加载时间和内存足迹。在.NET Framework 2.0 中,泛型在 IL(中间语言)和 CLR 本身中具有本机支持。在编译泛型 C#代码时,首先编译器会将其编译为 IL,就像其他任何类型一样。但是,IL 只包含实际特定类型的参数或占位符,并有专用的 IL 指令支持
泛型操作。泛型代码的元数据中包含泛型信息。真正的泛型实例化工作以“on-demand”的方式,发生在 JIT 编译时。当进行 JIT 编译时,JIT 编译器用指定的类型实参来替换泛型 IL 代码元数据中的 T,进行泛型类型的实例化。这会向 JIT 编译器提供类型特定的 IL 元数据定义,就好像从未涉及到泛型一样。这样,JIT 编译器就可以确保方法参数的正确性,实施类型安全检查,甚至执行类型特定的 IntelliSense。当.NET 将泛型 IL 代码编译为本机代码时, 所产生的本机代码取决于指定的类型。如果本机指定的是值类型,则 JIT 编译器将泛型类型参数替换为特定的值类型, 并且将其编译为本机代码。 JIT 编译器跟踪已经生成的类型特定的 IL 代码。如果 JIT 编译器用已经编译为本机代码的值类型编译泛型 IL 代码,则只是返回对该 IL 代码的引用。因为 JIT 编译器在以后的所有场合中都将使用相同的值类型特定的 IL 代码,所以不存在代码膨胀问题。如果本机指定的是引用类型,则 JIT 编译器将泛型 IL 代码中的泛型参数替换为 object,并将其编译为本机代码。在以后的任何针对引用类型而不是泛型类型参数的请求中,都将使用该代码。JIT 编译器只会重新使用实际代码。实例仍然按照它们离开托管堆的大小分配空间,并且没有强制类型转换。
泛型的好处
泛型使代码可以重用,类型和内部数据可以在不导致代码膨胀的情况下更改,而不管是值类型还是引用类型。可以一次性地开发、测试和部署代码,通过任何类型(包括将来的类型)来重用它,并且全部具有编译器支持和类型安全。因为泛型代码不会强行对值类型进行装箱和取消装箱, 或者对引用类型进行向下强制类型转换,所以性能得到显著提高。对于值类型,性能通常会提高 200%;对于引用类型,在访问该类型时,可以预期性能最多提高 100%(当然,整个应用程序的性能可能会提高,也可能不会提高) 。
以上是关于C#泛型类容器的主要内容,如果未能解决你的问题,请参考以下文章