.NET继承本质论

Diffusion Model原理详解 Diffusion Model(扩散模型 )对标的是生成对抗网络(GAN),本文将用通俗的语言和公式为大家介绍Diffusion Model,并且结合公式为大家梳理Diffusion Model的代码。 阅读详情
1. 引言

关于继承,你是否驾熟就轻,关于继承,你是否了如指掌。

本文不讨论继承的基本概念,我们回归本质,从编译器运行的角度来揭示.NET继承中的运行本源,来发现子类对象是如何实现了对父类成员与方法的继承,以最为简陋的示例来揭示继承的实质,阐述继承机制是如何被执行的,这对于更好的理解继承,是必要且必然的。

2. 分析

下面首先以一个简单的动物继承体系为例,来进行说明:

    public abstract class Animal
    {
        public abstract void ShowType();
        public void Eat()
        {
            Console.WriteLine("Animal always eat.");
        }
    }
    public class Bird: Animal
    {
        private string type = "Bird";
        public override void ShowType()
        {
            Console.WriteLine("Type is {0}", type);
        }
        private string color;
        public string Color
        {
            get { return color; }
            set { color = value; }
        }
    }
    public class Chicken : Bird
    {
        private string type = "Chicken";
        public override void ShowType()
        {
            Console.WriteLine("Type is {0}", type);
        }
        public void ShowColor()
        {
            Console.WriteLine("Color is {0}", Color);
        }
    }


然后,在测试类中创建各个类对象,由于Animal为抽象类,我们只创建Bird对象和Chicken对象。

    public class TestInheritance
    {
        public static void Main()
        {
            Bird bird = new Bird();
            Chicken chicken = new Chicken();
        }
    }


下面我们从编译角度对这一简单的继承示例进行深入分析,从而了解.NET内部是如何实现我们强调的继承机制。

(1)我们简要的分析一下对象的创建过程:

            Bird animal = new Bird();

Bird bird创建的是一个Bird类型的引用,而new Bird()完成的是创建Bird对象,分配内存空间和初始化操作,然后将这个对象赋给bird引用,也就是建立bird引用与Bird对象的关联。

(2)我们从继承的角度来分析在编译器编译期是如何执行对象的创建过程,因为继承的本质就体现于对象的创建过程。

在此我们以Chicken对象的创建为例,首先是字段,对象一经创建,会首先找到其父类Bird,并为其字段分配存储空间,而Bird也会继续找到其父类Animal,为其分配存储空间,依次类推直到递归结束,也就是完成System.Object内存分配为止。我们可以在编译器中单步执行的方法来大致了解其分配的过程和顺序,因此,对象的创建过程是按照顺序完成了对整个父类及其本身字段的内存创建,并且字段的存储顺序是由上到下排列,object类的字段排在最前面,其原因是如果父类和子类出现了同名字段,则在子类对象创建时,编译器会自动认为这是两个不同的字段而加以区别。

然后,是方法列表的创建,必须明确的一点是对象的创建是在运行时,而方法列表的创建是在编译时,在运行时创建对象的过程只是为方法列表加一个指向的指针,将对象与其动态方法列表相关联起来,因此方法列表是先于对象而存在的。类似于字段的创建过程,在编译期方法列表的编译也是父类在先子类在后,原因是显而易见的,类Chicken生成方法列表时,首先将Bird的所有方法拷贝一份,然后和Chicken本身的方法列表做以对比,如果有覆写的虚方法则以子类方法覆盖同名的父类方法,同时添加子类的新方法,从而创建完成Chicken的方法列表。这种创建过程也是逐层递归到Object类,并且方法列表中也是按照顺序排列的,父类在前子类在后,其原因和字段大同小异,留待读者自己体味。

结合我们的分析过程,现在将对象创建的过程以简单的图例来揭示其在内存中的分配情形,如下:

从我们的分析,和上面的对象创建过程可见,对继承的本质我们有了更明确的认识,对于以下的问题就有了清晰明白的答案:

  • 继承是可传递的,子类是对父类的扩展,必须继承父类方法,同时可以添加新方法。
  • 子类可以调用父类方法和字段,而父类不能调用子类方法和字段。
  • 虚方法如何实现覆写操作,使得父类指针可以指向子类对象成员。
  • new关键字在虚方法继承中的阻断作用。

你是否已经找到了理解继承、理解动态编译的不二法门。

3. 思考

