C#中类的继承和多态
C#中类的继承
继承是面向对象程序设计中最重要的概念之一。
继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。
已有的类被称为的基类,这个新的类被称为派生类
class <派生类> : <基类> { ... }
在类定义前面放置关键字sealed,可以将类声明为密封类,该类将不能被继承
C#不支持多重继承,但是,可以使用接口来实现多重继承
现在有一个MyClass类和IMyInterFace接口,那么派生类可以直接继承 class DiyClass : MycLASS,iMyInterFace{} 用逗号隔开实现类似多重继承
基类的初始化
派生类继承了基类的成员变量和成员方法。因此父类对象应在子类对象创建之前被创建。
可以在成员初始化列表中进行父类的初始化。
class Mycalss1{ public Mycalss1(double l, double w){ length = l; width = w; } } class Mycalss2{ public Mycalss2(double l, double w) : base(l, w) { } } 使用base表示基类,base(l, w)表示隐式的创建基类实例对象
C#中类的多态
多态是同一个行为具有多个不同表现形式或形态的能力。
在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。
多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。
在C#中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自Object
多态就是同一个接口,使用不同的实例而执行不同操作
静态多态性
在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。
C#提供了两种技术来实现静态多态性。分别为:函数重载和运算符重载
函数重载
可以在同一个范围内对相同的函数名有多个定义。
函数的定义必须彼此不同,可以是参数列表中的参数类型不同,也可以是参数个数不同。
不能重载只有返回类型不同的函数声明。
下面的实例演示了两个相同的函数 Add(),用于对不同个数参数进行相加处理:
using System; namespace PolymorphismApplication{ public class TestData{ public int Add(int a, int b, int c){ return a + b + c; } public int Add(int a, int b){ return a + b; } } class Program{ static void Main(string[] args){ TestData dataClass = new TestData(); Console.WriteLine("add1 :" + dataClass.Add(1, 2)); Console.WriteLine("add2 :" + Add(1, 2, 3)); Console.ReadKey(); } } }
运算符重载
重定义或重载C#中内置的运算符。
重载运算符是具有特殊名称的函数,是通过关键字operator后跟运算符的符号来定义的。
与其他函数一样,重载运算符有返回类型和参数列表
+、-、!、~、++、--:这些一元运算符只有一个操作数,且可以被重载 +、-、*、/、%:这些二元运算符带有两个操作数,且可以被重载 ==、!=、<、>、<=、>=:这些比较运算符可以被重载 &&、||:这些条件逻辑运算符不能被直接重载 +=、-=、*=、/=、%=:这些赋值运算符不能被重载 =、.、?:、->、new、is、sizeof、typeof:这些运算符不能被重载
using System; namespace OperatorOvlApplication{ class Box { private double length; // 长度 private double breadth; // 宽度 private double height; // 高度 public double getVolume(){ return length * breadth * height; } public void setLength( double len ){ length = len; } public void setBreadth( double bre ){ breadth = bre; } public void setHeight( double hei ){ height = hei; } // 重载 + 运算符来把两个 Box 对象相加 public static Box operator+ (Box b, Box c){ Box box = new Box(); box.length = b.length + c.length; box.breadth = b.breadth + c.breadth; box.height = b.height + c.height; return box; } } class Tester{ static void Main(string[] args){ Box Box1 = new Box(); Box Box2 = new Box(); Box Box3 = new Box(); double volume = 0.0; // Box1 详述 Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // Box2 详述 Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0); Console.WriteLine("Box1 的体积: {0}", Box1.getVolume()); Console.WriteLine("Box2 的体积: {0}", Box2.getVolume()); // 把两个对象相加 Box3 = Box1 + Box2; Console.WriteLine("Box3 的体积: {0}", Box3.getVolume()); Console.ReadKey(); } } }
动态多态性:
C#中的虚方法
当调用一个对象的方法时,系统会直接去检查这个对象申明定义的类,看所调用的方法是否为虚方法;
如果不是虚方法,那么它就直接执行该方法。而如果是一个虚方法,那么这个时候它就不会立刻执行该方法了,而是转去检查对象的实例类。
在这个实例类里,会检查这个实例类的定义中是否有重新实现该虚方法(通过override关键字),如果是有,那么OK,它就不会再找了,而马上执行该实例类中的这个重新实现的方法。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重载了该虚方法的父类为止,然后执行该父类里重载后的方法
虚方法是使用关键字virtual声明的
public virtual int seth(int helght){ //实现代码 } 在派生类中使用关键字override进行修改 public override int seth(int helght){ //修改后的代码 }
虚方法可以在不同的继承类中有不同的实现
对虚方法的调用是在运行时发生的
c#中的抽象类
C#允许使用关键字abstract创建抽象类和抽象方法
抽象类不能被实例化,它们只提供部分实现,抽象类只能通过接口和作为其它类的基类使用
抽象类不能被声明为sealed
抽象方法是一个没有方法体的方法
abstract public int len(); 在派生类中使用关键字override进行实现 public override int len(){ //实现代码 }
一个抽象类可以包含抽象和非抽象方法,当一个类继承于抽象类,那么这个派生类必须实现所有的的基类抽象方法
通过声明派生类也为抽象,我们可以避免所有或特定的虚方法的实现,这就是抽象类的部分实现
一个抽象类能够继承另一个非抽象类,继承了基类的方法,添加新的抽象和非抽象方法
C#中的接口
接口本身并不实现任何功能,它只是和声明实现该接口的对象订立一个必须实现哪些行为的契约。
接口只包含了成员的声明,成员的定义是派生类的责任
接口使得实现接口的类或结构在形式上保持一致
抽象类在某种程度上与接口类似,但是,抽象类大多只是用在当只有少数方法由基类声明由派生类实现时
使用interface关键字声明接口,接口声明默认是public的
interface IMyInterface{ int Myfunc(int x , int y); } class MyClass:IMyInterFace{ public int Myfunc(int x , int y){ return x+y; } } 以上代码定义了接口IMyInterface 通常接口命令以I字母开头 类MyClass继承了接口IMyInterFace并实现了Myfunc方法
接口的注意事项:
1、接口不能包含属性 2、接口中的方法不能包含方法体 3、接口中的方法声明时不能使用访问修饰符 4、继承接口的类(包括抽象类)必须将接口中的方法全部实现 5、接口可以继承接口,派生的接口无需实现方法