目录
定义
多态即相同类型的对象调用相同的方法却表现出不同行为的现象
分类
- 编译时的多态性
编译时的多态性通过重载实现,即在同一个类中定义多个方法,这些方法具有相同的名称但参数列表(方法签名)不同,编译器根据传递给方法的参数类型和参数数量来决定调用哪个方法。
- 运行时的多态性
运行时的多态性通过重写实现,即派生类重写基类中已经存在的方法,当基类和派生类都定义了同名的方法时,通过基类引用派生类对象来调用方法时,实际上会调用派生类中重写的方法。
实现方式
- 接口实现多态
- 继承实现多态
- 抽象类实现多态
实现方式举例:https://www.cnblogs.com/liuling2010/archive/2010/12/10/1902283.html
使用基类的引用
派生类的实例由基类的实例和派生类新增的成员组成,派生类的引用指向整个类对象,包括基类部分。如果有一个派生类对象的引用,就可以获取该对象基类部分的引用(通过类型转化),将派生类对象强制转化为基类对象后产生的变量只能访问基类的成员,但是实现却是派生类的实现。
PS:为什么要这样强制转化为基类对象而不是直接使用派生类对象?我的理解是为了更好的拓展,如果我有一个方法需要传入一个类型并调用父类的某一个方法,但是有时候不同情况我需要不同的实现,如果方法传入类型我写的是某个派生类类型,那么有几个派生类我就要写几个方法,但是如果方法传入类型是父类的话我可以传不同的派生类进去,而且调用父类的某个方法也是你传进来的派生类的方法,就不用再写多一个方法。
/// <summary>
/// 基类
/// </summary>
class A
{
public virtual void Print()
{
Console.WriteLine("this is A");
}
}
/// <summary>
/// 派生类
/// </summary>
class B : A
{
public int Id { get; set; }
public override void Print()
{
Console.WriteLine("this is not A,this is B");
}
}
/// <summary>
/// 派生类
/// </summary>
class C : A
{
public string Id { get; set; }
public override void Print()
{
Console.WriteLine("this is not A,this is C");
}
}
/// <summary>
/// 方法类
/// </summary>
class Method
{
/// <summary>
/// 需要调用基类某个方法的方法
/// </summary>
/// <param name="a"></param>
public static void UsePrint(A a)
{
a.Print();
}
/// <summary>
/// 如果参数指定的是某个派生类的话,那你这个方法就只能调用这个派生类的实现
/// 如果你想再要一个C的实现就又得写一个方法public void UsePrint(C c)
/// </summary>
/// <param name="b"></param>
public static void UsePrint(B b)
{
b.Print();
}
}
internal class Program
{
static void Main(string[] args)
{
A a1 = new B();
A a2 = new C();
Method.UsePrint(a1); //输出:this is not A,this is B
Method.UsePrint(a2); //输出:this is not A,this is C
//Method.UsePrint(new B()); 也是可以的,派生类可以隐式转换为基类
}
}
下面的图展示了上面例子A a1 = new B();的内存情况,B类型的实例包括了基类(A)的实例和新增的成员,由于B重写了A的方法所以A实例中的Print方法指向了B的Print方法。A a1 = new B()可以看做是B b = new B();A a1 = (B)b;这里b指向的就是下面的B实例,对B进行类型转化为A其实就是把B实例的A实例指针返回给a1。
override和new
通过override和new都可以重写父类中的方法,但是两者在转换为基类对象时,基类的引用访问得到的结果却不一样。总结来说,如果派生类通过override覆写了基类方法,那么派生类转换为基类时基类被覆写的方法实现就是派生类的实现;如果派生类通过new隐藏了基类方法,那么派生类转换为基类时基类被隐藏的方法实现就是基类的实现。
覆写方法可以在继承的任何层级出现,当使用对象基类部分的引用调用一个被覆写的方法时,方法的调用会沿着派生层次上溯执行,一直到标记为override的方法的最高派生版本。举例来说如果最顶级的基类是A,然后有B继承A、C继承B、D继承C且每一个派生类都使用override重写了基类的Print方法,那么执行A a = new D();a.Print();时,a会一层一层往上找使用override重写的Print方法,经过A->B->C->D最终执行D里面的Print方法。
下面举例说明override和new的区别
/// <summary>
/// 基类
/// </summary>
class BaseClass
{
public virtual void Print()
{
Console.WriteLine("This is the base class");
}
}
/// <summary>
/// 派生类
/// </summary>
class DerivedClass : BaseClass
{
public override void Print()
{
Console.WriteLine("This is the derived class");
}
}
/// <summary>
/// 派生类(覆盖方法)
/// </summary>
class SecondDerivedForOR :DerivedClass
{
public override void Print()
{
Console.WriteLine("This is the second class");
}
}
/// <summary>
/// 派生类(隐藏方法)
/// </summary>
class SecondDerivedForNew : DerivedClass
{
public new void Print()
{
Console.WriteLine("This is the second class");
}
}
class Program
{
static void Main(string[] args)
{
SecondDerivedForOR forOR = new SecondDerivedForOR();
SecondDerivedForNew forNew = new SecondDerivedForNew();
BaseClass baseClass1 = (BaseClass)forOR;
BaseClass baseClass2 = (BaseClass)forNew;
baseClass1.Print(); //This is the second class
baseClass2.Print(); //This is the derived class
}
}