通过上面的讲述与分析,我们基本上对.NET在编译期的实现原理有了大致的了解,但是还有以下的问题,一定会引起一定的疑惑,那就是:

            Bird bird2 = new Chicken();

这种情况下,bird2.ShowType应该返回什么值呢?而bird2.type有该是什么值呢?有两个原则,是.NET专门用于解决这一问题的:

  • 关注对象原则:调用子类还是父类的方法,取决于创建的对象是子类对象还是父类对象,而不是它的引用类型。例如Bird bird2 = new Chicken()时,我们关注的是其创建对象为Chicken类型,因此子类将继承父类的字段和方法,或者覆写父类的虚方法,而不用关注bird2的引用类型是否为Bird。引用类型不同的区别决定了不同的对象在方法表中不同的访问权限。

 

注意

根据关注对象原则,那么下面的两种情况又该如何区别呢?

            Bird bird2 = new Chicken();
            Chicken chicken = new Chicken();
根据我们上文的分析,bird2对象和chicken对象在内存布局上是一样的,差别就在于其引用指针的类型不同:bird2为Bird类型指针,而chicken为Chicken类型指针。以方法调用为例,不同的类型指针在虚拟方法表中有不同的附加信息作为标志来区别其访问的地址区域,称为offset。不同类型的指针只能在其特定地址区域内进行执行,子类覆盖父类时会保证其访问地址区域的一致性,从而解决了不同的类型访问具有不同的访问权限问题。
 
  • 执行就近原则:对于同名字段或者方法,编译器是按照其顺序查找来引用的,也就是首先访问离它创建最近的字段或者方法,例如上例中的bird2,是Bird类型,因此会首先访问Bird_type(注意编译器是不会重新命名的,在此是为区分起见),如果type类型设为public,则在此将返回“Bird”值。这也就是为什么在对象创建时必须将字段按顺序排列,而父类要先于子类编译的原因了。

 

思考

1. 上面我们分析到bird2.type的值是“Bird”,那么bird2.ShowType()会显示什么值呢?答案是“Type is Chicken”,根据本文上面的分析,想想到底为什么?

2. 关于new关键字在虚方法动态调用中的阻断作用,也有了更明确的理论基础。在子类方法中,如果标记new关键字,则意味着隐藏基类实现,其实就是创建了与父类同名的另一个方法,在编译中这两个方法处于动态方法表的不同地址位置,父类方法排在前面,子类方法排在后面。
 
在.NET中,如果创建一个类,则该类总是在继承。这缘于.NET的面向对象特性,所有的类型都最终继承自共同的根System.Object类。可见,继承是.NET运行机制的基础技术之一,一切皆为对象,一切皆于继承。本文从基础出发,深入本质探索本源,分析疑难比较鉴别。对于什么是继承这个话题,希望每个人能从中寻求自己的答案,理解继承、关注封装、玩转多态是理解面向对象的起点,希望本文是这一旅程的起点。
using System;
namespace testoverload
{
 /// <summary>
 /// Class1 的摘要说明。
 /// </summary>
 class Class1
 {
  /// <summary>
  /// 应用程序的主入口点。
  /// </summary>
   public abstract class Animal
  {
   public abstract void ShowType();
   public void Eat()
   {
    Console.WriteLine("Animal always eat.");
   }
  }
  public class Bird: Animal
  {
   private string type = "Bird";
   public override void ShowType()
   {
    Console.WriteLine("Type is {0}", type);
   }
   private string color;
   public string Color
   {
    get { return color; }
    set { color = value; }
   }
  }
  public class Chicken : Bird
  {
   private string type = "Chicken";
   
   public override void ShowType()
   {
    Console.WriteLine("Type is {0}", type);
   }
   public void ShowColor()
   {
    Console.WriteLine("Color is {0}", Color);
   }
  }
  public class TestInheritance
  {
   public static void Main()
   {
    Bird bird = new Chicken();
    //虽然声明了一个新的chicken对象,但是返回的却是一个bird指针,因此bird不具有ShowColor方法
    Chicken chicken = new Chicken();
    //但是如果将代码改为 Chicken chicken=new Bird(); 则会出错
    
    bird.ShowType();
    
    
    chicken.Color="assdf";
    chicken.ShowColor();
    chicken.ShowType();
    Console.ReadLine();
   }
  }
 }
}
 
 
基于Simulink的光储系统黑启动能力仿真 本文基于Simulink平台构建了光储系统黑启动仿真模型,详细阐述了储能VF控制、光伏孤岛MPPT和同步并网等核心控制策略。通过分阶段协调控制(储能启动→关键负载→光伏接入→非关键负载→同步并网),实现了60秒内恢复供电、母线电压波动<5%的性能指标。仿真结果表明,协调控制策略相比无序启动可显著提升系统稳定性,避免电压崩溃和负载跳闸。文章还提供了完整的建模步骤、核心代码及进阶优化方向,为微电网应急供电设计提供了实用参考。该模型适用于光伏储能系统的黑启动能力验证和优化研究。 阅读详情

