再说C#多态

来源:互联网 发布:淘宝卖家联系方式修改 编辑:程序博客网 时间:2024/05/17 06:41

一、什么是多态

面向对象程式设计中的另外一个重要概念是多态性。在运行时,能够通过指向基类的指针,来调用实现派生类中的方法。能够把一组对象放到一个数组中,然后调用他们的方法,在这种场合下,多态性作用就体现出来了,这些对象不必是相同类型的对象。当然,假如他们都继承自某个类,您能够把这些派生类,都放到一个数组中。假如这些对象都有同名方法,就能够调用每个对象的同名方法。

同一操作作用于不同的对象,能够有不同的解释,产生不同的执行结果,这就是多态性。多态性通过派生类重载基类中的虚函数型方法来实现。

在面向对象的系统中,多态性是个很重要的概念,他允许客户对一个对象进行操作,由对象来完成一系列的动作,具体实现哪个动作、如何实现由系统负责解释。

“多态性”一词最早用于生物学,指同一种族的生物体具备相同的特性。在c#中,多态性的定义是:同一操作作用于不同的类的实例,不同的类将进行不同的解释,最后产生不同的执行结果。c#支持两种类型的多态性:

● 编译时的多态性

编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。

● 运行时的多态性

运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。c#中,运行时的多态性通过虚成员实现

编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。

二、实现多态

多态性是类为方法(这些方法以相同的名称调用)提供不同实现方式的能力。多态性允许对类的某个方法进行调用而无需考虑该方法所提供的特定实现。例如,可能有名为 road 的类,他调用另一个类的 drive 方法。这另一个类 car 可能是 sportscar 或 smallcar,但二者都提供 drive 方法。虽然 drive 方法的实现因类的不同而异,但 road 类仍能够调用他,并且他提供的结果可由 road 类使用和解释。

能够用不同的方式实现组件中的多态性:

● 接口多态性。

● 继承多态性。

● 通过抽象类实现的多态性。

接口多态性

多个类可实现相同的“接口”,而单个类能够实现一个或多个接口。接口本质上是类需要如何响应的定义。接口描述类需要实现的方法、属性和事件,连同每个成员需要接收和返回的参数类型,但将这些成员的特定实现留给实现类去完成。

组件编程中的一项强大技术是能够在一个对象上实现多个接口。每个接口由一小部分紧密联系的方法、属性和事件组成。通过实现接口,组件能够为需要该接口的任何其他组件提供功能,而无需考虑其中所包含的特定功能。这使后续组件的版本得以包含不同的功能而不会干扰核心功能。其他研发人员最常使用的组件功能自然是组件类本身的成员。然而,包含大量成员的组件使用起来可能比较困难。能够考虑将组件的某些功能分解出来,作为私下实现的单独接口。

根据接口来定义功能的另一个好处是,能够通过定义和实现附加接口增量地将功能添加到组件中。长处包括:

1.简化了设计过程,因为组件开始时能够很小,具备最小功能;之后,组件继续提供最小功能,同时不断插入其他的功能,并通过实际使用那些功能来确定合适的功能。

2.简化了兼容性的维护,因为组件的新版本能够在添加新接口的同时继续提供现有接口。客户端应用程式的后续版本能够利用这些接口的长处。

通过继承实现的多态性

多个类能够从单个基类“继承”。通过继承,类在基类所在的同一实现中接收基类的任何方法、属性和事件。这样,便可根据需要来实现附加成员,而且能够重写基成员以提供不同的实现。请注意,继承类也能够实现接口,这两种技术不是互斥的。

c# 通过继承提供多态性。对于小规模研发任务而言,这是个功能强大的机制,但对于大规模系统,通常证实会存在问题。过分强调继承驱动的多态性一般会导致资源大规模地从编码转移到设计,这对于缩短总的研发时间没有任何帮助。

何时使用继承驱动的多态性呢?使用继承首先是为了向现有基类添加功能。若从经过完全调试的基类框架开始,则程式员的工作效率将大大提高,方法能够增量地添加到基类而不中断版本。当应用程式设计包含多个相关类,而对于某些通用函数,这些相关类必须共享同样的实现时,您也可能希望使用继承。重叠功能能够在基类中实现,应用程式中使用的类能够从该基类中派生。抽象类合并继承和实现的功能,这在需要二者之一的元素时可能很有用。

通过抽象类实现的多态性

抽象类同时提供继承和接口的元素。抽象类本身不能实例化,他必须被继承。该类的部分或全部成员可能未实现,该实现由继承类提供。已实现的成员仍可被重写,并且继承类仍能够实现附加接口或其他功能。

