下面内容转自:https://blog.csdn.net/czc1997/article/details/78785448
C#是一门OOP(面向对象程序设计)语言,面向对象的3大原则,封装、继承、多态
今天要讨论的虚方法、抽象方法、抽象类、接口所有的一切都是以多态作为基础的,所以让我们聚焦多态————
多态是什么?
多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。
用我自己的理解来说:多态就是在继承的前提下,不同对象调用相同方法却表现出不同的行为,此为多态。
关键性的一句话:多态性在C++中是通过虚函数实现的,这在C#中同样适用。但是在C#中有三种方法来体现:虚方法,抽象类,接口。
所谓的虚函数,也就是我们首先要讨论的虚方法。
I.虚方法 Virtual (在子类重写方法的时候可以使用base.方法名来实现父类原本的函数功能)
虚方法存在于相对于需要实现多态的子类的父类中,同时也是最基本的实现多态的方法。
具体的语法是在父类中用virtual修饰,然后在子类中使用override进行重写。以下是一个简单易懂的例子:猫和狗都是动物,它们都会叫,但是叫声是不一样的。
1.先定义父类,只定义一个叫做Dosth的方法,代表动物的嚎叫。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CsharpTest9_10
{
//父类
class Animal
{
public Animal()
{
}
public virtual void Dosth()
{
Console.WriteLine("动物的嚎叫");
}
}
}
2.定义猫类:override重写
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CsharpTest9_10
{
class cat:Animal
{
public override void Dosth()
{
base.Dosth();//这一句话执行父类方法,在子类重写方法的时候可以使用base.方法名来实现父类原本的函数功能
Console.WriteLine("喵");
}
}
}
3.定义狗类:override重写
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CsharpTest9_10
{
class dog:Animal
{
public override void Dosth()
{
//注释掉狗类只实现自己的方法不实现父类方法
// base.Dosth();
Console.WriteLine("汪");
}
}
}
在主程序中:
cat c1 = new cat();
dog d1 = new dog();
c1.Dosth();
d1.Dosth();
运行结果:
通过上面的这个例子,算是对多态已经有了最基本的了解。存在于父类中的虚方法Dosh()是有自己的方法体的(方法体中没有必要的代码),而且这些方法体是必要的,少了他们就无法完成逻辑,这种情况需要使用虚方法。
如果父类中的方法完全不知道去干什么(即方法体中没有必要的代码),必须要子类进行重写才有意义的话,这种情况就需要使用抽象方法。
接下来是由虚方法引出的抽象方法以及抽象类。
II.抽象方法以及抽象类 Abstract (父类 抽象方法里 没有具体的代码功能)
抽象方法必须存在于抽象类中,抽象类的具体语法是类名前加上abstract。抽象方法的语法实例如下:public abstract void FUN();
仍然使用上面的例子。父类(Animal)中的Dosth方法中,并没有必要的代码,即使方法里面什么都不写对子类仍然没有什么影响。这种情况就可以使用抽象方法和抽象类。
首先需要注意的是:抽象方法没有方法体,且所有继承了抽象类的子类必须重写所有的抽象方法。
抽象类中可以包括普通方法,并且抽象类不能被实例化。
namespace CsharpTest9_10
{
//注意抽象父类与子类都是Private
abstract class Animal
{
public abstract void Dosth();
}
}
子类的代码不变。但是此时就不能使用base.方法体了,因为根本就不存在方法体。
抽象类的使用场景:
1.父类方法不知道如何去实现;
2.父类没有默认实现且不需要实例化
总的来说,抽象方法和虚方法的差别并不是很大,实现的功能都差不多。抽象类保证了每个抽象方法都必须得到重写,我们就要根据实际需要来选择对应的方法。
III.接口 Interface
接口是从抽象类演变而来的————如果抽象类中的所有方法都是抽象方法,这个抽象类就可以叫做接口。当然,接口中的所有方法都不能有方法体。
接口中不能包含字段,但是可以包含属性。这里没有字段怎么编写属性呢?这里有一个自动属性的概念,
接口中的所有成员都默认为public,这是不能被修改的,自己也不能写上去。
我们可以将接口想象为一个插件,可以用来实现一些附加功能。
代码还是使用上一个例子中的那份,首先定义接口:在program中右键新建项,选择接口
namespace CsharpTest9_10
{
interface Interface1
{
void Eat();
}
}
在猫类中的接口调用:
namespace CsharpTest9_10
{
class cat : Animal,Interface1
{
public override void Dosth()
{
//base.Dosth();
Console.WriteLine("喵");
}
public void Eat()
{
Console.WriteLine("猫在进食");
}
}
}
狗类同理。
我们可以看到调用的语法就是在继承的父类后加上一个逗号,再写接口名即可。
此时在接口名上右键后点击实现接口,会自动生成接口中方法的实现。
接口的作用就是实现某些类的特殊功能。
总结
三者之间的关系,用一张图来表示。