博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(转载)浅谈C#中的泛型
阅读量:6150 次
发布时间:2019-06-21

本文共 6022 字,大约阅读时间需要 20 分钟。

本文记录一些自己读书中感觉脑神经一跳的地方,但都还是比较浅显的东西。由于自己在泛型使用上还火候未到,什么地方有错误还请各位纠正,高手请勿喷啊。

什么是泛型?

    泛型是.NET Framework 2.0新增的一个东西,泛型与程序集中的IL 代码高度集成。有了泛型,我们可以创建独立于被包含类型的类和方法,我们不必给不同的类编写功能相同的很多方法或者类,只创建一个方法或类就可以了。和Object类相比,Object类属于非类型安全的,而泛型使用泛型类型,可以根据需要用特定的类型替换泛型类型,这样就保证了类型安全类。另一方面,泛型和C++的模板很相似,但泛型是由CLR定义的。

泛型LIST<T>与集合ArryList的比较

    ArryList 可以存储值类型或者引用类型,但对于值类型存入要进行一次装箱的过程。取出的时候也要进行拆箱操作。

var list = new ArrayList(); list.Add(123);        //添加int型的123,将int装箱为object foreach (int item inlist) //取出list[0]为item,将object拆箱为int{     Console.WriteLine(item);  }

    拆箱装箱的操作自然会影响程序的运行效率。而在向Arrylist里添加成员的时候它不会判断数据类型,也就是不管什么类型的对象都可以添加到Arrylist里,当arrylist里的数据类型不相同时,程序运行时会出现异常。

var list = new ArrayList();list.Add("string1");list.Add(123);list.Add(DateTime.Now);list.Add(new MyClass());foreach (int item in list)        //当转换MyClass的事例时,系统会抛出异常。{       Console.WriteLine(item); }

     相对于ArryList,List<T>中,泛型类型T定义了允许使用的类型,当定义List<int> 的一个泛型对象,它就只允许int的类型传入,如果尝试传入其他类型的对象,编译时不会通过,因为参数是无效的。

List<T>的内存

    首先在创建List对象的时候,系统只会为List对象本身分配内存,其Capacity属性也为0。当为List对象传入第一个成员后,会为其开辟长度为4的内存空间,同样Capacity属性值也为4。当继续为其增加元素总数超过当前Capacity个的话,List对象会扩充为2倍当前Capacity的容量,也就是说当向list添加第5个元素后,这个List的Capacity是8。 但List内的元素内存是一直保持连续的,所以每一次扩充容量,都会先开辟一块是原来容量两倍大的内存,然后把之前的数据拷贝到新开辟的内存中,这样一来,如果List内的数据量很大的话,也会是一个很大的开销。

    但是如果每次添加元素超过当前Capacity,都会申请一块两倍大的内存,有时可能会造成对内存的浪费。这时我们可以使用List提供的方法TrimExcess,来使List对象的容量等于元素的数量。但是,重新分配和复制很大的List 的开销可能很大,因此,如果列表大于容量的 90%,则 TrimExcess 方法将不执行任何操作,也就是说如果未用容量已经小于等于总容量的 10%,则列表容量不会进行调整。这样可以避免为获得相对较少的收益而产生大量重新分配开销。 请看以下代码比较好的说明前面提到的。

 

List
value = new List
();//此时 Count:0 Capacity: 0 for (int i = 1; i <= 4; i++){ value.Add(i);}//此时 Count:4 Capacity: 4 value.Add(5);////此时 Count:5 Capacity: 8 value.TrimExcess();//此时 Count:5 Capacity: 5//remove an itemvalue.RemoveAt(4);//此时 Count:4 Capacity: 5value.TrimExcess();//此时 Count:4 Capacity: 5 未用容量小于总容量的 10%//remove another itemvalue.RemoveAt(1);//此时 Count:3 Capacity: 5value.TrimExcess();//此时 Count:3 Capacity: 3value.Clear();//此时 Count:0 Capacity: 3value.TrimExcess();//此时 Count:0 Capacity: 0

 泛型类

     为什么说使用泛型会使程序变得更加灵活呢?看一下以下定义了一个泛型类。

public class MyClass
{ public MyClass(T value) { this.Value = value; } public T Value { get; set; } }

    名为MyClass 的类为一个泛型类,因为他用泛型类型T声明,这样泛型类型就可以在类中作一个字段成员,其构造函数也可以接受T类型的对象,T类型也就是所有类型。下面例子一个DocumentManager泛型类。

