学习c#的第十三天

目录

C# 多态性

静态多态性

函数重载

运算符重载

动态多态性

virtual 和 abstract

抽象方法和虚方法的区别

重载(overload)和重写(override)

隐藏方法


C# 多态性

多态是同一个行为具有多个不同表现形式或形态的能力。

多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。

多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。

在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 Object。

多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:

现实中,比如我们按下 F1 键这个动作:

  • 如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
  • 如果当前在 Word 下弹出的就是 Word 帮助;
  • 在 Windows 下弹出的就是 Windows 帮助和支持。

同一个事件发生在不同的对象上会产生不同的结果。

静态多态性

在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C# 提供了两种技术来实现静态多态性。分别为:

函数重载

在C#中,函数重载指的是在同一个类中可以定义多个具有相同名称但参数列表不同的函数。通过函数重载,可以根据不同的参数列表来调用合适的函数。

下面是一个简单的示例,演示了如何在C#中使用函数重载:

using System;public class DateUtils
{// 函数重载public string FormatDate(DateTime dt){return dt.ToString("yyyy/MM/dd HH:mm:ss.fff");}public string FormatDate(DateTime dt, string format){return dt.ToString(format);}
}public class Program
{public static void Main(){DateUtils utils = new DateUtils();DateTime now = DateTime.Now;// 使用不同的重载函数进行日期格式化string formattedDate1 = utils.FormatDate(now); // 使用第一个重载函数string formattedDate2 = utils.FormatDate(now, "yyyy-MM-dd HH:mm:ss"); // 使用第二个重载函数// 输出格式化后的日期Console.WriteLine("格式化日期1: " + formattedDate1);Console.WriteLine("格式化日期2: " + formattedDate2);}
}

在上面的示例中,DateUtils 类中定义了两个名为 FormatDate 的函数。第一个函数接受一个 DateTime 类型的参数,并将日期格式化为特定的格式。第二个函数接受一个 DateTime 类型的参数和一个表示日期格式的字符串参数,并根据传入的格式对日期进行格式化。这两个函数拥有相同的名称但参数列表不同。

运算符重载

C#允许我们重定义或重载内置运算符,以便在用户自定义类型上使用它们。运算符重载通过关键字operator后面跟着运算符的符号来定义。

下面是一些常见的运算符重载的示例:

1、算术运算符:

  • +:重载为public static T operator +(T operand1, T operand2)
  • -:重载为public static T operator -(T operand1, T operand2)
  • *:重载为public static T operator *(T operand1, T operand2)
  • /:重载为public static T operator /(T operand1, T operand2)
  • %:重载为public static T operator %(T operand1, T operand2)

2、比较运算符:

  • ==:重载为public static bool operator ==(T operand1, T operand2)
  • !=:重载为public static bool operator !=(T operand1, T operand2)
  • <:重载为public static bool operator <(T operand1, T operand2)
  • >:重载为public static bool operator >(T operand1, T operand2)
  • <=:重载为public static bool operator <=(T operand1, T operand2)
  • >=:重载为public static bool operator >=(T operand1, T operand2)

3、逻辑运算符:

  • !:重载为public static bool operator !(T operand)
  • &:重载为public static bool operator &(T operand1, T operand2)
  • |:重载为public static bool operator |(T operand1, T operand2)

4、位运算符:

  • ~:重载为public static T operator ~(T operand)
  • &:重载为public static T operator &(T operand1, T operand2)
  • |:重载为public static T operator |(T operand1, T operand2)
  • ^:重载为public static T operator ^(T operand1, T operand2)

5、赋值运算符:

  • =:重载为public static void operator =(T destination, T source)
  • +=:重载为public static T operator +=(T operand1, T operand2)
  • -=:重载为public static T operator -=(T operand1, T operand2)
  • *=:重载为public static T operator *=(T operand1, T operand2)
  • /=:重载为public static T operator /=(T operand1, T operand2)
  • %=:重载为public static T operator %=(T operand1, T operand2)

这些示例只是一些常见的运算符重载示例,你可以根据自己的需求重载其他运算符。需要注意的是,并非所有运算符都可以被重载,具体可重载的运算符列表请参考C#官方文档。

代码示例

using System;public class Complex
{public double Real { get; set; }public double Imaginary { get; set; }public Complex(double real, double imaginary){Real = real;Imaginary = imaginary;}// 重载加法运算符 "+"public static Complex operator +(Complex c1, Complex c2){return new Complex(c1.Real + c2.Real, c1.Imaginary + c2.Imaginary);}// 重载减法运算符 "-"public static Complex operator -(Complex c1, Complex c2){return new Complex(c1.Real - c2.Real, c1.Imaginary - c2.Imaginary);}// 重载乘法运算符 "*"public static Complex operator *(Complex c1, Complex c2){double real = c1.Real * c2.Real - c1.Imaginary * c2.Imaginary;double imaginary = c1.Real * c2.Imaginary + c1.Imaginary * c2.Real;return new Complex(real, imaginary);}// 重载复数的输出格式public override string ToString(){return $"{Real} + {Imaginary}i";}
}class Program
{static void Main(){Complex c1 = new Complex(1, 2);Complex c2 = new Complex(3, 4);Complex sum = c1 + c2; // 使用重载的加法运算符Console.WriteLine($"总和: {sum}");Complex difference = c1 - c2; // 使用重载的减法运算符Console.WriteLine($"差别: {difference}");Complex product = c1 * c2; // 使用重载的乘法运算符Console.WriteLine($"相乘: {product}");}
}

当上面的代码被编译和执行时,它会产生下列结果:

总和: 4 + 6i
差别: -2 + -2i
相乘: -5 + 10i

动态多态性

在C#中,使用抽象类和虚方法可以实现动态多态性。

C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。派生类具有更专业的功能。

请注意,下面是有关抽象类的一些规则:

  • 抽象类不能被实例化,只能被用作其他类的基类。
  • 抽象类中可以包含抽象方法,这些方法没有具体的实现,而是由派生类来实现。
  • 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。

下面的程序演示了一个抽象类:

using System;// 定义抽象类
public abstract class Shape
{public string Name { get; set; }// 定义抽象方法public abstract double CalculateArea();
}// 继承自抽象类的具体类:圆形
public class Circle : Shape
{public double Radius { get; set; }// 实现抽象方法public override double CalculateArea(){return Math.PI * Radius * Radius;}
}// 继承自抽象类的具体类:正方形
public class Square : Shape
{public double SideLength { get; set; }// 实现抽象方法public override double CalculateArea(){return SideLength * SideLength;}
}class Program
{static void Main(){Circle circle = new Circle { Name = "圆形", Radius = 3 };Console.WriteLine("圆形的面积: " + circle.CalculateArea());  // 输出结果:"圆形的面积: 28.274333882308138"Square square = new Square { Name = "正方形", SideLength = 4 };Console.WriteLine("正方形的面积: " + square.CalculateArea());  // 输出结果:"正方形的面积: 16"}
}

 另外,虚方法是在基类中,可以使用关键字virtual来声明一个方法为虚方法,表示它可以被派生类重写。而在派生类中,可以使用关键字override来重写基类中的虚方法。

通过使用抽象类和虚方法,可以实现代码的可扩展性和灵活性。抽象类提供了一种统一的接口,定义了需要实现的方法,而具体的实现则由派生类来完成。虚方法允许在派生类中重写方法,实现不同的行为。

以下是一个简单的代码示例,演示了如何使用抽象类和虚方法来实现动态多态性。

using System;// 定义一个抽象类
public abstract class Shape
{// 定义一个虚方法public virtual double CalculateArea(){return 0;}
}// 派生类1:矩形
public class Rectangle : Shape
{public double Width { get; set; }public double Height { get; set; }// 实现抽象方法public override double CalculateArea(){return Width * Height;}
}// 派生类2:圆形
public class Circle : Shape
{public double Radius { get; set; }// 实现抽象方法public override double CalculateArea(){return Math.PI * Radius * Radius;}
}class Program
{static void Main(){// 创建一个矩形对象Shape rect = new Rectangle { Width = 5, Height = 10 };Console.WriteLine("矩形的面积:" + rect.CalculateArea());// 创建一个圆形对象Shape circle = new Circle { Radius = 3 };Console.WriteLine("圆形的面积:" + circle.CalculateArea());}
}

当上面的代码被编译和执行时,它会产生下列结果:

矩形的面积:50
圆形的面积:28.274333882308138

virtual 和 abstract

  • virtual关键字用于修饰方法,表示该方法在基类中有一个默认实现,但派生类可以选择性地重写它。通常情况下,virtual方法会提供一个默认实现,但可以在派生类中进行修改或扩展。即使派生类没有重写virtual方法,它仍然会使用基类中的默认实现。
  • abstract关键字用于修饰方法或类,表示它们没有具体的实现。抽象方法只有定义,没有实现,派生类必须重写它们,并且提供具体的实现。抽象类本身也不能被实例化,只能作为基类被其他类继承。

需要注意的是,抽象方法只能存在于抽象类中,而不是普通的类。如果一个类包含抽象方法,则该类本身必须被声明为抽象类。而虚方法可以存在于任何类中,无论是抽象类还是普通类。

总之,virtual和abstract关键字都用于实现多态性,在派生类中重新定义父类的方法。virtual方法允许派生类选择重写并提供新的实现,而abstract方法则要求派生类必须提供具体的实现。另外,抽象类本身不能被实例化,只能作为其他类的基类使用。

抽象方法和虚方法的区别

  1. 抽象方法(abstract method)必须在抽象类中声明,并且没有具体的实现部分。派生类必须重写并提供具体的实现,否则派生类将无法被实例化。
  2. 虚方法(virtual method)在基类中有一个默认的实现,但也可以在派生类中进行重写。派生类可以选择性地覆盖虚方法,或者直接使用基类的默认实现。
  3. 抽象方法和虚方法都可以供派生类重写以定制化行为,但抽象方法必须在派生类中进行重写,而虚方法可以选择性地进行重写。

重载(overload)和重写(override)

1、重载(overload): 在同一个作用域(一般指一个类)的两个或多个方法函数名相同,参数列表不同的方法叫做重载,它们有三个特点(俗称两必须一可以):

  •  方法名必须相同
  •  参数列表必须不相同
  •  返回值类型可以不相同

2、重写(override):子类中为满足自己的需要来重复定义某个方法的不同实现,需要用 override 关键字,被重写的方法必须是虚方法,用的是 virtual 关键字。它的特点是(三个相同):

  • 相同的方法名
  • 相同的参数列表
  • 相同的返回值

代码示例

using System;public class Calculator
{// 方法重载,参数列表不同public int Add(int num1, int num2){return num1 + num2;}public double Add(double num1, double num2){return num1 + num2;}
}public class Shape
{// 虚方法public virtual void Draw(){Console.WriteLine("正在绘制形状。。。");}
}public class Circle : Shape
{// 重写基类的虚方法public override void Draw(){Console.WriteLine("正在绘制圆。。。");}
}class Program
{static void Main(){Calculator calculator = new Calculator();int result1 = calculator.Add(3, 4);              // 调用第一个Add方法double result2 = calculator.Add(2.5, 3.7);       // 调用第二个Add方法Console.WriteLine("int加法的结果: " + result1); // 输出结果:"int加法的结果: 7"Console.WriteLine("double加法的结果: " + result2); // 输出结果:"double加法的结果: 6.2"Shape shape1 = new Shape();shape1.Draw();         // 输出结果:"正在绘制形状。。。"Shape shape2 = new Circle();shape2.Draw();         // 输出结果:"正在绘制圆。。。"}
}

隐藏方法

当在派生类中定义一个与基类中同名的方法时,会发生方法隐藏。使用关键字 new 可以实现方法隐藏。

using System;public class Shape
{public void Draw(){Console.WriteLine("正在绘制形状。。。");}
}public class Circle : Shape
{public new void Draw(){Console.WriteLine("正在绘制圆。。。");}
}class Program
{static void Main(){Shape shape1 = new Shape();shape1.Draw();         // 输出结果:"正在绘制形状。。。"Shape shape2 = new Circle();shape2.Draw();         // 输出结果:"正在绘制形状。。。"(调用的是基类的方法)Circle circle = new Circle();circle.Draw();         // 输出结果:"正在绘制圆。。。"}
}

在上述代码中,Shape 类定义了一个 Draw() 方法,Circle 类继承自 Shape 并定义了一个同名的 Draw() 方法并使用 new 关键字进行方法隐藏。

在 Main() 方法中,首先创建了一个 Shape 对象 shape1,调用其 Draw() 方法,输出结果为 "正在绘制形状。。。"。接着创建了一个 Circle 对象 shape2,将其赋值给 Shape 类型的变量 shape2,再次调用 shape2.Draw() 方法,输出结果仍然为 ""正在绘制形状。。。"",这是因为变量的静态类型是 Shape,所以调用的是基类的方法。最后,创建了一个 Circle 对象 circle,直接调用其 Draw() 方法,输出结果为 "正在绘制圆。。。",这是因为直接通过派生类的实例调用方法时,会调用派生类中隐藏的方法。

需要注意的是,方法隐藏并不是方法重写。如果想要实现方法的多态性,应该使用方法重写(override)而不是隐藏(new)。

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

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

相关文章

Postman的Cookie鉴权

近期在复习Postman的基础知识&#xff0c;在小破站上跟着百里老师系统复习了一遍&#xff0c;也做了一些笔记&#xff0c;希望可以给大家一点点启发。 一&#xff09;什么是Cookie 定义&#xff1a;存储在客户端的一小段文本信息&#xff0c;格式为键值对的形式. 二&#xff09…

Leetcode刷题详解——岛屿数量

1. 题目链接&#xff1a;200. 岛屿数量 2. 题目描述&#xff1a; 给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上…

汽车OBD2蓝牙诊断仪解决方案程序开发

1、因TL718已经为你建立了物理层、数据链层和部分应用层的协议&#xff0c;所以只要OBD2标准应用层协议文本&#xff0c;ISO15031-5 或 SAE J1979&#xff08;这两个协议是相同的内容&#xff09;。 2、TL718诊断接口 1 套或用TL718芯片自建电路。3、家用PC机电脑一台。4、安…

计算机网络——物理层-编码与调制(数字基带信号、模拟基带信号、码元、常用编码、基本调制方法、混合调制)

目录 编码与调制 数字基带信号 模拟基带信号 码元 常用编码 不归零编码 归零编码 曼彻斯特编码 差分曼彻斯特编码 编码习题 基本调制方法 调幅 调频 调相 混合调制 QAM-16 编码与调制 在计算机网络中&#xff0c;计算机需要处理和传输用户的文字、图片、音频…

深度学习AI识别人脸年龄

以下链接来自 落痕的寒假 GitHub - luohenyueji/OpenCV-Practical-Exercise: OpenCV practical exercise https://download.csdn.net/download/luohenyj/10993309 import cv2 as cv import time import argparsedef getFaceBox(net, frame, conf_threshold0.7):frameOpencvDn…

结构工程师软件 Naviate Core MEP for Revit 3.4 Crk

Naviate Fabrication - 先进的建模和制造命令&#xff0c;可提高 VDC 设计师、细节设计师和承包商的生产力和收入。 Naviate MEP - 通过 MEP 工程师和设计师的建模和参数提高效率 导航架构 Naviate Architecture 完全集成到 Revit 平台中&#xff0c;增强了 BIM 提供的协作可能…

C++标准模板(STL)- 类型支持 (属性查询,获取类型的对齐要求)

类型特性 类型特性定义一个编译时基于模板的结构&#xff0c;以查询或修改类型的属性。 试图特化定义于 <type_traits> 头文件的模板导致未定义行为&#xff0c;除了 std::common_type 可依照其所描述特化。 定义于<type_traits>头文件的模板可以用不完整类型实例…

使用JAVA pdf转word

使用spire.pdf 非常简单。 查看 https://mvnrepository.com/artifact/e-iceblue/spire.pdf 注意&#xff0c;这个包在 e-iceblue 下。 下面开始撸代码 先来pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://mav…

大数据-之LibrA数据库系统告警处理(ALM-12047 网络读包错误率超过阈值)

告警解释 系统每30秒周期性检测网络读包错误率&#xff0c;并把实际错误率和阈值&#xff08;系统默认阈值0.5%&#xff09;进行比较&#xff0c;当检测到网络读包错误率连续多次&#xff08;默认值为5&#xff09;超过阈值时产生该告警。 用户可通过“系统设置 > 阈值配置…

微服务架构演进

系统架构演变 没有最好的架构&#xff0c;只有最合适的架构&#xff1b;架构发展过程&#xff1a;单体架构》垂直架构》SOA 面向服务架构》微服务架构&#xff1b;推荐看看《淘宝技术这十年》&#xff1b; 单体架构 互联网早期&#xff0c;一般的网站应用流量较小&#xff0…

keepalived安装配置(服务器主备、负载均衡)

系统拓扑 安装keepalived 主备服务器上都需要安装 在线安装 yum install -y keepalived 离线安装 # todo 服务器准备 虚拟机ip&#xff1a;192.168.11.56 主服务器&#xff1a;192.168.11.53 备服务器&#xff1a;192.168.11.54 配置文件修改 keepalived安装之后&…

接口

文章目录 概述语法使用特性接口的继承抽象类和接口的区别 概述 电脑的USB口上&#xff0c;可以插&#xff1a;U盘、鼠标、键盘…所有符合USB协议的设备 电源插座插孔上&#xff0c;可以插&#xff1a;电脑、电视机、电饭煲…所有符合规范的设备 通过上述例子可以看出&#xff…

【Rust】快速教程——从hola,mundo到所有权

前言 学习rust的前提如下&#xff1a; &#xff08;1&#xff09;先把Rust环境装好 &#xff08;2&#xff09;把VScode中关于Rust的插件装好 \;\\\;\\\; 目录 前言先写一个程序看看Rust的基础mut可变变量let重定义覆盖变量基本数据类型复合类型&#xff08;&#xff09;和 [ …

pg_bouncer在使用中的坑勿踩

目录 简介 环境信息 问题配置 问题配置 启动pgbouncer 链接逻辑图 测试存在问题 pgadmin4 Idea JAVA调用 ​编辑 dbeaver 建议&#xff1a; 简介 前面文章说过关于pg_bouncer的安装讲解&#xff0c;这里讲一下在使用中的坑&#xff0c;在进行配置的时候需要注意。 …

系列三、双亲委派机制

一、概述 当一个类收到了类加载的请求&#xff0c;它首先不会尝试自己去加载这个类&#xff0c;而是把这个请求委派给父类去完成&#xff0c;每一层的类加载器都是如此&#xff0c;因此所有的请求都应该传送到启动类加载器中&#xff0c;只有当父类加载器反馈自己无法完成这个…

PowerPoint技巧:如何将一张图片同时加到全部幻灯片里?

想把一张图片加到PPT每一张幻灯片的同一个位置&#xff0c;如果一张一张的添加就太耗时间了&#xff0c;一起来看看如何利用母版快速设置同时添加吧。 首先&#xff0c;打开需要编辑的PPT&#xff0c;在菜单栏依次点击【视图】→【幻灯片母版】&#xff1b; 打开母版后&#x…

vue3实现数据大屏内数据向上滚动,鼠标进入停止滚动 vue3+Vue3SeamlessScroll

1.效果图 2.npm下载依赖及main.js文件配置 npm install vue3-seamless-scroll --saveimport vue3SeamlessScroll from vue3-seamless-scroll;app.use(vue3SeamlessScroll) 3.html代码 <!-- scrollFlag为true时再渲染,vue3只要涉及到传值子页面需要加flag判断&#xff0c;否…

【BIM入门实战】Revit图元的选择方式,总有一款适合你

Revit图元的五种常见选择方式,总有一款适合你。 文章目录 一、直接单击二、加选和减选三、连续框选四、按类别选择五、全选过滤选择操作可以在三维视图、平面视图等多种视图中进行。 一、直接单击 直接单击,即可选中某一个图元,如选择一个扶手。 二、加选和减选 按住ctrl键…

vue-router配置

1、路由安装 npm install vue-router4 2、创建router目录 3、编辑文件且引入router包 4、main.js引入

Ubuntu18.04平台下Qt开发程序打包的一些问题总结

目录 前言 一、在Ubuntu18.04开发环境下打包有两种方式 1、利用linuxdeployqt软件进行打包 2、利用编写shell脚本的方式进行打包 二、详细介绍shell脚本打包的方式 1、新建一个空的文件夹 2、准备脚本copylib.sh 3、准备脚本xxxx.sh。 4、给上述两个脚本添加可执行权限…