从装箱拆箱看泛型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从装箱拆箱看泛型相关的知识,希望对你有一定的参考价值。

.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代码

技术分享
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; }
    }

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

以上是关于从装箱拆箱看泛型的主要内容,如果未能解决你的问题,请参考以下文章

跟王老师学泛型:Java自动装箱与拆箱

WPF中多线程统计拆箱装箱和泛型的运行效率

四:泛型

第6章 泛型

java基础-泛型的优点

再谈C#装箱和拆箱操作