C# 类和继承(使用基类的引用)

article/2025/6/21 8:37:11

使用基类的引用

派生类的实例由基类的实例和派生类新增的成员组成。派生类的引用指向整个类对象,包括
基类部分。

如果有一个派生类对象的引用,就可以获取该对象基类部分的引用(使用类型转换运算符把
该引用转换为基类类型)。类型转换运算符放置在对象引用的前面,由圆括号括起的要被转换成
的类名组成。类型转换将在第17章阐述。将派生类对象强制转换为基类对象的作用是产生的变
量只能访问基类的成员(在被覆写方法中除外,稍后会讨论)。

接下来的几节将阐述使用对象的基类部分的引用来访问对象。我们从观察下面两行代码开
始,它们声明了对象的引用。图8-6阐明了代码,并展示了不同变量所看到的对象部分。

  • 第一行声明并初始化了变量derived,它包含一个MyDerivedClass类型对象的引用。
  • 第二行声明了一个基类类型MyBaseClass的变量,并把derived中的引用转换为该类型,
    给出对象的基类部分的引用。
    • 基类部分的引用被存储在变量mybc中,在赋值运算符的左边。
    • 基类部分的引用“看不到"派生类对象的其余部分,因为它通过基类类型的引用“看”
      这个对象。
MyDerivedClass derived=new MyDerivedClass();      //创建一个对象
MyBaseClass mybc=(MyBaseClass) derived;           //转换引用

派生类的引用可以看到完整的MyDerivedClass对象,而mybc只能看到对象的
MyBaseClass部分

下面的代码展示了两个类的声明和使用。图8-7阐明了内存中的对象和引用。
Main创建了一个MyDerivedClass类型的对象,并把它的引用存储到变量derived中。Main
还创建了一个MyBaseClass类型的变量,并用它存储对象基类部分的引用。当对每个引用调用
print方法时,调用的是该引用所能看到的方法的实现,并产生不同的输出字符串。