相关推荐

OPTISYSTEM实战指南:线性调频连续波激光雷达系统仿真设计

本文提供了一份基于OPTISYSTEM软件的线性调频连续波激光雷达系统仿真实战指南。文章详细阐述了从软件界面熟悉、核心参数设置,到发射端、信道模型、相干接收端完整系统搭建的全过程,并介绍了性能优化与高级仿真技巧,旨在帮助读者快速掌握利用OPTISYSTEM进行低成本、高效率激光雷达系统设计与验证的方法。

weixin_29288313的博客 297

.net---继承和多态

派生类型的每个方法采用基类型的实现代码,除非在派生类型的定义中指定重写该方法的实现代码。如果在派生类中声明了与继承的成员同名的成员,则该重名成员将隐藏从基类继承的同名成员变量。可以定义抽象类型的变量,但其值必须为 null,或者是其派生的非抽象类的实例的引用。一般用于指定该类型具有某类可用的特性,在接口中定义通用的实现代码是没有意义的。一般用于增加现有类型的功能,或许多相关的类型共享一组重要的公共功能的场合。派生类中相同签名方法重新定义了基类中的方法,即隐藏了基类中的同名方法。

醉卧考场君莫笑 1545

画个板子玩一玩STM32F030F4P6,也许是最便宜的32bit MCU

前言,什么是STM32F030F4P6 STM32目前最便宜的芯片之一应该就是这一块,STM32F030F4P6,这一个芯片没有USB,但是其他都有,引脚少,价格低廉,如果找不到原厂,可以买替代的国产芯片,性能也一样。 不管怎么样也是Cortex-M0,比8bit和16bit的性能都强,总的来说就是低价,引脚少,体积小的同时,还容易开发,有很多模拟输入可玩,还有SPI,硬件I2C等等 所以,这一款MCU真的是从理论来说又便宜又好用,非常适合把玩。 画个板,最小系统 STM32F030其..

arenascat的博客 2万+

Altium Designer 21 DRC检查:从新手到高手,这10个常见错误你踩过几个?

本文详细解析Altium Designer 21 DRC检查中的10个高频错误,包括间距违规、线宽违规、未连接网络等常见问题,并提供精准修复方案和实战技巧。通过分层诊断、智能规则设置和三维排查等方法,帮助工程师从新手进阶为高手,提升PCB设计效率和质量。

weixin_26873711的博客 222

中国大学MOOC-翁恺-C语言程序设计习题集-解答汇总

中国大学MOOC-翁恺-C语言程序设计习题集 PAT 习题集 02-0. 整数四则运算(10) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 乔林(清华大学) 本题要求编写程序,计算2个正整数的和、差、积、商并输出。题目保证输入和输出全部在整型范围内。 输入格式: 输入在一行中给出2...

weixin_30691871的博客 3万+

【个人总结】父子类,继承详解

继承 重写

qq_52080086的博客 1139

你必须知道的.net学习总结之继承

  继承是面向对象中类与类的关系,继承的类称为子类,派生类,而被继承类称为父类、基类或超类。 这事书中的一句话,我个人理解继承其实简单,每一个类都可以继承一个父类,被继承的类,可以使用父类的属性以及方法。相当于扩展。 继承与接口 抽象类适合于有族层概念的类间关系,而接口最适合为不同的类提供通用功能。 接口着重于can-do关系,而抽象类则遍重于is-a的关系。 接口多定义对象的行为;抽象...

weixin_33814685的博客 152

source tree clone error:fatal remote error: Repository not found

source tree clone error:fatal remote error: Repository not found

Linda的前端开发路 3077

