虚方法
虚方法存在于相对于需要实现多态的子类的父类当中,同时也是实现多态的最基本的方法。
具体语法:父类的方法,用virtual修饰,表示虚方法。继承它的子类,在内部用override进行重写。
下面进行案例分析:
猫和狗都是动物,他们都会叫,但是叫声不一样。提取出相同的部分,即叫声,将叫声实现为多态。
新建父类:Animal,新建子类Cat、Dog,代码如下:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Animal animal = new Animal();
animal.Cry();
Cat cat = new Cat();
cat.Cry();
Dog dog = new Dog();
dog.Cry();
Console.ReadKey();
}
}
/// <summary>
/// 动物类
/// </summary>
public class Animal
{
public virtual void Cry()
{
Console.WriteLine("动物在叫!");
}
}
/// <summary>
/// 猫类
/// </summary>
public class Cat : Animal
{
public override void Cry()
{
Console.WriteLine("小猫喵喵!");
}
}
/// <summary>
/// 狗类
/// </summary>
public class Dog : Animal
{
public override void Cry()
{
Console.WriteLine("小狗汪汪!");
}
}
}
执行结果:
抽象方法和抽象类
抽象方法和抽象类的关键字都是:abstract
如果父类的方法很抽象,而且没有具体的do(做什么)即方法体,必须要子类进行重写才有实际意义的话,这种情况就需要用抽象方法了。
实例代码:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
cat.Eat();
Dog dog = new Dog();
dog.Eat();
Console.ReadKey();
}
}
/// <summary>
/// 动物类
/// </summary>
public abstract class Animal
{
public abstract void Eat();
}
/// <summary>
/// 猫类
/// </summary>
public class Cat : Animal
{
public override void Eat()
{
Console.WriteLine("小猫在吃猫粮!");
}
}
/// <summary>
/// 狗类
/// </summary>
public class Dog : Animal
{
public override void Eat()
{
Console.WriteLine("小狗在吃狗粮!");
}
}
}
执行结果:
抽象方法,必须存在于抽象类当中。相反,抽象类中不一定全部是抽象方法。比如我们可以在里面写上普通方法,有实现的虚方法或者没有实现的虚方法都可以。抽象方法没有方法体,继承了抽象类的子类,必须实现父类的所有的抽象方法。
在虚方法中,假如子类需要完成多态的表现,而父类的方法实际上也有实际意义,那父类可以选择实现,等需要子类重写的时候去重写实现调用,父类的方法暂时没有实际意义,也可以不用具体实现。所以,父类的虚方法可以实现(有方法体),也可以不实现(没有方法体)。而抽象方法必须通过子类的重写来实现。
抽象类使用场景:
1.父类方法不知道如何去使用
2.父类没有默认实现,而且不需要实例化
总的来说:抽象方法和虚方法差不多,实现的功能都差不多。抽象类保证了所有的抽象方法必须得到重写,而虚方法可以根据需要来选择是否进行重写。
接口:Interface
接口和抽象类差不多,区别在于,接口内包含的全部是未实现的方法。而且接口类和方法的关键词不需要再声明abstract,接口类的关键词,interface,一般定义接口名称,按照约定,我们会在名称前面加上一个I。例如下图的打印机接口。
里面有一个未实现的方法。下面定义一个惠普打印机实现类,继承自接口IPrinter
实例完整代码:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
HPPrinter hp = new HPPrinter();
hp.Print();
Console.ReadKey();
}
}
/// <summary>
/// 打印接接口
/// </summary>
public interface IPrint
{
void Print();
}
/// <summary>
/// 惠普打印机类
/// </summary>
class HPPrinter : IPrint
{
public void Print()
{
Console.WriteLine("惠普打印机正在打印文件!");
}
}
}
执行结果:
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。
例如上面实例中,接口IPrint定义了一个打印机(是什么),派生类HPPrinter说明了是惠普打印机打印文件(怎么做)。
接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。接口使得实现接口的类或结构在形式上保持一致。
抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。