多态的实现(重载,虚方法,抽象类,接口)

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 关键字进行声明。

posted @ 2018-10-18 11:27  Kyle0418  阅读(469)  评论(0编辑  收藏  举报