[你必须知道的.NET] 第四回:后来居上:class和struct

本文将介绍以下内容:

• 面向对象基本概念

• 类和结构体简介

• 引用类型和值类型区别

1. 引言

提起class和struct,我们首先的感觉是语法几乎相同,待遇却翻天复地。历史将接力棒由面向过程编程传到面向对象编程,class和struct也背负着各自的命运前行。在我认为,struct英雄迟暮,class天下独行,最本质的区别是class是引用类型,而struct是值类型,它们在内存中的分配情况有所区别。由此产生的一系列差异性,本文将做以全面讨论。 

2. 基本概念

2.1. 什么是class?

class(类)是面向对象编程的基本概念,是一种自定义数据结构类型,通常包含字段、属性、方法、属性、构造函数、索引器、操作符等。因为是基本的概念,所以不必在此详细描述,读者可以查询相关概念了解。我们重点强调的是.NET中,所有的类都最终继承自System.Object类,因此是一种引用类型,也就是说,new一个类的实例时,对象保存了该实例实际数据的引用地址,而对象的值保存在托管堆(managed heap)中。 

2.2. 什么是struct?

struct(结构)是一种值类型,用于将一组相关的信息变量组织为一个单一的变量实体 。所有的结构都继承自System.ValueType类,因此是一种值类型,也就是说,struct实例分配在线程的堆栈(stack)上,它本身存储了值,而不包含指向该值的指针。所以在使用struct时,我们可以将其当作int、char这样的基本类型类对待。

3. 相同点,不同点

相同点:语法类似。

不同点:

  • class是引用类型,继承自System.Object类;struct是值类型,继承自System.ValueType类,因此不具多态性。但是注意,System.ValueType是个引用类型。
  • 从职能观点来看,class表现为行为;而struct常用于存储数据。
  • class支持继承,可以继承自类和接口;而struct没有继承性,struct不能从class继承,也不能作为class的基类,但struct支持接口继承(记得吗,《第二回:对抽象编程:接口和抽象类》也做过讨论)
  • class可以声明无参构造函数,可以声明析构函数;而struct只能声明带参数构造函数,且不能声明析构函数。因此,struct没有自定义的默认无参构造函数,默认无参构造器只是简单地把所有值初始化为它们的0等价值
  • 实例化时,class要使用new关键字;而struct可以不使用new关键字,如果不以new来实例化struct,则其所有的字段将处于未分配状态,直到所有字段完成初始化,否则引用未赋值的字段会导致编译错误。
  • class可以实抽象类(abstract),可以声明抽象函数;而struct为抽象,也不能声明抽象函数。
  • class可以声明protected成员、virtual成员、sealed成员和override成员;而struct不可以,但是值得注意的是,struct可以重载System.Object的3个虚方法,Equals()、ToString()和GetHashTable()。
  • class的对象复制分为浅拷贝和深拷贝(该主题我们在本系列以后的主题中将重点讲述,本文不作详述),必须经过特别的方法来完成复制;而struct创建的对象复制简单,可以直接以等号连接即可。
  • class实例由垃圾回收机制来保证内存的回收处理;而struct变量使用完后立即自动解除内存分配。
  • 作为参数传递时,class变量是以按址方式传递;而struct变量是以按值方式传递的。

我们可以简单的理解,class是一个可以动的机器,有行为,有多态,有继承;而struct就是个零件箱,组合了不同结构的零件。其实,class和struct最本质的区别就在于class是引用类型,内存分配于托管堆;而struct是值类型,内存分配于线程的堆栈上。由此差异,导致了上述所有的不同点,所以只有深刻的理解内存分配的相关内容,才能更好的驾驭。本系列将再以后的内容中,将引用类型和值类型做以深入的比较和探讨,敬请关注。当然正如本文标题描述的一样,使用class基本可以替代struct的任何场合,class后来居上。虽然在某些方面struct有性能方面的优势,但是在面向对象编程里,基本是class横行的天下。

那么,有人不免会提出,既然class几乎可以完全替代struct来实现所有的功能,那么struct还有存在的必要吗?答案是,至少在以下情况下,鉴于性能上的考虑,我们应该考虑使用struct来代替class:

  • 实现一个主要用于存储数据的结构时,可以考虑struct。
  • struct变量占有堆栈的空间,因此只适用于数据量相对小的场合。
  • 结构数组具有更高的效率。
  • 提供某些和非托管代码通信的兼容性。

所有这些是struct有一席之地的理由,当然也许还有其他的更多说法,只是我不知道罢了:-)

4. 经典示例

4.1 小菜一碟

下面以示例为说明,来阐述本文的基本规则,详细见注释内容。
(1)定义接口

    interface IPerson
    {
        void GetSex();
    }

