当前位置: 首页 > article >正文

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

使用基类的引用

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

如果有一个派生类对象的引用,就可以获取该对象基类部分的引用(使用类型转换运算符把
该引用转换为基类类型)。类型转换运算符放置在对象引用的前面,由圆括号括起的要被转换成
的类名组成。类型转换将在第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);}
}

相关文章:

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

使用基类的引用 派生类的实例由基类的实例和派生类新增的成员组成。派生类的引用指向整个类对象,包括 基类部分。 如果有一个派生类对象的引用,就可以获取该对象基类部分的引用(使用类型转换运算符把 该引用转换为基类类型)。类…...

进程间通信(消息队列)

目录 一 原理 二 API 1. ftok 2. msgget 3. msgctl 4. msgsnd 5. msgrcv 三 demo代码 四 基于责任链模式和消息队列对数据处理 1. 什么是责任链模式 2. 下面基于责任链模式来对消息队列获取的消息进行处理 前置 其实system v 版本的进程间通信,设计的接…...

Linux gron 命令使用详解

简介 gron 是一个独特的命令行工具,用于将 JSON 数据转换为离散的、易于 grep 处理的赋值语句格式。它的名字来源于 “grepable on” 或 “grepable JSON”,主要解决在命令行中处理复杂 JSON 数据的难题。 核心价值 gron 的核心是将 JSON 数据展平为类…...

Nginx--手写脚本压缩和切分日志(也适用于docker)

