多态
多态
就是对象可以表现成多个类型的能力
多态就是调用方法的时候不用关心调用的到底是哪个子类的那个对象
子类可以和父类有相同的方法名:
签名不同(重载)
签名相同(隐藏基类方法)
子类和父类的方法重名的时候,子类会先调用自己的方法
如果子类没有某个方法,在调用的时候会调用父类的方法,但是参数是自己的
如果子类和父类的方法名重名了,会报一个警告,处理方法:
一、在子类的方法的访问修饰符后面添加一个关键字new就OK 了,这种方式是隐藏了基类的方法(少用)
二、将父类中的方法的访问修饰符后面添加一个virtual关键字,表示这是一个虚方法,然后在子类的方法访问修饰符后面添加一个override,来重写父类的方法
隐藏基类成员
要隐藏一个继承的数据成员,需要声明一个新的相同的数据类型成员,并且使用相同的名称。通过在派生类中声明新的带有相同签名的函数成员,可以隐藏或者掩盖继承的函数成员。签名由函数名和参数列表组成,但是不包括返回值类型。
而且要让编译器知道是故意隐藏继承的成员,是用new运算符。如果不使用它,程序能够成功的运行,但是会有一个警告
可以隐藏静态成员
class SomeClass //基类 { public string field = "SomeClass FieldOne"; public void Method(string value) { Console.WriteLine("SomeClass Method : {0}",value ); } } class OtherClass : SomeClass //派生类 { new public string field = "OtherClass Field"; //使用new关键字掩盖了基类成员 new public void Method(string value) //使用new关键字掩盖了基类成员 { Console.WriteLine("OtherClass Method : {0}",value ); } }
<span style="font-size:18px;"> class Program //主程序中 { static void Main(string[] args ) { OtherClass oc = new OtherClass(); oc.Method(oc.field); //使用掩盖成员,输出结果为:OtherClass Method : OtherClass Field } } 在子类中,同样可以访问被隐藏的基类成员,通过base.访问成员来实现:在上面OtherClass中添加一个方法 public void PrintField () { Console.WriteLine(field); //主程序调用后,输出的结果为:OtherClass Field Console.WriteLine(base.field); //主程序调用后,输出的结果为:SomeClass FieldOne } } //主程序中调用 oc.PrintField(); </span>
重写父类的方法要求
- 方法不能够用static修饰
- 方法的重写与基类的签名必须一致
- 重写的方法和被重写的方法具有相同的可访问性
- 方法、属性、索引、事件都可以被声明为virtual和override
- 父类方法中virtual不能够与private一起使用,子类中访问不到这个方法,何谈重写?
- 用virtual修饰的方法就是虚方法,虚方法可以在子类中通过override来进行方法的重写。常见的虚方法就是 ToString()
使用基类的引用
排生类的实例由基类的实例加上派生类自己的成员组成,派生类的引用指向整个类对象,同样包括基类部分。
如果有一个派生类对象的引用,就可以获取该对象基类部分的引用,使用类型转换运算把该引用转换成基类引用。
转换成基类引用后,不能够看到派生类中自己的的引用部分,因为它是站在基类角度的,能看到的是这个基类所具有的对象的引用
上面的代码中,在主程序中添加一句:SomeClass so = (SomeClass)oc; 那么so中就没有PrintField()方法,但是具有其它两个成员
虚方法和重写方法
- 能够重写 方法有:父类中被标记为virtual的方法,以及抽象方法,重写方式就是添加override关键字,函数签名和返回值不变
- 当使用对象基类部分的引用调用一个覆写方法的时候,方法的调用沿被派生层次向上回溯,一直到被标记为override的方法的最新派生版本位置。如果在更高的派生级别有该方法的其它声明,但是没有被标记为override,比如是用new关键字来修饰,那么他们不会被调用
- 上面的举例:有三个类A/B/C,关系为C继承B,B继承A。A中有一个用virtual标记的虚方法M,在B中通过override来重写了这个M方法。C中分别使用用override来重写M方法以及用new关键字来隐藏该方法。最后声明一个C的实例成员c1,将其转换成A类型,即:A a = (A)c1; 通过实例对象a来调用M方法【a.M()】,结果为:C类中通过override重写M方法的时候,执行的是C类中重写的方法;而通过 new 关键字来隐藏M方法时,执行的确实B类中重写后的M方法。
- 如果一个子类override了父类的virtual方法,但是又不想子类调用这个方法,就可以把这个方法标记为sealed
抽象类抽象方法
- 如果类中有一个方法加上了关键字abstract来修饰,那么这个方法叫做抽象方法,同时这个方法所在的这个类也必须要用abstract来修饰,那么这个类就变成了抽象类。而且抽象方法没有方法体
- 一个类如果有至少一个抽象成员,那么类必须是abstract的。
- 抽象方法只是能够存在于抽象类中
- 如果父类中的某一个方法在基类中不会被调用,就可以将这个方法设置为抽象方法。
- 尽管抽象方法必须在派生类中用相应的方法重写,但不能够把virtual修饰符加到abstract修饰符上面
- 抽象类不能够被实例化,只能够被用作其他类的基类。抽象类的设计就是被用来继承的
- 抽象类中可以包含抽象成员,但是不是必须的。抽象类成员可以是抽象成员和普通待实现的成员的组合
- 抽象类本身就可以派生自另外的一个抽象类。
- 任何派生自抽象类的类必须使用override关键字来实现该类所有的抽象成员,除非派生类自己也是一个抽象类
- 抽象方法同样需要用public关键字,如果没有,同样默认的是private
重载overload和重写override的区别
- 重载是方法的名称相同,方法的参数类型或者参数的个数不同,进行多次的重载以适应不同的需要,是一个面向过程的概念。
- 重写,是子类对基类中的方法进行一个重写,方法的签名是一样的,是一个面向对象的概念。
抽象方法(abstract)和虚方法(virtual)的区别
- 虚方法用的是virtual修饰,抽象方法用的是abstract来修饰
- virtual方法子类可以override,也可以不override。但是如果子类不是抽象类,那么父类定义的所有abstract成员子类都必须实现
- 父类定义virtual方法的时候必须提供实现。父类定义abstract方法不能提供实现。
- 虚方法除了密闭类以外都可以写,抽象方法只能够在抽象类中
密封类
- 和抽象类相反,抽象类只是能够被继承不能够被实例化,而密封类则是只能够被实例化不能够被继承
- 密封类用sealed修饰符来标注
- 密封类不能够被继承,是基于安全性的考虑
- String类就是一个密闭类,所以是不能够创建一个String类的子类