(2)定义类

    public class Person
    {
        public Person()
        { 
        }

        public Person(string name, int age)
        {
            _name = name;
            _age = age;
        }

        private string _name;

        public string Name
        {
          get { return _name; }
          set { _name = value; }
        }

        private int _age;

        public int Age
        {
          get { return _age; }
          set { _age = value; }
        } 
    }

(3)定义结构

   //可以继承自接口,不可继承类或结构
    struct Family: IPerson
    {
        public string name;
        public int age;
        public bool sex;
        public string country;
        public Person person;

        //不可以包含显式的无参构造函数和析构函数
        public Family(string name, int age, bool sex, string country, Person person)
        {
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.country = country;
            this.person = person;
        }

        //不可以实现protected、virtual、sealed和override成员
        public void GetSex()
        {
            if (sex)
                Console.WriteLine(person.Name + " is a boy.");
            else
                Console.WriteLine(person.Name + " is a girl.");
        }

        public void ShowPerson()
        {
            Console.WriteLine("This is {0} from {1}", new Person(name, 22).Name, country);
        }

        //可以重载ToString虚方法
        public override string ToString()
        {
            return String.Format("{0} is {1}, {2} from {3}", person.Name, age, sex ? "Boy" : "Girl", country);
        }
    }

(4)测试结构和类


猜猜运行结果如何,可以顺便检查检查对这个概念的认识。

4.2 .NET研究

在.NET 框架中,System.Drawing命名空间中的有些元素,如System.Drawing.Point就是实现为struct,而不是class。其原因也正在于以上介绍的各方面的权衡,大家可以就此研究研究,可以体会更多。另外,还有以struct实现的System.Guid。

5. 结论

对基本概念的把握,是我们进行技术深入探索的必经之路,本系列的主旨也是能够从基本框架中,提供给大家一个通向高级技术的必修课程。本文关于class和struct的讨论就是如此,在.NET框架中,关于class和struct的讨论将涉及到对引用类型和值类型的认识,并且进一步将触角伸向变量内存分配这一高级主题,所以我们有必要来了解其运行机制,把握区别和应用场合,以便在平常的系统设计中把握好对这一概念层次的把握。

另外,请大家就以下问题进行讨论,希望能够更加清晰本文的拓展:

  • struct还主要应用在哪些方面?
  •  C++和C#中,关于struct的应用又有所不同,这些不同又有哪些区别?

参考文献

(USA)Stanley B.Lippman, C# Primer

(USA)C# in a Nutshell

 温故知新

[开篇有益]

[第一回:恩怨情仇:is和as]

[第二回:对抽象编程:接口和抽象类] 

[第三回:历史纠葛:特性和属性]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux shell跳板机,用shell开发跳板机

信号列表:在linux中和信号相关的常见命令为kill及trap命令,下来如何利用trap控制跳板机脚本来使用命令:命令:kill -l 和 trap -l企业实战中linux系统的重要信号及说明HUP(1) 挂起INT(2) 中断OUIT(3) 退…

[你必须知道的.NET] 第五回:深入浅出关键字---把new说透

本文将介绍以下内容: 面向对象基本概念new关键字深入浅出对象创建的内存管理 1. 引言 园子里好像没有或者很少把new关键字拿出来说的,那我就占个先机吧,呵呵。那么,我们到底有必要将一个关键字拿出来长篇大论吗?看来…

linux tcp server开源,GitHub - 06linux/cellnet: 高性能,简单,方便的开源服务器网络库...

cellnetcellnet是一个高性能,简单,方便的开源服务器网络库自由混合编码,业务代码无需调整。TCP和html5的应用都可以直接使用cellnet迅速搭建服务器框架。与Java的Netty或Mina网络库类似的Handler机制将给予强大定制功能。特性数据协议支持混合…

[你必须知道的.NET] 第六回:深入浅出关键字---base和this

本文将介绍以下内容: 面向对象基本概念base关键字深入浅出this关键字深入浅出1. 引言 new关键字引起了大家的不少关注,尤其感谢Anders Liu的补充,让我感觉博客园赋予的交流平台真的无所不在。所以,我们就有必要继续这个话题&…

[你必须知道的.NET] 第七回:品味类型---从通用类型系统开始

本文将介绍以下内容: .NET 基础架构概念 类型基础通用类型系统CLI、CTS、CLS的关系简述1. 引言 本文不是连环画,之所以在开篇以图形的形式来展示本文主题,其实就是想更加特别的强调这几个概念的重要性和关注度,同时希望从剖析其关…

