首页 > 代码库 > 从装箱拆箱看泛型

从装箱拆箱看泛型

.NET很容易把值类型转换为引用类型,所以可以在需要对象的任意地方使用值类型。例如int可以赋予一个对象,从值类型转换为引用类型称为装箱。如果方法需要把一个对象作为参数,同时传递一个值类型,装箱操作就会自动进行。另一方面,装箱的值类型可以使用拆箱操作转换为值类型。

定义一个一般的、非泛型的简化链表类,它可以包含任意类型的对象,在链表中,一个元素引用下一个元素。所以必须创建一个类,它将对象封装在链表中,并引用下一个对象。类LinkedListNode包含一个属性Value,该属性用构造函数初始化。另外LinkedListNode类包含对链表中下一个元素和上一个元素的引用,这些元素都可以从属性中访问。

先定义LinkedListNode类

 public class LinkedListNode    {        public LinkedListNode(object value)        {            this.Value =http://www.mamicode.com/ 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 =http://www.mamicode.com/ 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代码

技术分享
IL_0000: nop        IL_0001: newobj instance void class PraticeCharter01.LinkedList`1<int32>::.ctor()        IL_0006: stloc.0        IL_0007: ldloc.0        IL_0008: ldc.i4.3        IL_0009: callvirt instance class PraticeCharter01.LinkedListNode`1<!0> class PraticeCharter01.LinkedList`1<int32>::AddLast(!0)        IL_000e: pop        IL_000f: ldloc.0        IL_0010: ldc.i4.4        IL_0011: callvirt instance class PraticeCharter01.LinkedListNode`1<!0> class PraticeCharter01.LinkedList`1<int32>::AddLast(!0)        IL_0016: pop        IL_0017: nop        IL_0018: ldloc.0        IL_0019: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class PraticeCharter01.LinkedList`1<int32>::GetEnumerator()        IL_001e: stloc.1        .try        {            IL_001f: br.s IL_002f            // loop start (head: IL_002f)                IL_0021: ldloc.1                IL_0022: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()                IL_0027: stloc.2                IL_0028: ldloc.2                IL_0029: call void [mscorlib]System.Console::WriteLine(int32)                IL_002e: nop                IL_002f: ldloc.1                IL_0030: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()                IL_0035: brtrue.s IL_0021            // end loop            IL_0037: leave.s IL_0044        } // end .try
IL Code

发现并没拆装箱过程,说明泛型能提供类型安全的类并能提供应用程序的性能,基于以上几点在访问数据层经常使用泛型以期提高代码的重用性,在数据访问泛型类通常需要调用泛型类型中的方法,所以必须给泛型类添加约束,泛型支持几种约束如下:

(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; }    }

总结泛型类可以创建独立于类型的类,泛型方法是独立于类型的方法,接口结构和委托也可以用泛型的方式创建。

从装箱拆箱看泛型