从装箱拆箱看泛型
.NET很容易把值类型转换为引用类型,所以可以在需要对象的任意地方使用值类型。例如int可以赋予一个对象,从值类型转换为引用类型称为装箱。如果方法需要把一个对象作为参数,同时传递一个值类型,装箱操作就会自动进行。另一方面,装箱的值类型可以使用拆箱操作转换为值类型。
定义一个一般的、非泛型的简化链表类,它可以包含任意类型的对象,在链表中,一个元素引用下一个元素。所以必须创建一个类,它将对象封装在链表中,并引用下一个对象。类LinkedListNode包含一个属性Value,该属性用构造函数初始化。另外LinkedListNode类包含对链表中下一个元素和上一个元素的引用,这些元素都可以从属性中访问。
先定义LinkedListNode类
public class LinkedListNode { public LinkedListNode(object value) { this.Value = value; } public object Value { get; private set; } public LinkedListNode Next { get; internal set; } public LinkedListNode Prev { get; internal set; } }
再定义一个非泛型的简化链表类,实现非泛型接口
public class LinkedList : IEnumerable { public LinkedListNode First { get; private set; } public LinkedListNode Last { get; private set; } public LinkedListNode AddLast(object node) { var newNode = new LinkedListNode(node); if (First == null) { First = newNode; Last = First; } else { Last.Next = newNode; Last = newNode; } return newNode; } public IEnumerator GetEnumerator() { LinkedListNode current = First; while (current != null) { yield return current.Value; current = current.Next; } } }
用ILSpy查看IL代码
IL_0000: nop IL_0001: newobj instance void PraticeCharter01.LinkedList::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.3 IL_0009: box [mscorlib]System.Int32 IL_000e: callvirt instance class PraticeCharter01.LinkedListNode PraticeCharter01.LinkedList::AddLast(object) IL_0013: pop IL_0014: ldloc.0 IL_0015: ldc.i4.4 IL_0016: box [mscorlib]System.Int32 IL_001b: callvirt instance class PraticeCharter01.LinkedListNode PraticeCharter01.LinkedList::AddLast(object) IL_0020: pop IL_0021: nop IL_0022: ldloc.0 IL_0023: callvirt instance class [mscorlib]System.Collections.IEnumerator PraticeCharter01.LinkedList::GetEnumerator() IL_0028: stloc.1 .try { IL_0029: br.s IL_003e // loop start (head: IL_003e) IL_002b: ldloc.1 IL_002c: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() IL_0031: unbox.any [mscorlib]System.Int32 IL_0036: stloc.2 IL_0037: ldloc.2 IL_0038: call void [mscorlib]System.Console::WriteLine(int32) IL_003d: nop IL_003e: ldloc.1 IL_003f: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0044: brtrue.s IL_002b // end loop IL_0046: leave.s IL_005a } // end .try
分析IL代码可知上述过程发生了两次装箱和两次拆箱,在foreach语句中,链表中的元素被强制转换为整形,装箱和拆箱操作很容易使用,但性能损失比较大,泛型能很好的避免拆装箱,从而提供性能。
再定义一个泛型版本,该泛型集合实现泛型接口IEnumerator
public class LinkedListNode<T> { public LinkedListNode(T value) { this.Value = value; } public T Value { get; private set; } public LinkedListNode<T> Next { get; internal set; } public LinkedListNode<T> Prev { get; internal set; } } public class LinkedList<T> : IEnumerable<T> { public LinkedListNode<T> First { get; private set; } public LinkedListNode<T> Last { get; private set; } public LinkedListNode<T> AddLast(T node) { var newNode = new LinkedListNode<T>(node); if (First == null) { First = newNode; Last = First; } else { Last.Next = newNode; Last = newNode; } return newNode; } public IEnumerator<T> GetEnumerator() { LinkedListNode<T> current = First; while (current != null) { yield return current.Value; current = current.Next; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
static void Main(string[] args) { var list = new LinkedList<int>(); list.AddLast(3); list.AddLast(4); foreach (int element in list) Console.WriteLine(element); Console.ReadKey(); }
查看IL代码
发现并没拆装箱过程,说明泛型能提供类型安全的类并能提供应用程序的性能,基于以上几点在访问数据层经常使用泛型以期提高代码的重用性,在数据访问泛型类通常需要调用泛型类型中的方法,所以必须给泛型类添加约束,泛型支持几种约束如下:
(1)where T:struct 对于结构约束,类型T必须是值类型
(2)where T:class 类约束指定类型T必须是引用类型
(3)where T:IFoo指定类型T必须实现接口IFoo
(4)where T:new()这是一个构造函数约束,指定类型T必须有一个默认构造函数
(5)where T1:T2这个约束也可以指定,类型T1派生自泛型类型T2、该约束称为裸约束
定义一个实现IComparable泛型接口的实体
public class EmployeModel:IComparable<EmployeModel> { public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } public int CompareTo(EmployeModel other) { if (Age > other.Age) return 1; else if (Age.Equals(other.Age)) return 0; else return -1; } }
定义泛型类,该泛型有两个约束
public class BaseDAL<T> where T : class,IComparable<T> { }
class Program { static void Main(string[] args) { var baseAccess = new BaseDAL<NullableStruct>();//不是引用类型编译错误 var student = new BaseDAL<Student>();//没有实现IComparable接口 var employeeAccess = new BaseDAL<EmployeModel>(); } } public struct NullableStruct { long longnumber; int intnumber; } public class Student { public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } }
总结泛型类可以创建独立于类型的类,泛型方法是独立于类型的方法,接口结构和委托也可以用泛型的方式创建。