目录
多态思想
在继承于同一个类的不同对象上执行任务时,多态是一种极其有效的技巧,使用的代码最少。能够把一组对象放到一个数组中然后调用它们的方法,在这种情况下多态的作用就体现出来了,这些对象不必是相同类型的对象。如果这些对象都有同名方法,则可以调用每个对象的同名方法。
虚方法
在学习多态的过程中我了解到,多态使子类的实例可以直接赋予父类的变量,然后直接通过这个变量调用子类的方法,而最简单的实现多态的方式就是通过重写虚方法。
方法通常在默认状态下都不是虚拟的,但我们可以用virtual关键字来声明方法为虚方法。定义虚方法后,可以(不是必须)在子类中重写虚方法,重写时要使用override关键字,这样在调用方法时可以调用对象类型的合适方法。简单的说,虚方法就是可以被子类重写的方法,如果子类重写了虚方法,那么运行时将使用重写后的逻辑,如果没有重写,则使用父类中虚方法的逻辑。
代码示例如下(bsae关键字与this关键字类似,this代表本类对象,base代表父类对象):
class People
{
public virtual void Say(string name)
{
Console.WriteLine(name);
}
}
class Student : People
{
public override void Say(string name)
{
base.Say(name + "学生");
}
}
class Teacher : People
{
public override void Say(string name)
{
base.Say(name + "老师");
}
}
class Program
{
static void Main(string[] args)
{
Console.Write("请输入姓名:");
string strName = Console.ReadLine();
People[] people = new People[2];
people[0] = new Student();
people[1] = new Teacher();
for (int i = 0; i < people.Length; i++)
{
people[i].Say(strName);
}
Console.ReadLine();
}
}
输入:大马叔 输出: 大马叔学生 大马叔老师
抽象类、抽象方法
什么是抽象类?
通过学习继承和多态,我发现在继承关系组成的继承树中,越是在上方的类越抽象,例如自行车继承于二轮车类,二轮车类继承于车类,车类继承于交通工具类。所以在一些特定的多态机制中,我们不需要父类的实例化对象而只需要子类,于是抽象类的概念出现了。
什么是抽象方法?
声明抽象类中需要使用的abstract关键字,而使用此关键字声明的方法称为抽象方法。抽象方法没有方法体,并且方法本身没有任何意义,除非被重写,但承载抽象方法的抽象类必须被继承。事实上抽象类如果不被继承将毫无意义。
语法上需要注意:
1.抽象方法必须声明在抽象类中。
2.声明抽象方法时,不能使用virtual、static和private修饰符
3.抽象方法没有方法体,即没有普通方法的大括号及其中的内容。
代码示例如下(抽象类和接口不能直接实例化对象,但可以间接引用):
public abstract class Car
{
public int produceYear;
public string Name;
public abstract void Show();
}
public class Bicycle : Car
{
public override void Show()
{
Console.WriteLine(produceYear + " " + Name);
}
}
class Program
{
static void Main(string[] args)
{
Bicycle a = new Bicycle();
Car b = a;
b.produceYear = 2023;
b.Name = "老马自行车";
b.Show();
Console.ReadKey();
}
}
输出结果为:2023 老马自行车
由于抽象类被继承后需要实现其中所有的抽象方法(方法名称、参数列表、返回值类型不能改变)。于是另一个问题出现了,如果我们把父类的方法声明为抽象方法,那么每个子类都要重写这个方法,那么也许某个子类不需要此方法也不得不重写此方法。理论上如果把该方法单独放在另一个类中,就可以让需要该方法的类继承这个类,不需要该方法的类继承自原来的抽象类父类,但是这种情况下所有的子类都需要抽象类父类,由此这种思路免不了需要多重继承,但是C#中类是不能多重继承的,面临这种情况,接口的概念出现了。
接口
因为多重继承通常会给程序带来复杂性等问题,但是客观世界中出现多重继承的情况又极多,于是接口的概念出现了。
我理解的接口就是抽象类Plus,也可以把它看做是纯粹的抽象类,和抽象类相同,接口中的所有方法都没有方法体,但是与抽象类又不太相同的方面就在一个子类只能继承一个抽象类,但是一个子类可以继承多个接口。
接口需要注意:
1.不能直接实例化接口。(可以间接引用)
2.接口不包含方法体。
3.接口不包含字段。(可以用属性封装)
4.类、结构体和接口自身可以从多个接口继承。
5.接口可以包含事件、索引器、方法和属性。
6.接口类似于抽象基类:继承接口的任何非抽象类都必须实现接口的所有成员。
PS:接口可以将方法、属性、索引器和事件作为成员,但是并不能设置这些成员的具体值。即只能定义,不能赋值。
声明接口时,interface关键字和接口名称不能省略(通常命名规范接口的名称加大写英文字母前缀“I”),其他都是可选项,但接口成员必须是公共的。
代码示例如下:
interface IPeople
{
string Name { get; set; }
int Age { get; set; }
}
interface IStudent : IPeople
{
void Study();
}
interface ITeacher : IPeople
{
void Teach();
}
class Program:IPeople,ITeacher,IStudent
{
string name;
int age;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
public void Study()
{
Console.WriteLine(Age + "岁" + Name + "学生");
}
public void Teach()
{
Console.WriteLine(Age + "岁" + Name + "老师");
}
static void Main(string[] args)
{
Program a = new Program();
IStudent student1 = a;
student1.Name = "张三";
student1.Age = 18;
student1.Study();
ITeacher teacher1 = a;
teacher1.Name = "李四";
teacher1.Age = 30;
teacher1.Teach();
Console.ReadKey();
}
}
输出结果为:18岁张三学生 30岁李四老师
显式接口成员实现
如果一个类继承自两个接口,但两个接口中具有相同名称的成员,那么在这个类中实现该成员将会导致两个接口都使用该成员作为它们的实现,可是在实际中我们可能需要两个接口实现不同的功能,那这时我们就需要显式实现该接口成员。其调用方式为接口名称+一个句点。
示例代码如下:
interface IAdd1
{
void Add();
}
interface IAdd2
{
void Add();
}
class Add : IAdd1, IAdd2
{
void IAdd1.Add()
{
int a = 1;
double b = 1.5;
Console.WriteLine(a + b);
}
void IAdd2.Add()
{
int a = 2;
int b = 3;
Console.WriteLine(a + b);
}
}
class Program
{
static void Main(string[] args)
{
Add a = new Add();
IAdd1 b = a;
b.Add();
IAdd2 c = a;
c.Add();
Console.ReadKey();
}
}
输出结果为:2.5 5
抽象类和接口的区别
从根本上抽象类和接口这两种类型用于完全不同的目的。抽象类主要用作对象系列的基类,共享某些主要特性,例如共有的目的和结构。接口则主要用于类,这些类在基础水平上有所不同(即基类特性不互通),但仍可以完成某些相同的任务。
1.抽象类中可以定义成员的实现,但接口中不可以。
2.抽象类中可以包含字段、构造函数、析构函数、静态成员或常量等,但接口中不可以。
3.它们的子类只能继承一个父类,即只能继承一个抽象类,但可以继承多个接口。
4.抽象类中的成员可以是private、protect、internal等
虚方法和抽象类的区别
1.抽象方法没有方法体,虚方法有方法体。
2.使用抽象方法后子类必须重写父类的抽象方法,使用虚方法后子类可以覆盖父类的方法体,也可以不覆盖。