class MyBaseClass
{public void Print(){Console.WriteLine("This is the base class.");}
}class MyDerivedClass:MyBaseClass
{public int var1;new public void Print(){Console.WriteLine("This is the derived class.");}
}class Program
{static void Main(){MyDerivedClass derived=new MyDerivedClass();MyBaseClass mybc=(MyBaseClass)derived;       //转换为基类derived.Print();                             //从派生类部分调用Printmybc.Print();                                //从基类部分调用Print//mybc.var1=5;                               //错误:基类引用无法访问派生类成员}
}

对派生类和基类的引用

虚方法和覆写方法

在上一节我们看到,当使用基类引用访问派生类对象时,得到的是基类的成员。虚方法可以
使基类的引用访问“升至“派生类内。
可以使用基类引用调用派生类的方法,只需满足下面的条件。

  • 派生类的方法和基类的方法有相同的签名和返回类型。
  • 基类的方法使用virtual标注。
  • 派生类的方法使用override标注。

例如,下面的代码展示了基类方法和派生类方法的virtual及override修饰符。

class MyBaseClass             //基类
{virtual public void Print()....
}class MyDerivedClass:MyBaseClass //派生类
{override void Print()}

图8-8阐明了这组virtual和override方法。注意和上一种情况(用new隐藏基类成员)相
比在行为上的区别。

  • 当使用基类引用(mybc)调用Print方法时,方法调用被传递到派生类并执行,因为:
    • 基类的方法被标记为virtual;
    • 在派生类中有匹配的override方法。
  • 图8-8阐明了这一点,显示了一个从virtual Print方法后面开始,并指向overridePrint
    方法的箭头。

虚方法和覆写方法

下面的代码和上一节中的相同,但这一次,方法上标注了virtual和override。产生的结果
和前一个示例有很大不同。在这个版本中,对基类方法的调用实际调用了子类中的方法。

class MyBaseClass
{virtual public void Print(){Console.WiteLine("This is the base class.");}
}class MyDerivedClass:MyBaseClass
{public override void Print(){Console.WriteLine("This is the derived class.");}
}class Program
{static void Main(){MyDerivedClass derived=new MyDerivedClass();MyBaseClass mybc=(MyBaseClass)derived; //强制转换成基类derived.Print();mybc.Print();}
}

其他关于virtual和override修饰符的重要信息如下。

  • 覆写和被覆写的方法必须有相同的可访问性。例如,这种情况是不可以的:被覆写的方
    法是private的,而覆写方法是public的。
  • 不能覆写static方法或非虚方法。
  • 方法、属性和索引器(前一章阐述过),以及另一种成员类型一一事件(将在后面阐述),
    都可以被声明为virtual和override。

覆写标记为override的方法

覆写方法可以在继承的任何层次出现。

  • 当使用对象基类部分的引用调用一个被覆写的方法时,方法的调用被沿派生层次上溯执
    行,一直到标记为override的方法的最高派生(most-derived)版本。
  • 如果在更高的派生级别有该方法的其他声明,但没有被标记为override,那么它们不会
    被调用。

例如,下面的代码展示了3个类,它们形成了一个继承层次:MyBaseClass、MyDerivedClass
和SecondDerived。所有这3个类都包含名为Print的方法,并带有相同的签名。在MyBaseClass
中,Print被标记为virtual。在MyDerivedClass中,它被标记为override。在类SecondDerived
中,可以使用override或new声明方法Print。让我们看一看在每种情况下将发生什么。

class MyBaseClass  //基类
{virtual public void Print(){COnsole.WriteLine("This is the base class.");}
}class MyDerivedClass:MyBaseClass   //派生类
{override void Print(){Console.WriteLine("This is the derived class.");}}class SecondDerived:MyDerivedClass   //最高派生类
{...//在后面给出
}

情况1:使用override声明Print

如果把SecondDerived的Print方法声明为override,那么它会覆写方法的两个低派生级别的
版本,如图8-9所示。如果一个基类的引用被用于调用Print,它会向上传递,一直到类secondDerived
中的实现。

执行被传递到多层覆写链的顶端
下面的代码实现了这种情况。注意方法Main的最后两行代码。

  • 两条语句中的第一条使用最高派生类SecondDerived的引用调用Print方法。这不是通过
    基类部分的引用的调用,所以它将会调用SecondDerived中实现的方法。
  • 而第二条语句使用基类MyBaseClass的引用调用Print方法。
class SecondDerived:MyDerivedClass
{override public void Print(){Console.WriteLine("This is the second derived class.");}
}class Program
{static void Main(){SecondDerived derived=new SecondDerived();//使用SecondDerivedMyBaseClass mybc=(MybaseClass)derived;    //使用MyBaseClassderived.Print();mybc.Print();}
}

结果是:无论Print是通过派生类调用还是通过基类调用的,都会调用最高派生类中的方法。
当通过基类调用时,调用沿着继承层次向上传递。这段代码产生以下输出:

This is the second derived class.
This is the second derived class.

2.情况2:使用new声明Print

相反,如果将SecondDerived中的Print方法声明为new,则结果如图8-10所示。Main和上
一种情况相同。

class SecondDerived:MyDerivedClass
{new public void Print(){Console.WriteLine("This is the second derived class.");}
}class Program
{static void Main(){SecondDerived derived=new SecondDerived();//使用SecondDerivedMyBaseClass mybc=(MyBaseClass)derived;    //使用MyBaseClassderived.Print();mybc.Print();}
}

结果是:当通过SecondDerived的引用调用方法Print时,SecondDenved中的方法被执行,
正如所期待的那样。然而,当通过MyBaseClass的引用调用Print方法时,方法调用只向上传递
了一级,到达类MyDerived,在那里它被执行。两种情况的唯一不同是SecondDerived中的方法
使用修饰符override还是修饰符new声明。
这段代码产生以下输出:

This is the second derived class.
This is the derived class.

隐藏覆写的方法

覆盖其他成员类型

在之前的几节中,我们已经学习了如何在方法上使用virtual/override。在属性、事件以及
索引器上的用法也是一样的。例如,下面的代码演示了名为MyProperty的只读属性,其中使用
了virtual/override。

class MyBaseClass
{private int _myInt=5;virtual public int MyProperty{get{return _myInt;}}
}class MyDerivedClass:MyBaseClass
{private int _myInt=10;override public int  MyProperty {get{return _myInt;}}
}class Program
{static void Main(){MyDerivedClass derived=new MyDerivedClass();MyBaseClass mybc=(MyBaseClass)derived;Console.WriteLine(derived.MyProperty);Console.WriteLine(mybc.MyProperty);}
}

http://www.hkcw.cn/article/aujNMfsITn.shtml

相关文章

VMvare 创建虚拟机 安装CentOS7,配置静态IP地址

创建虚拟机 安装CentOS7 设置网络模式 设置静态ip vim /etc/sysconfig/network-scripts/ifcfg-ens33 systemctl restart network

python:PyMOL 能处理 *.pdb 文件吗?

PyMOL 完全可以打开并处理 PDB(Protein Data Bank)文件,这是 PyMOL 最主要的功能之一。PDB 格式是结构生物学领域的标准文件格式,专门用于存储生物大分子(如蛋白质、核酸)的三维结构数据。 在 PyMOL 中打开…

【数据治理】要点整理-信息技术数据质量评价指标-GB/T36344-2018

导读:指标为数据质量评估提供了一套系统化、标准化的框架,涵盖规范性、完整性、准确性、一致性、时效性、可访问性六大核心指标,助力组织提升数据处理效率、支持决策制定及业务流程优化,确保数据在数据生存周期各阶段的质量可控。…

【Redis】hash 类型

hash 一. hash 类型介绍二. hash 命令hset、hgethexists、hdelhkeys、hvals、hgetallhmset、hmgethlen、hstrlen、hsetnxhincrby、hincrbyfloat 三. hash 命令小结四. hash 内部编码方式五. hash 的应用场景缓存功能缓存方式对比 一. hash 类型介绍 哈希表在日常开发中&#x…

ubuntu/windows系统下如何让.desktop/.exe文件 在开机的时候自动运行

目录 1,​​让 .desktop 文件在 Ubuntu 开机时自动启动​ 1.1 创建 autostart 目录(如果不存在)​ ​ 1.2 将 .desktop 文件复制到 autostart 目录​ ​ 1.3 确保 .desktop 文件有可执行权限​ 2,windows 2.1 打开「启动」文件夹​…

1-Wire 一线式总线:从原理到实战,玩转 DS18B20 温度采集

引言 在嵌入式系统中,通信总线是连接 CPU 与外设的桥梁。从 I2C、SPI 到 UART,每种总线都有其独特的应用场景。而本文要介绍的1-Wire 一线式总线,以其极简的硬件设计和独特的通信协议,在温度采集、身份识别等领域大放异彩。本文将…

有机黑鸡蛋与普通鸡蛋:差异剖析与选购指南

在我们的日常饮食结构里,鸡蛋始终占据着不可或缺的位置,是人们获取营养的重要来源。如今,市场上鸡蛋种类丰富,除了常见的普通鸡蛋,有机黑鸡蛋也逐渐崭露头角,其价格通常略高于普通鸡蛋。这两者究竟存在哪些…

Fastapi 学习使用

Fastapi 学习使用 Fastapi 可以用来快速搭建 Web 应用来进行接口的搭建。 参考文章:https://blog.csdn.net/liudadaxuexi/article/details/141062582 参考文章:https://blog.csdn.net/jcgeneral/article/details/146505880 参考文章:http…

数字化转型进阶:精读41页华为数字化转型实践【附全文阅读】

该文档聚焦华为数字化转型实践,核心内容如下: 转型本质与目标:数字化转型是通过数字技术穿透业务,实现物理世界与数字世界的融合,目标是支撑主业成功、提升体验与效率、探索模式创新。华为以 “平台 服务” 为核心&am…

共享内存-systemV

01. 共享内存简述 共享内存是一个允许多个进程直接访问同一块物理内存区域的进程通信工具,因其本身不涉及用户态与核心态之间转换,故效率最佳。为了使用一个共享内存段,一般需要以下几个步骤: 调用shmget()创建一个新共享内存段…

大语言模型值ollama使用(1)

ollama为本地调用大语言模型提供了便捷的方式。下面列举如何在windows系统中快捷调用ollama。 winR打开运行框,输入cmd 1、输入ollama list 显示已下载模型 2、输入ollama pull llama3 下载llama3模型 3、 输入 ollama run llama3 运行模型 4、其他 ollama li…

【基础算法】高精度(加、减、乘、除)

文章目录 什么是高精度1. 高精度加法解题思路代码实现 2. 高精度减法解题思路代码实现 3. 高精度乘法解题思路代码实现 4. 高精度除法 (高精度 / 低精度)解题思路代码实现 什么是高精度 我们平时使用加减乘除的时候都是直接使用 - * / 这些符号,前提是进行运算的数…

uni-data-picker级联选择器、fastadmin后端api

记录一个部门及部门人员选择的功能,效果如下: 组件用到了uni-ui的级联选择uni-data-picker 开发文档:uni-app官网 组件要求的数据格式如下: 后端使用的是fastadmin,需要用到fastadmin自带的tree类生成部门树 &#x…

MonitorSDK_性能监控(从Web Vital性能指标、PerformanceObserver API和具体代码实现)

性能监控 性能指标 在实现性能监控前,先了解Web Vitals涉及的常见的性能指标 Web Vitals 是由 Google 推出的网页用户体验衡量指标体系,旨在帮助开发者量化和优化网页在实际用户终端上的性能体验。Web Vitals 强调“以用户为中心”的度量,而…

Kubernetes架构与核心概念深度解析:Pod、Service与RBAC的奥秘

🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言:云原生时代的操作系统 在云原生技术浪潮中,Kubernetes(简称K8s)已成为容器编排领域的"分布式操…

enumiax:IAX 协议用户名枚举器!全参数详细教程!Kali Linux教程!

简介 enumIAX 是一个 Inter Asterisk Exchange 协议用户名暴力枚举器。enumIAX 可以以两种不同的模式运行;顺序用户名猜测或字典攻击。 enumIAX 可以以两种不同的模式运行:顺序用户名猜测或字典攻击。 顺序用户名猜测 在顺序用户名猜测模式下&#xf…

《深入解析SPI协议及其FPGA高效实现》-- 第一篇:SPI协议基础与工作机制

第一篇:SPI协议基础与工作机制 1. 串行外设接口导论 1.1 SPI的核心定位 协议本质 : 全双工同步串行协议(对比UART异步、IC半双工)核心优势 : 无寻址开销(通过片选直连)时钟速率可达100MHz&…

C++语法系列之模板进阶

前言 本次会介绍一下非类型模板参数、模板的特化(特例化)和模板的可变参数&#xff0c;不是最开始学的模板 一、非类型模板参数 字面意思,比如&#xff1a; template<size_t N 10> 或者 template<class T,size_t N 10>比如&#xff1a;静态栈就可以用到&#…

STL-list

1.list概述 List 并非 vector 与 string 那样连续的内存空间&#xff0c;list 每次插入或删除一个元素&#xff0c;都会新配置或释放一个元素的空间&#xff0c;所以list对于空间的使用很充分&#xff0c;一点也没有浪费&#xff0c;对于任意位置的插入或删除元素&#xff0c;时…

导入Maven项目

目录 5. 5.1 导入方法1 5.2 导入方法2 5.1 导入方法1 建议选择pom.xml文件导入 导入成功 5.2 导入方法2 导入成功