public class DocumentManager
//创建泛型类 { private Queue
documentQueue=new Queue
(); public void AddDocuemnt(T doc) //创建泛型方法 { lock(this) { documentQueue.Enqueue(doc); } } public bool IsDocumentAvailable { get { return documentQueue.Count > 0; } } public T GetDocument() { T doc = default(T); //默认值Default关键字,不能把null赋予泛型类型,因为泛型类型也可以实例化为值类型,通过default将null赋予引用类型,0赋予值类型。 lock (this) { doc = documentQueue.Dequeue(); } return doc; }

  泛型约束,如果泛型类要调用泛型类型中的方法,就必须添加约束,下面代码Document实现IDocument接口。

public interface IDocument    {        string Title { get; set; }        string Content { get; set; }    }    public class Document:IDocument    {        public Document()        {                    }        public Document(string title,string content)        {            this.Title = title;            this.Content = content;        }        public string Title { get; set; }        public string Content { get; set; }    }

    然后在DocumentManager类里添加方法用于显示所有文档的Title

public void DisplayAllDocumets()        {            foreach (T doc in documentQueue)            {                Console.WriteLine(((IDocument)doc).Title);            }        }

   但是如果类型T没有实现IDocument接口,运行程序是会抛出异常。这就需要为DocumentManager定义一个约束:(这里将T改为TDocument只是为了更方便阅读)

public class DocumentManager
where T:IDocument

  这样一来,就只接受实现了IDocument的参数,我们就可以改写DisplayAllDocumets方法如下:

public void DisplayAllDocumets()        {            foreach (T doc in documentQueue)            {                Console.WriteLine(doc.Title);  //可以直接调用Title属性            }        }

  在Main方法中来试一下

static void Main(string[] args)        {            var dm = new DocumentManager
(); dm.AddDocuemnt(new Document("Title A", "Content A")); dm.AddDocuemnt(new Document("Title B", "Content B")); dm.AddDocuemnt(new Document("Title C", "Content C")); dm.DisplayAllDocumets(); if (dm.IsDocumentAvailable) { Document d = dm.GetDocument(); Console.WriteLine(d.Content); } Console.ReadLine(); }

输出:

下图为泛型支持的几种约束类型

当使用多个约束时,是用逗号隔开的。例如:

public class DocumentManager
where T:IDocument,new() //多个约束

泛型类中的静态成员 只能在相同泛型类型的实例中共享,

public class StaticDemo
{ public static int x; } class Program { private static void Main(string[] args) { StaticDemo
.x = 4; StaticDemo
.x = 5; Console.WriteLine(StaticDemo
.x); //输出4 Console.WriteLine(StaticDemo
.x); //输出5 StaticDemo
.x+=4; Console.WriteLine(StaticDemo
.x); //输出8 } }

 

下面的代码常用于Web application取Session方法。

public static T GetSession
(string name) { if (HttpContext.Current.Session != null && HttpContext.Current.Session[name] != null) return (T)HttpContext.Current.Session[name]; return default(T); }

  GetSession方法将返回一个泛型类型的对象,比如我们有一个用户类UserInfo,并且再用户登陆后将UserInfo的一个实例存储到Session["User"],我们可以这样取出它。

var CurrentUser = GetSession
("User");

转载于:https://www.cnblogs.com/imust/archive/2012/11/16/2774102.html

你可能感兴趣的文章
java连接MySql数据库
查看>>
转:Vue keep-alive实践总结
查看>>
深入python的set和dict
查看>>
C++ 11 lambda
查看>>
Android JSON数据解析
查看>>
DEV实现日期时间效果
查看>>
java注解【转】
查看>>
centos 下安装g++
查看>>
嵌入式,代码调试----GDB扫盲
查看>>
类斐波那契数列的奇妙性质
查看>>
下一步工作分配
查看>>
Response. AppendHeader使用大全及文件下载.net函数使用注意点(转载)
查看>>
Wait Functions
查看>>
代码描述10313 - Pay the Price
查看>>
jQuery最佳实践
查看>>
centos64i386下apache 403没有权限访问。
查看>>
jquery用法大全
查看>>
PC-BSD 9.2 发布,基于 FreeBSD 9.2
查看>>
网卡驱动程序之框架(一)
查看>>
css斜线
查看>>