zoukankan      html  css  js  c++  java
  • c#笔记整理 关于继承与多态等

    【 塔 · 第 二 条 约 定 】

    c#面向对象基础

    整理private、protected、public、abstract等的异同

    1. public 公有访问。不受任何限制。

    2. private 私有访问。只限于本类成员访问,子类,实例都不能访问。

    3. protected 保护访问。只限于本类和子类访问,实例不能访问

      三者的对比:

    • private和protected的共同点:外部都不可以访问。
    • private和protected的不同点:在同一类中可视为一样,但在继承中就不同了,private在派生类中不可以被访问,而protected可以。
    • public对任何类和成员都完全公开,无限制访问。
    • class 内默认为private。struct 内默认为public
      eg:
    class music
    {
        public string singer;
        protected int age;
        private int weight;
    }
    class palyer
    {
         music obj = new music();
         obj.singer = sam;    
         //obj.age = 21;  不能访问
        //obj.weight = 100;  不能访问 如果是子类则可以访问
    }
    
    1. abstract类不能被实例化,它只提供其他类的继承的接口

    整理有关继承、多态等概念

    为了提高软件模块的可复用性和可扩充性,以便提高软件的开发效率,我们总是希望能够利用前人或自己以前的开发成果,同时又希望在

    自己的开发过程中能够有足够的灵活性,不拘泥于复用的模块。C#这种完全面向对象的程序设计语言提供了两个重要的特性--

    继承性inheritance 和多态性polymorphism。

      继承是面向对象程序设计的主要特征之一,它可以让您重用代码,可以节省程序设计的时间。继承就是在类之间建立一种相交关系,使得

    新定义的派生类的实例可以继承已有的基类的特征和能力,而且可以加入新的特性或者是修改已有的特性建立起类的新层次。

      现实世界中的许多实体之间不是相互孤立的,它们往往具有共同的特征也存在内在的差别。人们可以采用层次结构来描述这些实体之间的相似之处和不同之处。
    最高层的实体往往具有最一般最普遍的特征,越下层的事物越具体,并且下层包含了上层的特征。
    它们之间的关系是基类与派生类之间的关系。
    为了用软件语言对现实世界中的层次结构进行模型化,面向对象的程序设计技术引入了继承的概念。一个类从另一个类派生出来时,

    派生类从基类那里继承特性。派生类也可以作为其它类的基类。从一个基类派生出来的多层类形成了类的层次结构。

      注意:C#中,派生类只能从一个类中继承。这是因为,在C++中,人们在大多数情况下不需要一个从多个类中派生的类。从多个基类中派生一个类这往往会带来许多问题,从而抵消了这种灵活性带来的优势。
    C#中,派生类从它的直接基类中继承成员:方法、域、属性、事件、索引指示器。除了构造函数和析构函数,派生类隐式地继承了直接基类的所有成员。

    eg:(搜刮的栗子,看着挺好理解。)

    using System ;
    
    class Vehicle //定义交通工具(汽车)类
    
    {
    
         protected int wheels ; //公有成员:轮子个数
    
         protected float weight ; //保护成员:重量
    
         public Vehicle( ){;}
    
         public Vehicle(int w,float g)
    
         {
    
            wheels = w ;
    
            weight = g ;
    
         }
    
         public void Speak( )
    
         {
    
            Console.WriteLine( "交通工具的轮子个数是可以变化的! " ) ;
    
         }
    
    } ;
    
    class Car:Vehicle //定义轿车类:从汽车类中继承
    
    {
    
        int passengers ; //私有成员:乘客数
    
        public Car(int w , float g , int p) : base(w, g)
    
        {
    
            wheels = w ;
    
            weight = g ;
    
            passengers=p ;
    
        }
    
    }//Vehicle 作为基类,体现了"汽车"这个实体具有的公共性质:汽车都有轮子和重量。Car 类继承了Vehicle 的这些性质,并且添加了自身的特性:可以搭载乘客。
    

    C#中的继承符合下列规则:

    1. 继承是可传递的。如果C从B中派生,B又从A中派生,那么C不仅继承了B中声明的成员,同样也继承了A中的成员。Object 类作为所有类的基类。

    2. 派生类应当是对基类的扩展。派生类可以添加新的成员,但不能除去已经继承的成员的定义。

    3. 构造函数和析构函数不能被继承。除此以外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类能否访问它们。

    4. 派生类如果定义了与继承而来的成员同名的新成员,就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这些成员。

    5. 类可以定义虚方法、虚属性以及虚索引指示器,它的派生类能够重载这些成员,从而实现类可以展示出多态性。

    6. 派生类只能从一个类中继承,可以通过接吕实现多重继承。

    7. 隐藏基类成员

      想想看,如果所有的类都可以被继承,继承的滥用会带来什么后果?类的层次结构体系将变得十分庞,大类之间的关系杂乱无章,对类的理解和使用都会变得十分困难。有时候,我们并不希望自己编写的类被继承。另一些时候,有的类已经没有再被继承的必要。C#提出了一个

    • 密封类(sealedclass)的概念,帮助开发人员来解决这一问题。
        密封类在声明中使用sealed修饰符,这样就可以防止该类被其它类继承。如果试图将一个密封类作为其它类的基类,C#将提示出错。

    理所当然,密封类不能同时又是抽象类,因为抽象总是希望被继承的。

    在哪些场合下使用密封类呢?密封类可以阻止其它程序员在无意中继承该类。而且密封类可以起到运行时优化的效果。实际上,密封类中不可能有派生类。如果密封类实例中存在虚成员函数,该成员函数可以转化为非虚的,函数修饰符virtual 不再生效。
    eg:

    abstract class A
    
       {
    
           public abstract void F( ) ;
    
       }
    
       sealed class B: A
    
       {
    
           public override void F( )
    
           { // F 的具体实现代码 }
    
       }
    
      如果我们尝试写下面的代码
    
        class C: B{ }
    

      C#会指出这个错误,告诉你B是一个密封类,不能试图从B 中派生任何类。
      

    •  使用 new 修饰符可以显式隐藏从基类继承的成员。若要隐藏继承的成员,请使用相同名称在派生类中声明该成员,并用 new 修饰符修饰它。

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

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

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

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

    • 编译时的多态性

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

    • 运行时的多态性

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

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

    静多态

    通过

    1. 函数重载
    2. 运算符重载

    实现

    动态多态

    通过

    1. 抽象类
    2. 虚方法

    实现

    虚方法

    当类中的方法声明前加上了virtual修饰符,我们称之为虚方法,反之为非虚。使用了virtual修饰符后,不允许再有static, abstract,或override 修饰符。

    eg:带有虚方法的类

    using System ;
    
    public class DrawingBase
    
    {
    
        public virtual void Draw( )
    
        {
    
            Console.WriteLine("这是一个虚方法!") ;
    
        }
    
    }
    
    虚方法与非虚方法的区别
    using System ;
    
    class A
    
    {
    
       public void F( )
    
       {
    
           Console.WriteLine("A.F") ;
    
       }
    
       public virtual void G( )
    
       {
    
           Console.WriteLine("A.G") ;
    
       }
    
    }
    
    class B: A
    
    {
    
       new public void F( )
    
       {
    
           Console.WriteLine("B.F") ;
    
       }
    
       public override void G( )
    
       {
    
           Console.WriteLine("B.G") ;
    
       }
    
    }
    
    class Test
    
    {
    
       static void Main( )
    
       {
    
          B b = new B( ) ;
    
          A a = b;
    
          a.F( ) ;
    
          b.F( ) ;
    
          a.G( ) ;
    
          b.G( ) ;
    
       }
    
    }
    

    A 类提供了两个方法:非虚的F 和虚方法G 。类B 则提供了一个新的非虚的方法F, 从而覆盖了继承的F; 类B 同时还重载了继承的方法G 。那么输出应该是:A.F B.F B.G B.G
    注意到本例中,方法a.G(实际调用了B.G,而不是A.G,这是因为编译时值为A,但运行时值为B ,所以B 完成了对方法的实际调用。在派生类中对虚方法进行重载

    注意:
    普通的方法重载指的是:类中两个以上的方法(包括隐藏的继承而来的方法),取的名字相同,只要使用的参数类型或者参数个数不同,编译器便知道在何种情况下应该调用哪个方法。

    而对基类虚方法的重载是函数重载的另一种特殊形式。在派生类中重新定义此虚函数时,要求的是方法名称,返回值类型、参数表中的参数个数、类型顺序都必须与基类中的虚函数完全一致。在派生类中声明对虚方法的重载,要求在声明中加上override 关键字,而且不能有new,static 或virtual 修饰符。

    多态性的栗子:

    eg:

    using System ;
    
    class Vehicle//定义汽车类
    
    {
    
       public int wheels; //公有成员轮子个数
    
       protected float weight; //保护成员重量
    
       public Vehicle(int w,float g)
    
       {
    
           wheels = w;
    
           weight = g;
    
       }
    
       public virtual void Speak( )
    
       {
    
          Console.WriteLine( " the w vehicle is speaking!" ) ;
    
       }
    
    };
    
    class Car:Vehicle //定义轿车类
    
    {
    
       int passengers; //私有成员乘客数
    
        public Car(int w,float g,int p) : base(w,g)
    
        {
    
            wheels = w;
    
            weight = g;
    
            passengers = p;
    
        }
    
        public override void Speak( )
    
        {
    
           Console.WriteLine( " The car is speaking:Di-di!" ) ;
    
        }
    
    }
    
    class Truck:Vehicle //定义卡车类
    
    {
    
        int passengers; //私有成员乘客数
    
        float load; //私有成员载重量
    
        public Truck (int w,float g,int p, float l) : base(w,g)
    
        {
    
            wheels = w;
    
            weight = g;
    
            passengers = p;
    
            load = l;
    
        }
    
        public override void Speak( )
    
        {
    
            Console.WriteLine( " The truck is speaking:Ba-ba!" ) ;
    
        }
    
        public static void Main( )
    
        {
    
            Vehicle v1 = new Vehicle(0,0 ) ;
    
            Car c1 = new Car(4,2,5) ;
    
            Truck t1 = new Truck(6,5,3,10) ;
    
            v1.Speak( ) ;
    
            v1 = c1;
    
            v1.Speak( ) ;
    
            c1.Speak( ) ;
    
            v1 = t1;
    
            v1.Speak( ) ;
    
            t1.Speak( ) ;
    
        }
    
    }
    
    • Vehicle 类中的Speak 方法被声明为虚方法,那么在派生类中就可以重新定义此方法。

    • 在派生类Car 和Truck 中分别重载了Speak 方法,派生类中的方法原型和基类中的方法原型必须完全一致。

    • 在Test 类中,创建了Vehicle 类的实例v1, 并且先后指向Car 类的实例c1 和Truck 类的实例t1。

    • 运行该程序结果应该是:

    The Vehicle is speaking!

    The car is speaking:Di-di!

    The car is speaking:Di-di!

    The truck is speaking:Ba-ba!

    The truck is speaking:Ba-ba!

    • 这里,Vehicle 类的实例v1 先后被赋予Car类的实例c1, 以及Truck类的实例t1的值。在执行过程中,v1先后指代不同的类的实例,从而调用不同的版本。这里v1 的Speak 方法实现了多态性,并且v1.Speak究竟执行哪个版本,不是在程序编译时确定的,而是在程序的动态运行时,根据v1 某一时刻的指代类型来确定的,所以还体现了动态的多态性。
  • 相关阅读:
    除去DataTable中的空行!
    通过cmd命令安装、卸载、启动和停止Windows Service(InstallUtil.exe)
    installshield学习笔记
    托管与非托管
    C#语言之“String.Split”的使用【转】
    C#语言之“中英文混合字符串对齐”的方法
    栈和堆的区别
    UNICODE与ANSI的区别
    C#多线程:深入了解线程同步lock,Monitor,Mutex,同步事件和等待句柄(中)
    java自定义注解
  • 原文地址:https://www.cnblogs.com/mercuialC/p/6383896.html
Copyright © 2011-2022 走看看