抽象类提供继承和接口实现的功能。抽象类不能示例化,必须在继承类中实现。他能够包含已实现的方法和属性,但也能够包含未实现的过程,这些未实现过程必须在继承类中实现。这使您得以在类的某些方法中提供不变级功能,同时为其他过程保持灵活性选项打开。抽象类的另一个好处是:当需要组件的新版本时,可根据需要将附加方法添加到基类,但接口必须保持不变。

何时使用抽象类呢?当需要一组相关组件来包含一组具备相同功能的方法,但同时需要在其他方法实现中具备灵活性时,能够使用抽象类。当预料可能出现版本问题时,抽象类也具备价值,因为基类比较灵活并易于被修改。

 二、方法覆盖与多态

C#示例:
//myClass.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace myClass
{
    class myFirst
    {
        int value_myFirst;
        public myFirst(int f)
        {
            value_myFirst = f;
        }
        public void f1()
        {
            System.Console.WriteLine("myFirst.f1()!");
        }
        public virtual void f2()  //virtual也可以提到最前面
        {
            System.Console.WriteLine("myFirst.f2()!");
        }
    }

    class mySecond : myFirst
    {
        int value_mySecond;

        public mySecond(int s)
            : base(s)
        {
            value_mySecond = s;
        }
       
        //使用关键字new覆盖基类中的同名方法
        public new void f1()     //new也可以提到最前面
        {
            System.Console.WriteLine("mySeconde.f1()!");
        }
       
        //error当基类函数myFirst.f1()没有声明为virtual,abstract时不能override!
        //public override void f1() 
        //{
        //    System.Console.WriteLine("mySeconde.f1()!");
        //}
       
        //基类函数中虽然声明是virtual,但是仍然可以用new覆盖。
        //public new void f2()
        //{
        //    System.Console.WriteLine("mySeconde.f2()!");
        //}
       
        ////基类函数中声明是virtual,必须用override覆盖。
        public override void f2()   //override也可以提到最前面
        {
            System.Console.WriteLine("mySeconde.f2()!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            myFirst mf = new myFirst(1);
            mySecond ms = new mySecond(2);
            mf.f1(); //myFirst.f1()!
            mf.f2(); //myFirst.f2()!
            ms.f1(); //mySeconde.f1()!
            ms.f2(); //mySeconde.f2()!

            mf = ms; //向上转型之后
            mf.f1(); //myFirst.f1()!
            mf.f2();//mySeconde.f2()! 这是用override的运行结果;
            //如果是new那么,结果是myFirst.f2( )!
     

        }
    }
}

 

三、抽象类

C#示例
上面已经说明,虽然基类方法声明为virtual,以便派生类用override覆盖,但是派生类仍然可以用
new关键字覆盖(不具有多态性)。
可以强制让派生类覆盖基类的方法,将基类方法声明为抽象的,采用abstract关键字。
抽象方法没有方法体,由派生类来提供。

如果派生类不实现基类的抽象方法,则派生类也需要声明为abstract类

//myClass.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace myClass{
   
    //类中只要存在抽象方法,就必须声明为抽象类
    abstract class myFirst 
    {
        int value_myFirst;
        public myFirst(int f)
        {
            value_myFirst = f;
        }
       
        //抽象方法没有方法体,以分号结尾。
        public abstract void f1();
       
        public void f2()
        {
            System.Console.WriteLine("myFirst.f2()!");
        }

        public virtual void f3()
        {
            System.Console.WriteLine("myFirst.f3()!");
        }
    }

    class mySecond : myFirst
    {
        int value_mySecond;

        public mySecond(int s)
            : base(s)
        {
            value_mySecond = s;
        }
       
        //覆盖基类抽象方法
        public override  void f1()  
        {
            System.Console.WriteLine("mySeconde.f1()!");
        }
        
        //覆盖基类一般方法    
        public new void f2()
        {
            System.Console.WriteLine("mySeconde.f2()!");
        }

        //覆盖基类虚拟方法
        public override void f3()
        {
            System.Console.WriteLine("mySecond.f3()!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //抽象类和接口不能声明对象
            //myFirst mf = new myFirst(1); 
            mySecond ms = new mySecond(2);
           
            ms.f1();  //mySeconde.f1()!
            ms.f2();  //mySeconde.f2()!
            ms.f3();  //mySecond.f3()!

            //这里向上转型采用强类型转换的方式
            ((myFirst)ms).f1();   //mySeconde.f1()!
            ((myFirst)ms).f2();   //myFirst.f2()!
            ((myFirst)ms).f3();   //mySecond.f3()!
           
        }
    }
}

原创粉丝点击