原文网址:Nginx--手写脚本压缩和切分日志(也适用于docker)_IT利刃出鞘的博客-CSDN博客 简介 本文介绍nginx如何手写脚本压缩和切分日志。 1.创建切分日志的脚本 创建脚本文件:/work/tmp/nginx-log_sh(后边要用run-…...

OpenCv高阶(十八)——dlib人脸检测与识别

文章目录 一、dlib库是什么?二、opencv库与dlib库的优缺点对比1、opencv优缺点2、dlib库优缺点 三、dlib库的安装1、在线安装2、本地安装 四、dlib库的人脸检测器1. 基于 HOG 的检测器2. 基于 CNN 的检测器 五、dlib人脸检测的简单使用1、导入必要库2、初始化人脸检…...

中山大学无人机具身导航新突破!FlightGPT:迈向通用性和可解释性的无人机视觉语言导航

作者:Hengxing Cai 1 , 2 ^{1,2} 1,2, Jinhan Dong 2 , 3 ^{2,3} 2,3, Jingjun Tan 1 ^{1} 1, Jingcheng Deng 4 ^{4} 4, Sihang Li 2 ^{2} 2, Zhifeng Gao 2 ^{2} 2, Haidong Wang 1 ^{1} 1, Zicheng Su 5 ^{5} 5, Agachai Sumalee 6 ^{6} 6, Renxin Zhong 1 ^{1} …...

WIN11+CUDA11.8+VS2019配置BundleFusion

参考: BundleFusion:VS2019 2017 ,CUDA11.5,win11,Realsense D435i离线数据包跑通,环境搭建 - 知乎 Win10VS2017CUDA10.1环境下配置BundleFusion - 知乎 BundleFusionWIN11VS2019 CUDA11.7环境配置-CSDN博客 我的环境:Win 11…...

WPF prism

Prism Prism.Dryloc 包 安装 Nuget 包 - Prism.DryIoc 1. 修改 App.xaml 修改 App.xaml 文件&#xff0c;添加 prism 命名空间, 继承由 Application → PrismApplication&#xff0c;删除默认启动 url, StartupUri“MainWindow.xaml” <dryioc:PrismApplicationx:Class…...

实时同步缓存,与阶段性同步缓存——补充理解《补充》

根据 Redis 缓存的数据与 DBMS 中数据的同步性划分&#xff0c;缓存一般可划分为两类&#xff1a;实时同步缓存&#xff0c;与阶段性同步缓存。 实时同步缓存是指&#xff0c;DBMS 中数据更新后&#xff0c;Redis 缓存中的存放的相关数据会被立即清 除&#xff0c;以促使再有对…...

[Redis] Redis:高性能内存数据库与分布式架构设计

标题&#xff1a;[Redis] 浅谈分布式系统 水墨不写bug 文章目录 一、什么是Redis&#xff1f;一、核心定位二、核心优势三、典型应用场景四、Redis vs 传统数据库 二、架构选择与设计1、单机架构&#xff08;应用程序 数据库服务器&#xff09;2、应用程序和数据库服务器分离3…...

Mobaxterm解锁Docker

Mobaxterm是一款功能强大的终端模拟器和SSH客户端&#xff0c;它支持Windows、Linux和Mac操作系统&#xff0c;对于使用Docker的开发者和运维人员来说&#xff0c;Mobaxterm是一个非常有用的工具。本文将深入解析Mobaxterm&#xff0c;并分享一些使用Docker时的高效技巧。 Mob…...

React 第四十九节 Router中useNavigation的具体使用详解及注意事项

前言 useNavigation 是 React Router 中一个强大的钩子&#xff0c;用于获取当前页面导航的状态信息。 它可以帮助开发者根据导航状态优化用户体验&#xff0c;如显示加载指示器、防止重复提交等。 一、useNavigation核心用途 检测导航状态&#xff1a;判断当前是否正在进行…...

【JavaEE】Spring事务

目录 一、事务简介二、Spring事务的实现2.1 事务的操作2.2 分类2.2.1 Spring编程式事务2.2.2 Spring 声明式事务 Transactional2.2.2.1 Transactional 详解2.2.2.1.1 rollbackFor2.2.2.1.2 Isolation2.2.2.1.3 propagation 一、事务简介 事务&#xff1a;事务是⼀组操作的集合…...

Flink 状态管理深度解析:类型与后端的全面探索

在流处理场景中,数据往往是连续且无界的,为了准确处理这些数据并维持计算的连续性,Flink 引入了状态管理机制。Flink 的状态管理包含状态类型和状态后端两大部分,它们相辅相成,共同为作业的可靠性、容错性和性能提供保障。接下来,我们将深入探究 Flink 状态管理中状态类型…...

Android15 userdebug版本不能remount

背景描述&#xff1a; 最近调试Android Vendor Hal的时候发现一个奇怪的现象: android userdebug版本刷到设备中&#xff0c;执行adb root没提示错误&#xff0c;但是没有获取到root权限。 Android设备运行的系统版本有三种情况&#xff1a;user版本、userdebug版本和eng版本…...

R包安装报错解决案例系列|R包使用及ARM架构解决data.table安装错误问题

有不少同学是Mac系统的&#xff0c;分析过程中会发现部分R包总是安装不成功&#xff0c;这是因为部分R包基于windowsx86架构编译的&#xff0c;最常见的就是含 C/C/Fortran 的包&#xff0c;对于初学者都是建议linux和win去做&#xff0c;Windows 通常直接安装预编译好的二进制…...

k8s Headless Service

Kubernetes 无头服务&#xff08;Headless Service&#xff09;配置与使用场景 1.无头服务概述 无头服务&#xff08;Headless Service&#xff09;是 Kubernetes 中的一种特殊服务类型&#xff0c;它**不分配集群 IP&#xff08;ClusterIP&#xff09;&#xff0c;而是直接暴露…...

Linux上安装MongoDB

目录 一、在Linux系统安装MongoDB服务器 1、下载MongoDB 2、上传MongoDB并解压 3、创建必要目录 4、配置环境变量 5、创建配置文件 6、启动命令 7、验证安装 二、在Linux系统安装MongoDB客户端Shell 1、下载MongoDB Shell 2、上传MongoDB Shell并解压 3、配置环境变…...

Redis最佳实践——安全与稳定性保障之访问控制详解

Redis 在电商应用的安全与稳定性保障之访问控制全面详解 一、安全访问控制体系架构 1. 多层级防护体系 #mermaid-svg-jpkDj2nKxCq9AXIW {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-jpkDj2nKxCq9AXIW .error-ico…...

【华为开发者空间 x DeepSeek】服务器运行Ollama并在本地调用

文章概述 本文介绍了如何在 华为开发者空间 中快速部署并使用 Ollama 模型运行框架&#xff0c;并结合 deepseek-r1 模型进行本地或远程交互推理。内容涵盖环境准备、模型配置、网卡绑定、内网穿透、API调用等多个环节&#xff0c;适合希望在华为云上快速搭建本地类大模型推理…...

Halcon

regiongrowing — Segment an image using regiongrowing. get_obj_class:获取图像的类别名 get_region_points&#xff1a;获取区域的像素 get_contour_xld&#xff1a;获取xld像素点坐标 get_polygon_xld&#xff1a;获取多边形的数据 get_region_polygon:计算一个区域的…...

STM32之IIC(重点)和OLED屏

内部集成电路概述 基本概念 内部集成电路&#xff08;Inter Integrated Circuit&#xff09;的简称叫做IIC或者I2C&#xff0c;是一种简单的、半双工同步通信的串行通信接口&#xff0c;IIC总线是上世纪80年代&#xff08;1982年&#xff09;由飞利浦公司设计出来&#xff0c…...

学习海康VisionMaster之表面缺陷滤波

一&#xff1a;进一步学习了 今天学习下VisionMaster中的表面缺陷滤波&#xff1a;简单、无纹理背景的表面缺陷检测&#xff0c;可以检测表面的异物&#xff0c;缺陷&#xff0c;划伤等 二&#xff1a;开始学习 1&#xff1a;什么表面缺陷滤波&#xff1f; 表面缺陷滤波的核心…...

游戏引擎学习第314天:将精灵拆分成多个层

回顾并为今天的工作做准备 我们今天继续昨天开始的工作&#xff0c;现在我们要回到渲染中处理 Z 值的最终环节。我们目前已经有一个我们认为还算合理的排序方式&#xff0c;虽然可能还需要在接下来的过程中进行一些调整&#xff0c;但总体上已经有了一个明确的方向。 我们已经…...

【学习笔记】深度学习-梯度概念

一、定义 梯度向量不仅表示函数变化的速度&#xff0c;还表示函数增长最快的方向 二、【问】为什么说它表示方向&#xff1f; 三、【问】那在深度学习梯度下降的时候&#xff0c;还要判断梯度是正是负来更新参数吗&#xff1f; 假设某个参数是 w&#xff0c;损失函数对它的…...

【数据结构】图的存储(邻接矩阵与邻接表)

图的存储结构 因为图中既有节点&#xff0c;又有边(节点与节点之间的关系)&#xff0c;因此&#xff0c;在图的存储中&#xff0c;只需要保存&#xff1a;节点和边关系即可。 节点保存比较简单&#xff0c;只需要一段连续空间即可&#xff0c;那边关系该怎么保存呢&#xff1…...

tomcat yum安装

使用yum安装 yum install -y java-1.7.0-openjdk* tomcat* --disablerepoepel## java-1.7.0-openjdk* 注意&#xff1a;最终安装的是java-1.8.0版本## --disablerepoepel 禁用&#xff1a;EPEL源&#xff0c;防止版本冲突 java -version (2) 启停&#xff1a;Tomcat 7 s…...

【Elasticsearch】suggest_mode

suggest_mode 是 Elasticsearch 中 term suggester 和 phrase suggester 的一个参数&#xff0c;用于控制建议的生成方式。它有以下三种模式&#xff1a; 1. missing&#xff1a;默认值。仅对索引中不存在的词项提供建议。如果输入的词已经在索引中存在&#xff0c;则不会生成建…...

数据库只更新特定字段的两种方式(先读后写 vs. 动态组织 SQL)-golang SQLx 实现代码(动态组织 SQL)

文章目录 数据库只更新特定字段的两种方式&#xff08;先读后写 vs. 动态组织 SQL&#xff09;go语言例子使用GORM的示例&#xff08;最常用的Go ORM库&#xff09;使用SQLx的两种更新方式实现golang SQLx 实现代码&#xff08;动态组织 SQL&#xff09; 数据库只更新特定字段的…...

从翻译后修饰角度解析人工合成途径与底盘细胞的适配性-文献精读136

Compatibility between synthetic pathway and chassis cells from the viewpoint of post-translational modifications 从翻译后修饰角度解析人工合成途径与底盘细胞的适配性 摘要 揭示工程化设计的人工合成途径与底盘细胞整体代谢网络的交互作用及适配性机制是合成生物学研…...