黑马程序员——1C#引用类型与值类型浅析_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 黑马程序员——1C#引用类型与值类型浅析

黑马程序员——1C#引用类型与值类型浅析

 2013/7/27 12:18:55  It_forever  博客园  我要评论(0)
  • 摘要:-----------------------------ASP.Net+Android+IO开发S、.Net培训、期待与您交流!-------------------------------------1引言对值类型和引用类型理解,是理解C#语言基础的重要主题之一。这里会介绍一下内容:1)值类型和引用类型的内存分配情况2)什么时候用值类型,什么时候用引用类型。3)装箱与拆箱1.1值类型与引用类型内存分配值类型:值类型实例要么分配在堆栈上,要么分配在堆上(此时以类型的字段存在托管堆上)
  • 标签:程序 C# 程序员 值类型

 class="Apple-style-span" style="font: bold 14px/21px Tahoma, 'Microsoft Yahei', Simsun; color: #444444; text-transform: none; text-indent: 0px; letter-spacing: normal; word-spacing: 0px; white-space: normal; orphans: 2; widows: 2; font-size-adjust: none; font-stretch: normal; background-color: #ffffff; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;">-----------------------------ASP.Net+Android+IO开发S、.Net培训期待与您交流!-------------------------------------

1引言

        对值类型和引用类型理解,是理解C#语言基础的重要主题之一。这里会介绍一下内容:

    • 1)值类型和引用类型的内存分配情况
    • 2)什么时候用值类型,什么时候用引用类型。
    • 3)装箱与拆箱

 1.1值类型与引用类型内存分配

      值类型:值类型实例要么分配在堆栈上,要么分配在堆上(此时以类型的字段存在托管堆上)。值类型包括简单类型、结构体类型和枚举类型等。定义值类型用关键字Struct。(由于值类型均是密封的,意味着所有定义的值类型均继承字ValueType,且不能派生,但是可以实现接口),如果值类型实例分配在堆栈上,那么方法调用退出后则销毁;若位于GC堆上,则随其所在对象消亡而消亡,主要有GC控制。由于值类型本身包含值,通常分配在堆栈上,访问无需地址转换,分配空间自动释放,所以值类型的效率更高。

     引用类型:引用类型实例分配在托管堆上,而引用类型变量仅仅是持有对象在托管堆上的引用,等于是建立了实例和变量之间的关系。定义引用类型通常用关键字calss,interface,delegate.引用类型实例由GC控制回收。

     如下:在Main方法中定义了值类型变量a,引用类型变量stu   

    class Program
    {
        static void Main(string[] args)
        {
            Pointer a;
            a.X = 100;
            a.Y = 200;
            Console.WriteLine("鼠标当前位置为{0},{1}",a.X,a.Y);

            Student stu=new Student("Josh","Smith",100,Sex.Shemale);
            stu.Learn();
        }
    }

    /// <summary>
    /// 定义一个代表鼠标位置的pointer类
    /// </summary>
    public struct Pointer
    {
        /// <summary>
        /// 水平位置
        /// </summary>
        public  int X ;

        /// <summary>
        /// 垂直位置
        /// </summary>
        public int Y ;
    }

    /// <summary>
    /// 学生
    /// </summary>
    public class Student
    {
        //名称
        private string _lastName;

        //
        private string _firtName;

        //名子
        public string Name
        {
            get { return _firtName+ _lastName; }
        }

        private Sex _sex=Sex.UnKnow;

        /// <summary>
        /// 性别
        /// </summary>
        public Sex Sex
        {
            get { return _sex; }
        }


        /// <summary>
        /// 年龄
        /// </summary>
        public int Age { get; set; }


        /// <summary>
        /// 学习
        /// </summary>
        public void Learn()
        {
            Console.WriteLine("{0}学生学习",Name);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="argFirstName"></param>
        /// <param name="argLastName">名称</param>
        /// <param name="argAge">年龄</param>
        /// <param name="argSex">性别</param>
        public Student(string argFirstName, string argLastName, int argAge, Sex argSex)
        {
            _firtName= argFirstName;
            _lastName= argLastName;
            _sex = argSex;
            Age = argAge;
        }
    }

    /// <summary>
    /// 人的性别
    /// </summary>
    public enum Sex
    {
        /// <summary>
        /// 男性
        /// </summary>
        Man,
        
        /// <summary>
        /// 女性
        /// </summary>
        Women,
        
        /// <summary>
        /// 人妖
        /// </summary>
        Shemale,
   
        /// <summary>
        /// 未知
        /// </summary>
        UnKnow,
    }

值类型变量a,和引用类型变量stu所引用的实例内存概况如下图

 

  1.2什么时候用值类型,什么时候用引用类型?

           引用类型有行为、有多态、有继承,而值类型无多态无继承。可以发现只要用到struct的场合,基本都可以用class替代。那么为什么还要有值类型呢?答案显而易见是效率问题。我们知道引用类型分配在堆上,而堆上的每个对象都有额外的一些成员(同步索引块,类型对象指针(指向类型对象的方法表)),这些都要初始化;另外从托管堆上分配一个对象,可能强制执行一次垃圾回收操作。由上可见软件系统的所有类型全是引用类型,那么引用程序的性能会显著下降。而值类型通常分配在栈上,通常为较小的带有数值含义的数据结构,访问的时候不需要像访问对象那样通过引用类型变量查找,使用完也是自动释放,这样效率就会很高。

           通常定义类型的时候应该优先考虑值类型,因为它的效率更高。值类型一般在以下场合使用:

                1类型本身数据量较小,且主要用于存储数据,表现出明显的数值含义。

                2类型不需要从其他任何类型继承,也不派生出任何其他类型

 相反,选用引用类型的时候会考虑其 1本身数据结构较大;2可能会扩展(即会派生)。目前个人知识有限,貌似也只能想到这两点。

 

3装箱与拆箱

              1概念:装箱与拆箱,就是值类型与引用类型的转换。装箱就是类型数据转换为引用对象,是的可以将之类型是为对象来处理,通常转换为Object类型的或者该值类型实现的任何接口引用类型;拆箱就是引用类型转换为值类型,通常伴随着从堆中复制对象的数据字段的操作。注:只有被装过箱的对象才能被拆箱。

             2先看一段代码

  static void Main(string[] args)
        {
            int x = 100;
            object o = x;//装箱
            int y = (int) o;//拆箱
        }

            装箱过程三步曲:

    • 内存分配:在托管堆中分配内存空间,内存大小为待装箱值类型的大小加上其他额外的内存空间(主要为类型对象指针和同步索引块)
    • 实例拷贝:将值类型的字段值拷贝到新分配的内存中
    • 引用返回:将托管堆中的对象地址返回给新的引用类型变量 (即例子中的对象o)

           拆箱过程两部曲

    •  实例检查:检查对象是否为null,检查对象是否为给定值类型的装箱值,不满足检查条件抛异常
    •  取数据字段地址:从实例中获取数据字段的地址(本例中为o中存储100至的地址)
    •  值拷贝:根据获取的数据字段地址将值拷贝到基于栈的值类型的实例中(本例中的y)

   总结:装箱拆箱会带来大量的性能损失,我们平时写程序的时候应尽可能用FCL提供的重载方法或者泛型。如大家经常用的StringBuilder 用来拼接大量的字符串 比用"+"性能要高的多。 

----------------------------------ASP.Net+Android+IOS开发.Net培训期待与您交流!------------------------------

发表评论
用户名: 匿名