【C#基础概念 】多态-----面向对象编程的三大特性 |多态--编程语言和类型论

前言(Preamble)

在理解多态之前先理解设计原则(Solid)中里氏替换原则

里氏替换原则(Liskov Substitution Principle):派生类(子类)对象能够替换其基类(超类)对象被使用。通俗一点的理解就是“子类是父类”,举个例子,“男人是人,人不一定是男人”,当需要一个父类类型的对象的时候可以给一个子类类型的对象;当需要一个子类类型对象的时候给一个父类类型对象是不可以的!

里氏替换原则 满足逻辑 中的充分不必要条件(A是B的充分不必要条件)。以及设计模型的依赖倒置模型。

多态性

定义:在编程语言类型论中,多态(英語:polymorphism)指为不同数据类型的实体提供统一的接口[1],或使用一个单一的符号来表示多个不同的类型[2]。---维基百科

最常见主要多态类别有:特定多态、参数多态、子类型多态。

在面像对象编程中的多态指 子类型多态,而「参数多态」则一般被称作泛型编程。

编程语言的多态性和面像对象编程的多态有什么区别?

 一门编程语言可以写出多种编程范式:面像对象、面像泛型、面像过程、面像函数(表达式)。而面像对象编程的多态性,只是语言多态性一种实现方式之一。

 面像对象编程中的多态定义:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。

多态的作用:

多态其实是一种行为的封装,多态实现代码的通用性。继承的作用是实现代码的复用性。

多态的最常见主要类别有:

特定多态(重载)

特定多态(ad hoc polymorphism)是程序设计语言的一种多态,多态函数有多个不同的实现,依赖于其实参而调用相应版本的函数。因此,特设多态仅支持有限数量的不同类型。函数重载乃至运算符重载也是特设多态的一种。是静态多态

特定的多态的实现方式就是:重载


参数多态(泛型)

「参数多态」则一般被称作泛型编程。 在程序设计语言与类型论中是指声明与定义函数、复合类型、变量时不指定其具体的类型,而把这部分类型作为参数使用,使得该定义对各种具体类型都适用。参数化多态使得语言更具表达力,同时保持了完全的静态类型安全。[1] 这被称为泛化函数、泛化数据类型、泛型变量,形成了泛型编程的基础。类类型和方法类型的方向是静态多态 接口类型泛型是动态多态。

特定的多态的实现方式就是:泛型


子类型多态(重写)

也叫包含多态是一种类型多态的形式。一个名字指称很多不同的类的实例,这些类有某个共同的超类[3]。
1、子类型多态遵循里氏替换原则。
2、子类型反映了类型(即面向对象中的接口)之间的关系;而继承反映了一类对象可以从另一类对象创造出来,是语言特性的实现也称作实现继承。
3、在面向对象程序设计中,多态一般仅指这里所说的「子类型多态」。 「参数多态」则一般被称作泛型编程。
4、实现子类型多态是重写(override)。

5、具体表现就是虚函数和抽象函数(接口是抽象函数)。

 多态可分为变量多态与函数多态。变量多态是指:基类型的变量(对于C++是引用或指针)可以被赋值基类型对象,也可以被赋值派生类型的对象。函数多态是指,相同的函数调用界面(函数名与实参表),传送给一个对象变量,可以有不同的行为,这视该对象变量所指向的对象类型而定。多态也可定义为“一种将不同的特殊行为和单个泛化记号相关联的能力”,变量多态是函数多态的基础。

具体的子类型多态实现如下:

Animal an =new Birl();
an.Eat();
an.Sleep();
an = new Dog();
an.Eat();
an.Sleep();
 
public abstract  class Animal
{
    public abstract void Eat();

    public virtual void Sleep()
    {

        Console.WriteLine("在睡觉");
    }

}
 
public class Birl:Animal
{
    public override void Eat()
    {

        Console.WriteLine("鸟在吃饭");
    }
    public override void  Sleep()
    {
        Console.WriteLine("鸟在树上睡觉");

    }

}
public class Dog : Animal
{
    public override void Eat()
    {

        Console.WriteLine("狗在吃饭");
    }
    public override void Sleep()
    {
        Console.WriteLine("狗在地上睡觉");

    }


}
/*输出:
鸟在吃饭
鸟在树上睡觉
狗在吃饭
狗在地上睡觉*/

 

 

 

 

编译时的多态性和运行时的多态性

依据实现时做出的选择,多态可分为:编译时的多态性和运行时的多态性


● 编译时的多态性

编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。C# 提供了两种技术来实现静态多态性。分别为:

  • 函数重载
  • 运算符重载(运算符的本质就是方法)

您可以在同一个范围内对相同的函数名有多个定义。函数的定义必须彼此不同,可以是参数列表中的参数类型不同,也可以是参数个数不同。不能重载只有返回类型不同的函数声明

● 运行时的多态性

运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中,运行时的多态性通过虚成员实现。C# 提供了两种技术来实现动态多态性分别为:

  • 虚方法
  • 抽象方法
  • 接口



具体的检查的流程如下:

1、当调用一个对象的函数时,系统会直接去检查这个对象申明定义的类,即申明类,看所调用的函数是否为虚函数;

2、如果不是虚函数,那么它就直接执行该函数。而如果有virtual关键字,也就是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。

3、在这个实例类里,他会检查这个实例类的定义中是否有重新实现该虚函数(通过override关键字),如果是有,那么OK,它就不会再找了,而马上执行该实例类中的这个重新实现的函数。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重载了该虚函数的父类为止,然后执行该父类里重载后的函数。

 class A
        {
            public void Greeding()
            {
                Console.WriteLine("A:Hello!");
            }

            public virtual void Gree2()
            {
                Console.WriteLine("A:Gree2!");
            }

            public virtual void G3()
            {
                Console.WriteLine("A:G3!");
            }
        }

        class B : A
        {
            public void Greeding()
            {
                Console.WriteLine("B:Hello!");
            }

            public override void Gree2()
            {
                Console.WriteLine("B:Gree2!");
            }

            public override void G3()
            {
                Console.WriteLine("B:G3!");
            }
        }

        class C : B
        {
            public void GreedC()
            {
                base.Greeding();
            }
            public void Gree2()
            {
                Console.WriteLine("C:Gree2!");
            }

            public void G3()
            {
                base.Gree2();
            }
        }
        class Test
        {
            static void Main()
            {
                A aa = new C();
                //aa.GreedC(); 报错
                C cc = new C();
                aa.Greeding();// A:Hello!
                aa.Gree2();   // B:Gree2!
                aa.G3();      // B:G3!
                cc.Gree2();   // C:Gree2!
                cc.Greeding();// B:Hello!
                cc.GreedC();  // B:Hello!
            }
        }

对于非虚方法来说,调用哪个方法取决于该实例编译时的类型。

但对于虚方法来说,调用哪个方法取决于该实例运行时的类型。

转:https://www.cnblogs.com/zhaoshujie/p/10502404.html

posted @ 2021-10-25 02:12  小林野夫  阅读(376)  评论(0编辑  收藏  举报
原文链接:https://www.cnblogs.com/cdaniu/