多态的实现(重载,虚方法,抽象类,接口)
1.前言
多态是指,在同一个方法中,由于参数不同而导致执行效果各异。多态的实现方式主要是通过函数、运算符重载,虚成员,以及抽象类实现和接口。下面的内容就详细介绍一下前三种多态的实现形式。
2.方法重载
在同一作用域范围内,可以为同一个方法名声明多个定义,但是方法之间的定义必须不同,可以是参数列表的类型或个数的差异,但不可以重载只有返回类型不同的函数。下面的示例展示了如何实现方法的重载。
class Person
{
public void Show()
{
Console.WriteLine($"name: kyle");
}
public void Show(int age)
{
Console.WriteLine($"name: kyle, age: {age}");
}
}
class Program
{
static void Main(string[] args)
{
Person p = new Person();
p.Show();
p.Show(23);
Console.ReadKey();
}
}
输出结果如下:
name: kyle
name: kyle, age: 23
常见的函数重载是对 Object 类中的 ToString()方法进行重载:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return "Person: " + Name + " " + Age;
}
}
其测试代码及结果如下:
Person person = new Person { Name = "John", Age = 12 };
Console.WriteLine(person);
// Output:
// Person: John 12
3.运算符重载
可以重定义或重载 C# 中内置的运算符,以此使用用户自定义类型的运算符。重载运算符是具有特殊名称的函数,是通过关键字 operator 后跟运算符的符号来定义的。与其他函数一样,重载运算符有返回类型和参数列表。
下面的代码演示了运算符 + 的重载:
class Box
{
double length;
double width;
public Box(double l, double w)
{
length = l;
width = w;
}
public double Area()
{
return length * width;
}
public static double operator + (Box box1, Box box2)
{
return box1.Area() + box2.Area();
}
}
class Program
{
static void Main(string[] args)
{
Box b1 = new Box(2.3, 1.2);
Box b2 = new Box(4.4, 2.6);
double total = b1 + b2;
Console.WriteLine($"total area is {total}");
Console.ReadKey();
}
}
输出结果如下:
total area is 14.2
3.虚成员
3.1 override重写
当派生类从普通基类继承时,它会获得基类的所有公开方法、字段、属性和事件,当基类的成员声明为 virtual 时,派生类还可以对基类的成员(除字段)进行重写。在进行成员的重写时,派生类中需使用 override 关键字显示成员的重写。
下面的代码展示了对基类中的虚成员的重写:
public class BaseClass
{
public virtual void DoWork() { }
public virtual int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public override void DoWork() { }
public override int WorkProperty
{
get { return 0; }
}
}
在使用 override 重写虚成员时,会对基类中的虚方法的覆盖,因此即便将派生类的示例转为基类的实例,仍会调用重写后的新成员,调用过程及结果如下:
DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.
BaseClass A = (BaseClass)B;
A.DoWork(); // Also calls the new method.
3.2 使用新成员隐藏基类成员
如果不希望覆盖掉基类中的既有虚成员,可以通过 new 关键字(默认),从而禁止派生类参与基类的虚成员调用。
以下代码展示了如何使用新成员来隐藏基类成员,而不是重写覆盖:
public class BaseClass
{
public void DoWork() { WorkField++; }
public int WorkField;
public int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public new void DoWork() { WorkField++; } // 不覆盖基类虚方法
public new int WorkField;
public new int WorkProperty
{
get { return 0; }
}
}
在使用 new 关键字重写虚成员后,如果将派生类的实例转换为基类的实例,将会调用基类原有的虚成员,调用过程及结果如下:
DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.
BaseClass A = (BaseClass)B;
A.DoWork(); // Calls the old method.
3.3 阻止派生类重写虚成员
在类的继承过程中,虚成员默认始终保持其虚拟性(因此 override 关键字声明的重写成员也具有虚拟性),如果希望停止其虚拟继承,可以将虚成员声明为 sealed 来实现,其实现代码如下:
public class A
{
public virtual void DoWork() { }
}
public class B : A
{
public override void DoWork() { }
}
public class C : B
{
public sealed override void DoWork() { }
}
在使用 sealed 声明虚成员之后,有 C 继承而来的类中的 Dowork() 不再保留虚拟性,但对于 C 的实例而言仍旧是虚拟的。C 的派生类可以通过 new 关键字最后重写 C 中的 Dowork() :
public class D : C
{
public new void DoWork() { }
}
3.4 在派生类中访问基类虚成员
如果要在派生类中直接访问基类的虚成员,可以使用 base 关键字进行访问,示例代码如下:
public class Base
{
public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
public override void DoWork()
{
//Perform Derived's work here
//...
// Call DoWork on base class
base.DoWork();
}
}
4.抽象类
通过 abstract 关键字声明一个抽象类,例如:
public abstract class A
{
// Class members here.
}
抽象类不可以进行实例化,其作用是作为一个可供多个派生类继承的通用基类。在抽象类中可以定义字段、属性、方法等,还可以通过关键字 abstract 添加到方法的返回类型前定义一个抽象方法,例如:
public abstract class A
{
public int name;
public string Show()
{
return "string";
}
public abstract void DoWork(int i);
}
抽象方法没有实现,抽象类的派生类必须实现所有的抽象方法。当抽象类从基类继承虚方法时,抽象类也可以使用抽象方法重写虚方法,例如:
public class D
{
public virtual void DoWork(int i)
{
// Original implementation.
}
}
public abstract class E : D
{
public abstract override void DoWork(int i);
}
public class F : E
{
public override void DoWork(int i)
{
// New implementation.
}
}
继承抽象方法的类无法访问方法的原始实现,即类 F 上的实例无法调用 D 中的 DoWork() ,通过这种方式,抽象类可强制派生类向虚拟方法提供新的方法实现。
由于抽象类需要作为基类由派生类继承,所以,抽象类无法使用 sealed 关键字和 static 关键字进行声明。