学习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.rhkb.cn/news/192877.html

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

相关文章

美国站群服务器IP如何设置分配?

​  在配置美国站群服务器时&#xff0c;IP的分配是一个重要的步骤。下面将介绍一些关于美国站群服务器IP分配的相关知识。 独享IP和虚拟IP 在租用美国站群服务器之前&#xff0c;我们需要了解提供的IP是独享的还是虚拟的。独享IP指每个网站都有独立的IP地址&#xff0c;而虚…

nginx-静态资源实践(压缩配置,常见静态资源配置)

Nginx 实战搭建一个静态资源web服务器 第一个阶段访问单个文件 listen 80; #监听的端口 server_name localhost; #服务名称 #配置路径映射 location /geotools/ {alias geotools/; #将geotools/ 和/geotools/路径一一对应起来 }目录关系如下&#xff1a; nginx下面建一个g…

day59【单调栈】503.下一个更大元素Ⅱ 42.接雨水 84.柱状图中最大的矩形

文章目录 503.下一个更大元素Ⅱ42.接雨水 503.下一个更大元素Ⅱ 力扣题目链接 代码随想录讲解链接 题意&#xff1a;给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。 数…

并发编程之生产者消费者模型

什么是生产者消费者模型 生产者消费者模型是多线程中一个比较典型的模型。 打个比方&#xff1a;你是一个客户&#xff0c;你去超市里买火腿肠。 这段话中的 "你"就是消费者&#xff0c; 那么给超市提供火腿肠的供货商就是生产者。超市呢&#xff1f;超市是不是被…

【学习笔记】 - GIT的基本操作,IDEA接入GIT以及上传hub

用github蛮多&#xff0c;但git没怎么用&#xff0c;看着视频对着写点笔记以及操作 一、GIT文件的三种状态和模式 已提交(committed) 已提交表示数据已经安全的保存在本地数据库中。 已修改(modified) 已修改表示修改了文件&#xff0c;但还没保存到数据库中。…

教你轻轻松松写出10万+的微头条爆文,赶紧收藏!

微头条是投放在今日头条上的稿件&#xff0c;重点在于微字&#xff0c;一般在300-500字之间&#xff0c;讲究的是原创干货&#xff0c;有独到见解。 企业和品牌撰写微头条来给自己带来更多曝光和展现。想要让你的微头条写出爆款内容&#xff0c;这是需要讲究技巧的&#xff0c…

Java实现DXF文件转换成PDF