基于PChmi.dll的PLC报警显示和记录(C#)

本文介绍了如何使用C#编写基于PChmi.dll的程序,实现PLC报警的显示和记录功能。通过订阅PLC的报警事件,我们可以实时显示报警信息,并通过日志文件记录报警历史。PLC(可编程逻辑控制器)在工业自动化领域中扮演着重要的角色,而报警显示和记录是PLC应用中常见的需求。本文将介绍如何使用C#编写基于PChmi.dll的程序,实现PLC报警的显示和记录功能。当然,这只是实现报警显示和记录的基本框架,具体的实现方式可能因不同的PLC和HMI库而有所差异。你需要根据你所使用的具体库和设备进行相应的调整和修改。

ByteLegend的博客 1673

深入了解.NET中继承和多态(上)

注意:在写完文章后很久才发现自己文章中【编译】两个字的让人误解,比如方法槽偏移量是在编译时获得的,其实我想表达的是JIT编译,而不是指IL编译。我大概修改了一些关键地方,但是可能有很多遗漏。大家要自己判断了,哈! 封装、继承、多态是面向对象的最重要的3个特点。但是想真的弄明白他们其中的奥秘还是的费一番功夫。记得在学校学习C++的时候,讲到这个地方,自己早已是一头雾水,当时还在想,弄成pri

CC的技术专栏 5192

lammps教程:非晶sio2建模方法

大家好,我是小马老师。 本文介绍非晶sio2的建模方法。 sio2的建模方法有很多,可以下载cif文件使用atomsk转为data文件,也可以直接在ms中导入sio2结构,这些方法建的sio2都是标准的sio2晶体。 如果要建立非晶状态的sio2,就是把排列规则的sio2变成无序排列的sio2。 非晶建模方法有很多,在lammps中,可以通过高温高压的方法打乱sio2的排列实现非晶建模。 代码中的温度和步数可根据需要自己调整。 下面给出in文件案例代码。 units metal atom

lammps_jiayou的博客 5268

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

本文将介绍以下内容:   • 什么是继承?   • 继承的实现本质 1. 引言   关于继承,你是否驾熟就轻,关于继承,你是否了如指掌。   本文不讨论继承的基本概念,我们回归本质,从编译器运行的角度来揭示.NET继承中的运行本源,来发现子类对象是如何实现了对父类成员与方法的继承,以最为简陋的示例来揭示继承的实质,阐述继承机制是如何被执行的,这对于更好的理解继承,是必要且必然的。

lihuang319的专栏 483

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

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

 本文将介绍以下内容:   • 什么是继承?  • 继承的实现本质  1. 引言  关于继承,你是否驾熟就轻,关于继承,你是否了如指掌。   本文不讨论继承的基本概念,我们回归本质,从编译器运行的角度来揭示.NET继承中的运行本源,来发现子类对象是如何实现了对父类成员与方法的继承,以最为简陋的示例来揭示继承的实质,阐述继承机制是如何被执行的,这对于更好的理解继承,是必要且必然的

mzoy的专栏 784

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

《你必须知道的.net》读书笔记-继承本质论

继承: 继承就是面向对象中类与类之间的一种关系。继承的类成为子类,派生类。被继承的类称为父类,基类 超类。使得子类就有父类的属性跟方法。同时子类也可以通过加入新的属性很方法或者修改父类的属性和方法建立新的类层次。 继承机制体现了面向对象技术红的复用性,扩展性和安全性。为面向对象软件开发与模块话软件架构提供了最基本的技术基础 分类: 1实现继承(单一继承). 2接口继承(多继...

weixin_30339969的博客 79

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

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

引用自:http://www.cnblogs.com/anytao/category/155694.html 作者:Anytao 本文将介绍以下内容: 什么是继承? 继承的实现本质   1. 引言 关于继承,你是否驾熟就轻,关于继承,你是否了如指掌。 本文不讨论继承的基本概念,我们回归本质,从编译器运行的角度来揭示.NET继承中的运行本源...

weixin_34149796的博客 117

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

本文将介绍以下内容:   • 什么是继承?   • 继承的实现本质   1. 引言   关于继承,你是否驾熟就轻,关于继承,你是否了如指掌。   本文不讨论继承的基本概念,我们回归本质,从编译器运行的角度来揭示.NET继承中的运行本源,来发现子类对象是如何实现了对父类成员与方法的继承,以最为简陋的示例来揭示继承的实质,阐述继承机制是如何被执行的,这对于更好的理解继承,是必要且必然的。...

weixin_34391854的博客 75
上一篇: Const的用法
下一篇: Remoting和WebService有什么区别 《转帖》
JYYCOM
博客等级 码龄20年 9粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值