从装箱拆箱看泛型

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

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

时间: 2024-11-18 05:46:28

从装箱拆箱看泛型的相关文章

Java之集合初探(二)Iterator(迭代器),collections,打包/解包(装箱拆箱),泛型(Generic),comparable接口

Iterator(迭代器) 所有实现了Collection接口的容器都有一个iterator方法, 用来返回一个实现了Iterator接口的对象 Iterator对象称作迭代器, 用来方便的实现对容器内的元素的遍历 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构.迭代器通常被称为"轻量级"对象,因为创建它的代价小. Java中的Iterator功能比较简单,并且只能单向移动: (1) 使用方法iterator()要求容器返回一个I

C#装箱拆箱

.       装箱和拆箱是一个抽象的概念 2.       装箱是将值类型转换为引用类型 :拆箱是将引用类型转换为值类型        利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类型的值相互转换,将值类型与引用类型链接起来 例如: int val = 100; object obj = val; Console.WriteLine (“对象的值 = {0}", obj); 这是一个装箱的过程,是将值类型转换为引用类型的过程 int val = 100; object obj

读书笔记-C#中装箱拆箱性能

前言 最近在看王涛大神的<你必须知道的.NET(第二版)>一书,嗯,首先膜拜一下…. 在书中的第五章-品味类型中,对装箱与拆箱一节感触很深,概念本身相信每一个程序猿都不陌生,装箱是将值类型转换为引用类型 ,拆箱是将引用类型转换为值类型(ps:不小心又背了一下书),也知道装箱与拆箱过程中将带来性能上的问题,但是在实际的项目中往往会忽略这个问题,将可能带来极大的效率上的问题.问题有多大,反正我哭过. 简单对比测试 在工作之余写了个简单的测试例子,以HashTable.ArraryList.List

值类型&amp;引用类型,装箱&amp;拆箱

值类型:声明一个值类型变量,会在栈上分配一个空间,空间里存储的就是变量的值引用类型:声明一个引用类型变量,会在栈中分配一个空间,存储一个引用,这个引用指向了一个托管堆. 值类型:struct,枚举,数值类型,bool类型引用类型:数组,类,接口,委托(delegate),Object,string 可以看下下面的例子 public class Person { public string Name { get; set; } public int Age { get; set; } } publ

NET中的类型和装箱/拆箱原理

谈到装箱拆箱,DebugLZQ相信给位园子里的博友一定可以娓娓道来,大概的意思就是值类型和引用类型的相互转换呗---值类型到引用类型叫装箱,反之则叫拆箱.这当然没有问题,可是你只知道这么多,那么DebugLZQ建议你花点时间看看楼主这篇文章,继续前几篇博文的风格--浅谈杂侃. 1. .NET中的类型 为了说明装箱和拆箱,那首先必须先说类型.在.NET中,我们知道System.Object类型是所有内建类型的基类.注意这里说的是内建类型,程序员可以编写不继承子自System.Object的类型,这

[Java5新特性]自动装箱/拆箱

自动装箱/拆箱概述 Java中具有基本类型(int,double,float,long,boolean,char,byte,short)和基本类型包装类(Integer,Double,Float,Long,Boolean,Char,Byte,Short),我们实现基本类型与包装类之间的转换基本有两种方式: 一种为JDK5之前的方式,比如Integer i = Integer.valueof(5);(这里5为基本类型int,Integer包装类利用valueof()方法将其转换为Integer类型

Java 装箱 拆箱

Java 自动装箱与拆箱 ??什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供的功能. 一般我们要创建一个类的对象的时候,我们会这样: Class a = new Class(parameter); 当我们创建一个Integer对象时,却可以这样: Integer i = 100; (注意:不是 int i = 100; ) 实际上,执行上面那句代码的时候,系统为我们执行了:Integer i = new Integer(1

java自动装箱拆箱总结

对于java1.5引入的自动装箱拆箱,之前只是知道一点点,最近在看一篇博客时发现自己对自动装箱拆箱这个特性了解的太少了,所以今天研究了下这个特性.以下是结合测试代码进行的总结. 测试代码: int a = 1; Integer b = 1; Integer c = 1; Integer d = 2; Integer e = 3; Integer f = 128; Integer g = 128; Long h = 3L; Double m = 4.0; Double n = 4.0; Float

6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱

6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱 引言 本篇文章主要介绍.NET中6个重要的概念:栈,堆,值类型,引用类型,装箱,拆箱.文章开始介绍当你声明一个变量时,编译器内部发生了什么,然后介绍两个重要的概念:栈和堆:最后介绍值类型和引用类型,并说明一些有关它们的重要原理. 最后通过一个简单的示例代码说明装箱拆箱带来的性能损耗. 声明变量的内部机制 在.NET程序中,当你声明一个变量,将在内存中分配一块内存.这块内存分为三部分:1,变量名:2,变量类型:3,变量值. 下图揭示了声