代码实现 public static void dxfToPdf(){// 加载DXF文件String inputFile "input.dxf";CadImage cadImage (CadImage) Image.load(inputFile);// 设置PDF输出选项PdfOptions pdfOptions new PdfOptions();pdfOptions.setPageWidth(200);pdfOptions.setPageHeigh…

同为科技(TOWE)主副控智能自动断电桌面PDU插排

在这个快节奏的现代社会&#xff0c;我们越来越需要智能化的产品来帮助我们提高生活质量和工作效率&#xff0c;同时&#xff0c;为各种家用电器及电子设备充电成为不少消费者新的痛点。桌面插排如何高效、安全地管理这些设备&#xff0c;成为了一个亟待解决的问题。同为科技&a…

网络基础(一)

文章目录&#xff1a; 计算机网络认识计算机网络背景网络发展认识 “协议” 网络协议初识协议分层OSI七层模型TC/IP 五层&#xff08;或四层&#xff09;模型 网络传输基本流程网络传输流程图同局域网的两台主机进行通信跨网络的两台主机进行通信数据包的封装和分用 网络中的地…

【深度学习】SimSwap: An Efficient Framework For High Fidelity Face Swapping 换脸,实战

代码&#xff1a;https://github.com/neuralchen/SimSwap 文章目录 摘要介绍RELATED WORK实验结论代码实操 SimSwap是一个高保真度人脸交换的高效框架。它将源脸的身份转移到目标脸上&#xff0c;同时保留目标脸的属性。该框架包括ID注入模块&#xff08;IIM&#xff09;&#…

Umeyama 算法之源码阅读与测试

Title: Umeyama 算法之源码阅读与测试 文章目录 前言I. Eigen 中 Umeyama 算法源码1. Eigen/src/Geometry/Umeyama.h 源码2. 代码测试 II. PCL 中 Umeyama 算法源码III. evo 中 Umeyama 算法源码1. evo/core/geometry.py 源码2. 代码测试 总结参考文献 [相关博文介绍] - 矩阵乘…

Avatar虚拟形象解决方案,趣味化的视频拍摄与直播新体验

企业们正在寻找新的方式来吸引和保持观众的注意力,一种新兴的解决方案就是使用Avatar虚拟形象技术&#xff0c;这种技术可以让用户在视频拍摄或直播场景中&#xff0c;以自定义的数字人形象出现&#xff0c;同时保持所有的表情和脸部驱动。美摄科技正是这个领域的领军者&#x…

Elastic stack8.10.4搭建、启用安全认证,启用https,TLS,SSL 安全配置详解

ELK大家应该很了解了&#xff0c;废话不多说开始部署 kafka在其中作为消息队列解耦和让logstash高可用 kafka和zk 的安装可以参考这篇文章 深入理解Kafka3.6.0的核心概念&#xff0c;搭建与使用-CSDN博客 第一步、官网下载安装包 需要 elasticsearch-8.10.4 logstash-8.…

【Pytorch和深度学习】栏目导读

一、栏目说明 本栏目《pytorch实践》是为初学者入门深度学习准备的。本文是该栏目的导读部分&#xff0c;因为计划本栏目在明年完成&#xff0c;因此&#xff0c;导读部分&#xff0c;即本文也在持续更新中。 本栏目设计目标是将深度学习全面用pytorch实践一遍&#xff0c;由浅…

【考研数据结构代码题6】构建二叉树及四大遍历(先中后层)

题目&#xff1a;请你编写完整的程序构建一棵二叉树并对其进行先序遍历、中序遍历、后序遍历与层次遍历&#xff0c;分别打印并输出遍历结果 难度&#xff1a;★★★ 二叉树的存储结构 typedef struct Node{char data;//数据域struct Node* left;//左子树struct Node* right;//…

物联网AI MicroPython学习之语法 GPIO输入输出模块

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; GPIO 介绍 模块功能: GPIO通用输入输出。 接口说明 GPIO - 构建GPIO对象 函数原型&#xff1a;Pin(port, dir , pull)参数说明&#xff1a; 参数类型必选参数&#xff1f;说明portintY对应开发板的引脚号…

[LeetCode周赛复盘] 第 371 场周赛20231112

[LeetCode周赛复盘] 第 371 场周赛20231112 一、本周周赛总结100120. 找出强数对的最大异或值 I1. 题目描述2. 思路分析3. 代码实现 100128. 高访问员工1. 题目描述2. 思路分析3. 代码实现 100117. 最大化数组末位元素的最少操作次数1. 题目描述2. 思路分析3. 代码实现 100124…

初始MySQL(四)(查询加强练习,多表查询)

目录 查询加强 where加强 order by加强 group by 分页查询 总结 多表查询(重点) 笛卡尔集及其过滤 自连接 子查询 子查询当作临时表 all/any 多列子查询 #先创建三张表 #第一张表 CREATE TABLE dept(deptno MEDIUMINT NOT NULL DEFAULT 0,dname VARCHAR(20) NOT …

Python数据结构:字典(dict)详解

1.字典概念 字典在其他语言中可能会被称为“关联存储”或“关联数组”。   在Python中&#xff0c;字典&#xff08;Dictionary&#xff09;是一种可变、无序且键值对&#xff08;key-value pairs&#xff09;唯一的数据结构。   字典也是一种标准映射类型&#xff0c;mapp…

【嵌入式设计】Main Memory:SPM 便签存储器 | 缓存锁定 | 读取 DRAM 内存 | DREM 猝发(Brust)

目录 0x00 便签存储器&#xff08;Scratchpad memory&#xff09; 0x01 缓存锁定&#xff08;Cache lockdown&#xff09; 0x02 读取 DRAM 内存 0x03 DREM Banking 0x04 DRAM 猝发&#xff08;DRAM Burst&#xff09; 0x00 便签存储器&#xff08;Scratchpad memory&#…