[你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理

本文将介绍以下内容: 类型的基本概念 值类型深入引用类型深入值类型与引用类型的比较及应用1. 引言 买了新本本,忙了好几天系统,终于开始了对值类型和引用类型做个全面的讲述了,本系列开篇之时就是因为想写这个主题,…

[你必须知道的.NET]第九回:品味类型---值类型与引用类型(中)-规则无边

接上回[第八回:品味类型---值类型与引用类型(上)-内存有理]的探讨,继续我们关注值类型和引用类型的话题。 本文将介绍以下内容: 类型的基本概念 值类型深入引用类型深入值类型与引用类型的比较及应用1. 引…

[你必须知道的.NET]第十回:品味类型---值类型与引用类型(下)-应用征途

本文将介绍以下内容: 类型的基本概念 值类型深入引用类型深入值类型与引用类型的比较及应用 [下载]:[类型示例代码] 1. 引言 值类型与引用类型的话题经过了两个回合([第八回:品味类型---值类型与引用类型(上&#xf…

[你必须知道的.NET]第十一回:参数之惑---传递的艺术(上)

本文将介绍以下内容: 按值传递与按引用传递深论ref和out比较 参数应用浅析 1. 引言 接上回《第九回:品味类型---值类型与引用类型(中)-规则无边》中,对值类型和引用类型的讨论,其中关于string…

[你必须知道的.NET]第十二回:参数之惑---传递的艺术(下)

本文将介绍以下内容: 按值传递与按引用传递深论ref和out比较 参数应用浅析 接上篇继续,『第十一回:参数之惑---传递的艺术(上)』 4.2 引用类型参数的按值传递 当传递的参数为引用类型时,传递和操作的是指…

[你必须知道的.NET]第十三回:从Hello, world开始认识IL

本文将介绍以下内容: IL代码分析方法 Hello, world历史 .NET学习方法论1. 引言 1988年Brian W. Kernighan和Dennis M. Ritchie合著了软件史上的经典巨著《The C programming Language》,我推荐所有的程序人都有机会重温这本历史上的经典之作。从那时起…

[你必须知道的.NET]第十四回:认识IL代码---从开始到现在

本文将介绍以下内容: IL代码分析方法 IL命令解析 .NET学习方法论 1. 引言 自从『你必须知道.NET』系列开篇以来,受到大家很多的关注和支持,给予了anytao巨大的鼓励和动力。俱往昔,我发现很多的园友都把…

[你必须知道的.NET]第十五回:继承本质论

本文将介绍以下内容: 什么是继承?继承的实现本质1. 引言 关于继承,你是否驾熟就轻,关于继承,你是否了如指掌。 本文不讨论继承的基本概念,我们回归本质,从编译器运行的角度来揭示.NET继承中的…

海岸鸿蒙2018年标准物质,海岸鸿蒙——20年权威的标准物质研制单位

摘要:海岸鸿蒙创办于1996年,是专业从事国家标准物质研发、生产、销售的高新技术企业。海岸鸿蒙创办于1996年,是专业从事国家标准物质研发、生产、销售的高新技术企业。海岸鸿蒙自创办以来,一直秉持“以市场为导向,以科…

[你必须知道的.NET]第十六回:深入浅出关键字---using全接触

本文将介绍以下内容: using指令的多种用法using语句在Dispose模式中的应用1. 引言 在.NET大家庭中,有不少的关键字承担了多种角色,例如new关键字就身兼数职,除了能够创建对象,在继承体系中隐藏基类成员,还在…

华为双系统是鸿蒙系统吗,华为p50pro是鸿蒙系统吗-华为p50pro有双系统吗

华为p50pro的外观基本上就是延续了上一代的风格,没有什么太大的变化,不过影像能力还是非常令人期待的,下面一起来了解华为p50pro的系统方面,看看有没有你暂所不知的消息。近日,有消息曝光了华为 P50 Pro将会有 Harmony…

[你必须知道的.NET]第十七回:貌合神离:覆写和重载

本文将介绍以下内容: 什么是覆写,什么是重载覆写与重载的区别覆写与重载在多态特性中的应用1. 引言 覆写(override)与重载(overload),是成就.NET面向对象多态特性的基本技术之一,两…

鸿蒙系统正式开源,余承东:鸿蒙系统正式开源,友商也可以使用!

鸿蒙OS2.0正式开源:从PPT走向前台,该谁脸红了?在9月10日的开发者大会上,华为鸿蒙2.0发布,已经不再是某些人嘴中的PPT、又哄又蒙的鸿蒙了。说鸿蒙是PPT的言论,在前不久还能看到,如今鸿蒙已经发布…

[你必须知道的.NET]第十八回:对象创建始末(上)

本文将介绍以下内容: 对象的创建过程内存分配分析内存布局研究1. 引言 了解.NET的内存管理机制,首先应该从内存分配开始,也就是对象的创建环节。对象的创建,是个复杂的过程,主要包括内存分配和初始化两个环节。例如&…

[你必须知道的.NET]第十九回:对象创建始末(下)

本文将介绍以下内容: 对象的创建过程内存分配分析内存布局研究接上回[第十八回:对象创建始末(上)],继续对对象创建话题的讨论>>> 2.2 托管堆的内存分配机制 引用类型的实例分配于托管堆上,而线…