C#基础系列—泛型(Generics)(上)

本文深入探讨C#中的泛型概念,包括其定义、优点及应用实例。通过对比泛型与非泛型集合,展示泛型如何提高性能并确保类型安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
C#基础系列—泛型(Generics)(上)
 (原创-- 转载请注明huwz)
这是一篇关于C#泛型介绍的文章,除了基本的概念性介绍之外,我会加一些我自己的理解和我的想法。
 
下面我主要从这四个方面来谈谈C#泛型:
l 什么是泛型
l 泛型的优点
l 常用泛型介绍
l 设计泛型
 
什么是泛型
先一起看看官方(MSDN)怎么说:
Generics are classes, structures, interfaces and methods that have placeholders(type parameters) for one or more of the types they store or use.
泛型是带有一个或多个占位符(类型参数)的类,结构,接口或方法,这些占位符被它们保留或者使用。
 
举个例子可能说的更明白一点:
Class MyClass<T>
{
   public T amember;
   public void Method<T>(T obj){ }
}
这里我们有一个泛型的类和一个泛型的方法,<T>表示的就是占位符(placeholder),但是T只能表示一种类型——任意的一种类型,可以是值类型,可以是引用类型,比如int, string, DateTime等等。在使用的时候,一旦指定了这个类型,那么就不允许更改了。
举个例子说,我们在某处有这么一行
MyClass<int> myObj = new MyClass<int>();
那么我们可以把这个类的定义想像成
Class MyClass<int>
{
   public int amember;
   public void Method<int>(int obj){ }
}
就是把T替换成了int,再简单不过了。
 
这里T灵活的表示了一个类型。如果我们需要支持多个类型,以两个类型为例,该如何写呢?
是 Class MyClass<T,U>
还是Class MyClass<T><U> ?
 
这个我们留在后面设计泛型的时候再来讨论。不过相信大家已经有了正确的判断。至于T,U,为什么用这两个字母,似乎这个没有规定,你用X,Y,Z也行;随便用什么字母都行,只要不是某种特定类型的,它都可以用作泛型。也不限定一个字母,两个字母,比如<Tu>,它也可以作泛型。
 
说了那么多,希望帮助大家理解什么是泛型。关于泛型类型的编译,有兴趣的可以看看下面这段我从别人那摘录的一段话。不明白也没关系,可以接着看下面的泛型的优点
 
C#泛型编译机制
   第一轮编译时,编译器只为Stack<T>类型产生“泛型版”的IL代码和元数据,并不进行泛型类型的实例化,T在中间只充当占位符。
   JIT编译时,当JIT编译器第一次遇到Stack<int>时,将用int类型替换“泛型版”IL代码与元数据中的T -- 进行泛型类型的实例化。
   CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码,但如果类型参数为“值类型”,对每一个不同的“值类型”,CLR将为其产生一份独立的代码。
 
 
泛型的优点
l 提高性能
l 编译前检查
这是使用泛型最直接的好处。
什么时候能提高性能呢?
大家看看这段代码
public class Demo
   
{
 
       
public void UseGeneircs()
       
{
           List
<int> list = new List<int>();
           
for (int i = 0; i < 1000; i++)
           
{
               list.Add(i);
           }

 
           
foreach (int value in list)
           
{
               Console.WriteLine(value);
           }

       }

 
       
public void NotUseGenerics()
       
{
           ArrayList list 
= new ArrayList();
           
for (int i = 0; i < 1000; i++)
           
{
               list.Add(i);
           }

 
           
foreach (int value in list)
           
{
               Console.WriteLine(value);
           }

       }

   }
 
这里有两个方法,它们都是做一件事情,将1000个数放到一个集合中,然后再从中读取出来;区别是UseGeneric()方法使用的是泛型集合,而NotUseGenerics()使用的是普通集合。我们知道ArrayList里的个体都是Object类型的,所以将值类型放进去的时候,必然有个装箱过程(Box),在读取数据的时候,又有个拆箱的过程(UnBox)。
无论是拆箱还是装箱,都会带来额外的开销,而使用泛型则可以避免这种开销。因此在这种情况下,可以带来性能上的提高。
 
那编译前检查又有什么好处呢?
我们再看一段代码。
public class Demo2
    
{
        
public void UseGeneric()
        
{
            List
<string> list = new List<string>();
            list.Add(
"abc");
            list.Add(
1);
        }

 
        
public void NotUseGeneric()
        
{
            ArrayList list 
= new ArrayList();
            list.Add(
"abc");
            list.Add(
1);
        }

}
 
 
 这里还是做一个事情的两个方法,但是在编译的时候,UseGeneric()方法会报错:
The best overloaded method match for 'System.Collections.Generic.List<string>.Add(string)' has some invalid arguments
因为我们试图把一个整型值加进去。
而NotUseGeneric()方法却无动于衷。这样会带来什么样的不好呢?
进去容易出来难啊!因为ArrayList中的个体都是object类型的,我们无法判断哪一个是int,哪一个是string,所以也就很容易出现类型转换的错误。这样我们就把代码中的风险带到了运行时,这是很要命的。就像是你过年回家好不容易买到一张票,结果半道上查票的时候发现你买的是假票,把你赶下火车;而使用泛型就像是你买票的时候就帮你鉴别票的真伪,上车查票的时候你就放心多了。
 
常用泛型介绍
泛型类最常用于集合,如链接列表,哈希表,堆栈,队列,树等,其中,像从集合中添加和移除项这样的操作都以大体上相同的方式执行,与所存储数据的类型无关。
还有数据类型,比如int,double,它们都可以加减乘除——与类型无关但执行方式相同。
 
List<> 和 ArrayList
Dictionary<,> 和HashTable
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值