十四、重写与多态

重写、多态

上一讲是,子类对父类横向上的扩展

这一讲是,子类对父类纵向上的扩展

在这里插入图片描述

方法重写

使用override关键字重写父类的方法

将父类原本方法的逻辑更新成新版本的逻辑

注:仅能重写可见的父类成员,并且重写要保持签名一致。

签名一致:对函数来说即,返回类型,名称,参数列表

python中对象有类型,但是变量是没有类型的,变量的类型永远是跟着对象走的。在Python中没有代差,重写就看不到多态的效果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace OverrideExample {internal class Program {static void Main(string[] args) {/* 重写表示:* 在继承链上不同的对象实例调用同一个方法,* 就会触发相对应的版本的方法*/var car = new Car();car.Run();//触发的是Car版本的Run方法//Car is running.var vehicle = new Vehicle();vehicle.Run();//触发的是基类版本的Run方法//I'm running.}}class Vehicle {/* virtual关键字* 表示该方法可被子类重写*/public virtual void Run() {Console.WriteLine("I'm running.");}}class Car : Vehicle {/* override关键字* 表示重写父类的方法* 此时Car这个类中,只有一个Run方法,就是这个类体中的那个重写了的方法*/public override void Run() {Console.WriteLine("Car is running.");}}
}

注:当一个类重写了父类的方法后,再被其他类所继承,那么其他类实际上继承的是Car版本的重写后的方法。

using System;namespace OverrideExample {internal class Program {static void Main(string[] args) {var raseCar = new RaseCar();raseCar.Run();//Car is Running.Console.ReadLine();}}class Vehicle {public virtual void Run() {Console.WriteLine("I'm running.");}}class Car : Vehicle {public override void Run() {Console.WriteLine("Car is running.");}}class RaseCar : Car {/* 此时有子类继承Car* 那么Rase类继承的是Car版本的Run方法*/}
}

方法隐藏

如果签名相同的方法在基类和派生类中都进行了声明,但是该方法没有分别声明为virtualoverride,派生类就会隐藏基类方法(要使用new关键字进行声明)。隐藏的话,也就是看不到了,实际这个方法还存在。

也就是说,派生类中看不到和基类同名的那个方法了。

using System;namespace OverrideExample {internal class Program {static void Main(string[] args) {// 父类引用指向子类对象Animal a = new Dog();// 调用非虚方法,根据引用的静态类型,调用父类的方法a.Eat(); // 输出 Animal eats// 调用虚方法,根据对象的动态类型,调用子类的方法a.Sleep(); // 输出 Dog sleepsConsole.ReadKey();}}// 基类public class Animal {// 非虚方法public void Eat() {Console.WriteLine("Animal eats");}// 虚方法public virtual void Sleep() {Console.WriteLine("Animal sleeps");}}// 子类public class Dog : Animal {/* 虽然继承了父类的同名方法,但是因为子类中也有同名的方法,* 所以子类会隐藏父类的同名的方法,也就是说子类见不到父类的方法* 表现在,子类类型引用的对象访问不到父类的同名方法。* * 隐藏父类的非虚方法*///public new void Eat()//这两者是等同的public void Eat() {Console.WriteLine("Dog eats");}// 重写父类的虚方法public override void Sleep() {Console.WriteLine("Dog sleeps");}}
}

总结一下:

如果使用派生类声明的对象,调用隐藏方法会调用派生类的,如果使用基类声明对象,那么就会调用基类的隐藏方法。

三、虚方法与隐藏方法的区别

1、重写和隐藏的定义
重写:继承时发生,在派生类中重新定义基类中的方法,派生类中的方法和基类的方法是一样的 。例如:基类方法声明为virtual(虚方法),派生类中使用override声明此方法的重写。

隐藏:基类方法不做声明(默认为非虚方法),在派生类中使用new声明此方法的隐藏(new可写可不写)。

2、重写和隐藏的区别
重写(virtaul)时,定义的变量为基类或派生类, 赋值为派生类时,皆调用基类的重写方法(会从派生类中查找有重写则调用 ,没则调用基类方法)。
隐藏(new)时,定义的变量为基类,则调用基类的方法(不管赋值是派生类还是基类),定义的变量为派生类则调用派生类的方法。
————————————————

原文链接:https://blog.csdn.net/qq_44034384/article/details/106652112

多态

主要表现在方法和属性上面。

父类的同一种行为,在不同的子类上有不同的实现,这种实现叫做多态。

C#中的多态是指同一个接口或方法,使用不同的对象或参数,可以实现不同的功能或行为。多态可以提高代码的灵活性和可扩展性,实现对象之间的解耦。

C#中的多态可以分为两种:

  • 静态多态:也叫编译时多态,是指在编译时就确定了函数的调用。静态多态主要通过函数重载运算符重载来实现。函数重载是指在同一个类中,可以定义多个同名但参数不同的函数,根据传入的参数类型和个数来决定调用哪个函数。运算符重载是指可以为用户自定义的类型重新定义运算符的含义和行为,使得运算符可以用于操作这些类型的对象。
  • 动态多态:也叫运行时多态,是指在运行时才确定了函数的调用。动态多态主要通过虚方法抽象类接口来实现。虚方法是指在父类中用virtual关键字声明的方法,可以在子类中用override关键字重写,实现不同的功能。抽象类是指用abstract关键字声明的类,不能被实例化,只能作为基类,包含抽象方法和非抽象方法。抽象方法是指在抽象类中用abstract关键字声明的没有方法体的方法,必须在子类中重写。接口是指用interface关键字声明的一种特殊的抽象类,只包含抽象成员,不能包含字段和构造函数,可以被类实现,实现类必须实现接口中的所有成员。

下面的例子主要是,通过虚方法来展现多态性。最下面有个综合示例。

using System;namespace OverrideExample {internal class Program {static void Main(string[] args) {/* 多态:* 使用一个父类的变量,引用一个子类的实例* 使用这个变量调用被重写的成员的时候,* 总是能调用到 与所引用的对象实例相对应的 版本*/Vehicle veh = new Car();veh.Run();//调用的Car类的版本Vehicle veh2 = new RaseCar();veh2.Run();//调用到RaseCar类的版本Console.ReadLine();}}class Vehicle {public virtual void Run() {Console.WriteLine("I'm running.");}}class Car : Vehicle {public override void Run() {Console.WriteLine("Car is running.");}}class RaseCar : Car {public override void Run() {Console.WriteLine("Rase Car is running.");}}
}
using System;namespace OverrideExample {internal class Program {static void Main(string[] args) {/* 多态:* 使用一个父类的变量,引用一个子类的实例* 使用这个变量调用被重写的成员的时候,* 总是能调用到 与所引用的对象实例相对应的 版本*/Vehicle veh = new Car();veh.Run();//调用的Car类的版本Vehicle veh2 = new RaseCar();veh2.Run();//调用到RaseCar类的版本Console.ReadLine();}}class Vehicle {public virtual void Run() {Console.WriteLine("I'm running.");}}class Car : Vehicle {public override void Run() {Console.WriteLine("Car is running.");}}class RaseCar : Car {public override void Run() {Console.WriteLine("Rase Car is running.");}}
}

综合例子:

using System;namespace OverrideExample {internal class Program {static void Main(string[] args) {// 创建一个Shape数组,存放不同的Shape对象Shape[] shapes = new Shape[2];shapes[0] = new Circle(5, "red");shapes[1] = new Rectangle(10, 8, "blue");// 遍历数组,调用每个对象的Area和Draw方法foreach (Shape shape in shapes) {Console.WriteLine($"The area is {shape.Area()}");shape.Draw();}// 创建一个IColor数组,存放不同的IColor对象IColor[] colors = new IColor[2];colors[0] = new Circle(5, "red");colors[1] = new Rectangle(10, 8, "blue");// 遍历数组,调用每个对象的GetColor方法foreach (IColor color in colors) {Console.WriteLine($"The color is {color.GetColor()}");}}}// 定义一个抽象类Shape,包含一个抽象方法Area和一个虚方法Drawpublic abstract class Shape {public abstract double Area();public virtual void Draw() {Console.WriteLine("Drawing a shape.");}}// 定义一个接口IColor,包含一个抽象方法GetColorpublic interface IColor {string GetColor();}// 定义一个Circle类,继承自Shape类,实现IColor接口,重写Area和Draw方法,实现GetColor方法public class Circle : Shape, IColor {private double _radius;private string _color;public Circle(double radius, string color) {_radius = radius;_color = color;}public override double Area() {return Math.PI * _radius * _radius;}public override void Draw() {Console.WriteLine("Drawing a circle.");}public string GetColor() {return _color;}}// 定义一个Rectangle类,继承自Shape类,实现IColor接口,重写Area和Draw方法,实现GetColor方法public class Rectangle : Shape, IColor {private double _length;private double _width;private string _color;public Rectangle(double length, double width, string color) {_length = length;_width = width;_color = color;}public override double Area() {return _length * _width;}public override void Draw() {Console.WriteLine("Drawing a rectangle.");}public string GetColor() {return _color;}}
}

重写属性

using System;namespace OverrideExample {internal class Program {static void Main(string[] args) {Vehicle veh = new Vehicle();veh.Run();Console.WriteLine(veh.Speed);Vehicle car = new Car();car.Run();Console.WriteLine(car.Speed);Console.ReadLine();}}class Vehicle {private int _speed;public virtual int Speed { get => _speed; set => _speed = value; }public virtual void Run() {Console.WriteLine("I'm running.");_speed = 100;}}class Car : Vehicle {private int _rpm;public override void Run() {Console.WriteLine("Car is running.");_rpm = 5000;}public override int Speed { get => _rpm / 100; set => _rpm = value * 100; }}class RaseCar : Car {public override void Run() {Console.WriteLine("Rase Car is running.");}}
}

总结

方法隐藏是指当子类声明了一个与父类签名相同的非虚方法时,会隐藏父类中的同名非虚方法。这种情况下,调用该方法时,会根据引用的静态类型来决定调用哪个类的方法,而不是根据对象的动态类型。

这与方法重写不同,方法重写是指当子类重写了父类的虚方法时,会覆盖父类中的同名虚方法。这种情况下,调用该方法时,会根据对象的动态类型来决定调用哪个类的方法,而不是根据引用的静态类型。

无论是重写基类的方法还是重写基类的属性,这两者都必须是 virtualabstractoverride

不能重写非虚的、静态的方法或属性

// 基类
public class Animal
{// 非虚方法public void Eat(){Console.WriteLine("Animal eats");}// 虚方法public virtual void Sleep(){Console.WriteLine("Animal sleeps");}
}// 子类
public class Dog : Animal
{// 隐藏父类的非虚方法public new void Eat(){Console.WriteLine("Dog eats");}// 重写父类的虚方法public override void Sleep(){Console.WriteLine("Dog sleeps");}
}// 测试
public class Test
{public static void Main(string[] args){// 父类引用指向子类对象Animal a = new Dog();// 调用非虚方法,根据引用的静态类型,调用父类的方法a.Eat(); // 输出 Animal eats// 调用虚方法,根据对象的动态类型,调用子类的方法a.Sleep(); // 输出 Dog sleeps}
}

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

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

相关文章

GEE 将裁剪后的Sentinel-2影像 在ArcGIS中去除空值

在ArcGIS中,可以使用栅格计算器(Raster Calculator)工具来设置NoData值为空。以下是在ArcGIS中将NoData值设置为空的步骤: 打开ArcGIS软件并加载下载的Sentinel-2影像数据。 影像Nodata空值以黑色背景呈现,影响矢量数据…

SpringCloud2023最新版本该如何进行组件选型?

前言 Developing distributed systems can be challenging. Complexity is moved from the application layer to the network layer and demands greater interaction between services. Making your code ‘cloud-native’ means dealing with 12-factor issues such as exte…

鸿蒙4.0-DevEco Studio界面工程

DevEco Studio界面工程 DevEco Studio 下载与第一个工程新建的第一个工程界面回到Project工程结构来看 DevEco Studio 下载与第一个工程 DevEco Studio 下载地址:点击跳转 https://developer.harmonyos.com/cn/develop/deveco-studio#download 学习课堂以及文档地址…

《金三银四求职攻略》:程序员面试季倒计时

程序员的金三银四求职宝典 大家好,我是小明,一位即将面临春季求职季的程序员。在这个黄金时段,如何在众多应聘者中脱颖而出,拿下理想的offer,成为了我思考的重点。今天,我将分享一些我个人的求职攻略&…

增强型YOLOv5应用于扩展PASCAL VOC数据集

目录 摘要关键词 1. 引言2. 性能研究2.1 YOLO2.2 PASCAL VOC 2007 数据集2.3 MS COCO 数据集 3 实验方法3.1 利用COCO数据集扩展PASCAL VOC 2007(以elephant大象为例)3.2 方法实现(以YOLOv5为例)3.2.1 YOLOv5介绍3.2.2 YOLOv5…

题目 1605: 蓝桥杯-阿尔法乘积

题目描述: 计算一个整数的阿尔法乘积。对于一个整数x来说,它的阿尔法乘积是这样来计算的:如果x是一个个位数,那么它的阿尔法乘积就是它本身;否则的话,x的阿 尔法乘积就等于它的各位非0的数字相乘所得到的那个整数的阿…

算法刷题day23:归并排序

目录 引言概念一、火柴排队二、归并排序三、逆序对的数量四、小朋友排队五、超级快速排序 引言 关于这个归并排序,考察的还是挺多的,在笔试面试中会问你,或者直接让你写一个归并排序,还有竞赛中有时也会考察,不过一般…

【详识C语言】动态内存管理

本章重点 为什么存在动态内存分配 动态内存函数的介绍 malloc free calloc realloc 常见的动态内存错误 几个经典的笔试题 柔性数组 为什么存在动态内存分配 我们已经掌握的内存开辟方式有: int val 20;//在栈空间上开辟四个字节char arr[10] {0};//在栈空间上开…

Three.js--》探寻Cannon.js构建震撼的3D物理交互体验(二)

我们用three.js可以绘制出各种酷炫的画面,但是当我们想要一个更加真实的物理效果的话,这个时候我们就需要一个物理的库,接下来我们就讲解一下今天要学习的canon,它可以给我们提供一个更加真实的物理效果,像物体的张力、…

程序员超强大脑——提高命名的质量(二)

哪些类型的标识符更容易理解 使用完整的单词似乎是一个合理的选择。虽然单词构成的标识符便于理解,但较长的标识符也可能产生负面影响。设计高质量的标识符是需要仔细衡量单词的清晰性与缩写的简洁性:注意单词的清晰性便于代码阅读者理解程序和排查错误…

粒子群算法优化ELMAN神经网络的分类预测,pso-elman

目录 背影 ELMAN神经网络的原理 ELMAN神经网络的定义 受限玻尔兹曼机(RBM) 灰狼算法原理 灰狼算法优化elman神经网络回归分析 基本结构 主要参数 数据 MATALB代码 结果图 展望 完整代码下载:粒子群算法优化ELMAN神经网络的分类预测资源-CSDN文库 https://download.csdn.net…

2024护网面试题精选(一)

0x00.基础漏洞篇 00-TOP10漏洞 1.SQL注入 2.失效的身份认证和会话管理 3.跨站脚本攻击XSS 4.直接引用不安全的对象 5.安全配置错误 6.敏感信息泄露 7.缺少功能级的访问控制 8.跨站请求伪造CSRF 9.实验含有已知漏洞的组件 10.未验证的重定向和转发 01-SQL注入漏洞 …

java多线程 简简单单的学它

java多线程 什么是线程 通俗的讲,就是一个软件里相互独立,同时运行的功能。比如我们打开B站,看视频时,我们会看到画面,听到声音,闪过的弹幕,这些都可以看作是一个线程。 在理解线程前&#x…

【Java_JSON】如何从JSON数据中提取value值

如何从JSON数据中提取value值? 首先将JSON数据转成字符串 创建JSONObject 对象 通过kv键值对的特性 使用key值来获取value 值 并输出 结果:

c++中锁定数据出现读写错误的例子

2010-4 程序出现了问题,引出了数据逻辑问题。 下面两个函数GetNextDataIndexW();和SetNextDataIndexW分别锁定, 导致了可能两个线程通过了GetNextDataIndexW();但是在SetNextDataIndexW出现数据写入错误。 这是一个典型的数据同步错误,也是…

mysql 中的一些重要函数

show create table user_profile 查看表结构 1.datediff(end_date,start_date)函数,now(), curdate() curtime() date_add(日期,interval num 时间) date_format(日期,格式) 4.select IFNULL(null,0); oracle 中nvl 函数 5.select IF(2 > 1, 2,0)&#xff…

c++: 缺省参数/默认参数的详解及其应用

c缺省参数/默认参数的详解及其应用 缺省参数是什么 #include<iostream> using namespace std;void func(int a 666) {cout << "a " << a << endl; } int main() {func(); //没有传参func(10); //传参return 0; }缺省参数就是在我们不进…

计算机体系结构:VLIW

原文来自知乎 计算机体系结构&#xff1a;VLIW 本文主要介绍计算机体系结构中的VLIW&#xff0c;以供读者能够理解该技术的定义、原理、应用。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;计算机杂记 &#x1f…

腾讯云服务器99元一年购买入口链接

腾讯云服务器99元一年购买入口链接如下&#xff0c;现在已经降价到61元一年&#xff0c;官方活动链接如下&#xff1a; 腾讯云99元服务器一年购买页面腾讯云活动汇聚了腾讯云最新的促销打折、优惠折扣等信息&#xff0c;你在这里可以找到云服务器、域名、数据库、小程序等等多种…

【uniapp】小程序自定义一个通用的返回按钮组件

左边箭头&#xff0c;右边文字可以自定义&#xff0c;但是不要太长&#xff0c;太长可以自己改 .back的width值&#xff0c;改宽一点。 用这个组件的时候首先要在pages.json里把导航栏变成自定义的&#xff1a; ,{"path" : "pages/test/test","style&…