C#多态性学习,虚方法、抽象方法、接口等用法举例
1. 多态性定义
C#中的多态性是OOP(面向对象编程)的一个基本概念,它允许一个对象在不同情况下表现出不同的行为,以增强代码的可重用性和灵活性。
根据网上的教程,我们得知C#多态性分为两类,静态和动态。但实际上,C#没有严格的静态和动态多态性的分法。之所以这么分,还是为了我们便于理解,我们沿用这个思维来大概分类:
采用函数重载或运算符重载方法的,属于静态多态性;
采用虚方法、抽象方法、接口等方式,属于动态多态性。
拓展:在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。什么意思呢?
在静态语言中,许多多态性的特性可以在编译时确定,编译器可以根据数据类型的信息来确定方法的调用方式。
而在动态语言中,数据类型的确定通常是在运行时进行的,这种行为被称为动态多态性。
2. 函数重载示例
函数重载是指在同一个类中,定义多个方法,它们的方法名相同,但是参数类型、参数数量、参数顺序不同。以下是一个函数重载的例子:
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
public float Add(float a, float b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
}
在这个例子中,Calculator类定义了4个Add()方法,它们的方法名相同但是参数列表不同(分别有2个整型参数、3个整型参数、2个浮点型参数、2个双精度浮点型参数)。这些方法根据声明的参数类型和数量而得到不同的签名,因此构成函数重载,当调用Add()方法时,编译器会根据参数的类型和数量来选择正确的方法重载进行调用。
3. 虚方法示例
虚方法和重写(override)方法:在父类中声明一个虚方法,子类可以重写该方法并实现自己的行为。在运行时,程序根据对象的实际类型调用相应的方法。这种方式也称为“消除静态绑定”。
注意事项:虚方法是使用关键字 virtual 声明的。同时继承类中的重写虚函数需要声明关键字 override 。下面是一个示例:
// 定义一个Animal类和其子类
class Animal
{
public virtual void Speak()
{
Console.WriteLine("I am an animal.");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Meow!");
}
}
// 示例程序
class Program
{
static void Main(string[] args)
{
Animal[] animals = new Animal[2];
animals[0] = new Dog();
animals[1] = new Cat();
foreach (Animal animal in animals)
{
animal.Speak();
}
Console.ReadKey();
}
}
在这个例子中,Animal类中声明了一个虚方法Speak(),它的子类 Dog 和 Cat 分别对该方法进行了重写。在Main 方法中,创建了一个 Animal 数组,其中存放了 Dog 和 Cat 的实例。在foreach循环中,程序根据实际对象类型调用了不同的Speak()方法,实现了多态性的效果。
小拓展:关键字重写 override 与覆盖 new 较为容易搞混,有关两者区别可移步:C#中重写(override)及覆盖(new)的区别详解。
4. 抽象方法示例
抽象方法是在抽象类中定义的,它没有具体实现的代码,而只是定义了方法的名称、参数和返回值类型等信息。抽象方法必须在子类中进行完整的实现,否则子类本身也必须定义为抽象类。使用abstract关键字来定义抽象类和抽象方法。
下面的示例演示了如何定义并使用抽象方法:
abstract class Shape
{
public abstract double Area(); // 定义抽象方法Area()
}
// 派生类Rectangle继承抽象类Shape
class Rectangle : Shape
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
}
public override double Area() // 实现抽象方法Area()
{
return width * height;
}
}
class Triangle : Shape
{
double baseValue, height;
public Triangle(double bv, double h)
{
baseValue = bv;
height = h;
}
public override double Area() // 实现抽象方法Area()
{
return baseValue * height * 0.5;
}
}
class Program
{
static void Main(string[] args)
{
Rectangle r = new Rectangle(5, 8);
Console.WriteLine("矩形的面积 = {0}", r.Area());
Triangle t = new Triangle(5, 8);
Console.WriteLine("三角形的面积 = {0}", t.Area());
}
}
上面的代码定义了Shape类和两个派生类Rectangle和Triangle。Shape类中定义了一个抽象方法Area(),并在Rectangle和Triangle中实现了这个抽象方法。在Main方法中,创建了一个Rectangle对象 r 和一个Triangle对象 t,并分别调用它们的Area()方法计算出它们的面积。
5. 接口示例
C#接口是一种约定,是一个抽象的类型,它定义了一组公共的方法、属性、索引器和事件,这些成员没有实现细节和实现代码,只定义了接口的行为。
5.1 接口语法
定义接口的语法如下:
interface 接口名称
{
方法1
方法2
属性1
索引器1
事件1
}
其中,方法、属性、索引器和事件都是接口的成员,它们都没有实现,只是定义了行为的名称和参数。
方法定义的语法如下:
返回类型 方法名称(参数列表);
属性定义的语法如下:
属性类型 属性名称 { get; set; }
索引器定义的语法如下:
索引器类型 this[索引器参数] { get; set; }
事件定义的语法如下:
event 事件委托类型 事件名称;
其中,事件委托类型是一个Delegate类型。
5.2 接口使用示例
定义一个接口之后,可以通过继承或实现来使用接口。接口的继承使用“:”符号,需要注意的是,如果一个类实现了一个接口,那么它必须实现接口中所有的方法和属性。下面是接口示例:
interface IShape
{
double Perimeter();
double Area();
}
interface ICircle : IShape
{
double Radius { get; set; }
}
interface IRectangle : IShape
{
double Width { get; set; }
double Height { get; set; }
}
class Circle : ICircle
{
public double Radius { get; set; }
public double Perimeter()
{
return 2 * Math.PI * Radius;
}
public double Area()
{
return Math.PI * Radius * Radius;
}
}
class Rectangle : IRectangle
{
public double Width { get; set; }
public double Height { get; set; }
public double Perimeter()
{
return 2 * (Width + Height);
}
public double Area()
{
return Width * Height;
}
}
class Program
{
static void Main(string[] args)
{
Circle c = new Circle();
c.Radius = 1;
Console.WriteLine("Circle: Perimeter = {0}, Area = {1}", c.Perimeter(), c.Area());
Rectangle r = new Rectangle();
r.Width = 2;
r.Height = 3;
Console.WriteLine("Rectangle: Perimeter = {0}, Area = {1}", r.Perimeter(), r.Area());
}
}
上面的代码定义了IShape、ICircle和IRectangle三个接口、以及Circle和Rectangle两个类。其中,Circle类实现了ICircle接口,Rectangle类实现了IRectangle接口。在Main方法中,创建了一个Circle对象和一个Rectangle对象,并给它们的属性赋值。然后分别调用了Circle和Rectangle对象的Perimeter()和Area()方法输出结果。
运行程序,输出以下结果:
Circle: Perimeter = 6.28318530717959, Area = 3.14159265358979
Rectangle: Perimeter = 10, Area = 6
可以看到,通过使用接口,我们可以很方便地定义出不同的形状,然后计算出它们的周长和面积。这就展示了接口在C#编程中的重要地位。
想必大家看到这里对于多态性的实现方式已经有了一定的了解,不知大家是否发现,这些方式中,抽象方法和虚方法是如此的相似,那么我们就有了新的疑问:两者有何区别?使用场景是否有所不同?
在下面这篇随笔中,我针对这个问题进行了深入学习:C#中抽象方法与虚方法的区别。
C#多态性学习,虚方法、抽象方法、接口等用法举例的更多相关文章
- C# 虚方法 抽象方法 接口
虚方法:virtu 注意的几点: 1,父类中如果有方法让子类重写,则可以将该方法标记为virtual 2.虚方法在父类中必须有实现,哪怕是空实现 3虚方法子类可以重写,也可以不重写 4.如果类是抽象类 ...
- c#基础精华01(强调代码规范,虚方法,抽象方法,接口)
强调代码规范 规则(法律,必须遵守否则报错) 语法 规范(道德,大家都喜欢有道德的人.) 注释//,/**/,/// 骆驼命名 :第一个单词首字母小写,之后的单词首字母大写 userName.user ...
- C#通过完整的例子,Get常用的2个套路,理解抽象方法,虚方法,接口,事件
一.理解:抽象方法,虚方法,接口,事件 描述: 1.定义一个抽象父类"People": 要求: 1>3个属性:名字,性别,年龄: 2>一个普通方法"说话&qu ...
- 浅谈C#抽象方法、虚方法、接口
每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不 ...
- C#中的抽象方法,虚方法,接口之间的对比
1.首先来看一看抽象类 抽象类是特殊的类,不能够被实例化:具有类的其他特性:抽象方法只能声明于抽象类中,且不包含任何实现 (就是不能有方法体),派生类也就是子类必须对其进行重写.另外,抽象类可以派生自 ...
- C#属性-索引器-里氏替换-多态-虚方法-抽象-接口-泛型-
1.属性 //属性的2种写法 public class person { private string _name; public string Name { get { return _name; ...
- C#之类的继承、抽象类和虚方法
代码下载地址 类的继承: 写电池的基类:包含条码和箱体码两个字段,含有两个参数的构造函数 class Battery { public string _barCode; public string _ ...
- c# 多态实现_虚方法
实现方法: 虚方法, 抽象类, 接口 1.虚方法 将父类的方法标记为虚方法,使用关键字virtual,这个方法可以被子类重新写一遍. 在父类的方法前面加上一个virtual,在子类的方法前面加上一个o ...
- C#类和接口、虚方法和抽象方法及值类型和引用类型的区别
1.C#类和接口的区别接口是负责功能的定义,项目中通过接口来规范类,操作类以及抽象类的概念!而类是负责功能的具体实现!在类中也有抽象类的定义,抽象类与接口的区别在于:抽象类是一个不完全的类,类里面有抽 ...
- C#类、接口、虚方法和抽象方法0322
虚拟方法和抽象方法有什么区别与联系: 1.抽象方法只有声明没有实现代码,需要在子类中实现:虚拟方法有声明和实现代码,并且可以在子类中重写,也可以不重写使用父类的默认实现. 2.抽象类不能被实例化(不可 ...
随机推荐
- ETSI GS MEC 014,UE 标识 API
目录 文章目录 目录 版本 功能理解 UML UE Identity tag registration UE Identity tag de-registration API Definition U ...
- Vue3:Cannot read properties of null (reading 'isCE')
Cannot read properties of null (reading 'isCE') 这个问题是在vue3中引入elementui的列表框时出现的.经过网上查询,有说是装了两个vue版本 ...
- Linux:查看磁盘配额报告数据
近期在超算云上跑代码遇到了个乌龙,就是作业一提交到集群上去先是PD状态,然后马上就终止调了.后来知道是我用户目录下文件数量太多,导致已经超过管理员给我分配的磁盘配额了(众所周知机器学习相关项目的数据集 ...
- Vue——基本使用
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统.我们不再和 HTML 直接交互了.一个 Vue 应用会将其挂载到一个 DOM 元素上 (对于这个例子是 #app ...
- Go 指针逃逸分析
引用 https://my.oschina.net/renhc/blog/2222104
- Java自增
Java自增 本文分为以下部分: 栗子 栗子解释 来点复杂的 字节码解读 总结 栗子 java存在一种神奇的操作符,++,自增1,但是经常分不清楚 i++ 和++i 两者的区别,虽然最后结果可能都是 ...
- 文字转语音 - 搭建微软tts整合web服务提供api接口(免费)
微软tts是业界公认文字转语音效果最佳本文使用docker搭建微软tts服务并提供api接口对外提供服务对接官方免费在线体验接口,搭建后可免费进行调用使用,不保证永久稳定可用 调用方式url:http ...
- 【昇腾开发全流程】AscendCL开发板模型推理
前言 学会如何安装配置华为云ModelArts.开发板Atlas 200I DK A2. 并打通一个Ascend910训练到Ascend310推理的全流程思路. 在本篇章,我们继续进入推理阶段! 推理 ...
- 薄书的pytorch项目实战lesson49-情感分类+蹭免费GPU
项目来源 B站视频pytorch项目实战-情感分类问题 github lesson49-情感分类实战 1 实验环境 在这里和大家推荐一个学习ML和DL的一个实验运行平台,就是google的Colabo ...
- 7.21考试总结(NOIP模拟22)[d·e·f]
你驻足于春色中,于那独一无二的春色之中. 前言 首先,这套题的暴力分数十分丰厚,大概是 81+89+30=200 . T1 的特殊性质比较多,也都很好想,于是考场 81pts 是没有问题的. T2 暴 ...