继承是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。
当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类。基类有时又叫父类、超类,派生类有时又叫子类。
在逻辑上,可以认为,派生类属于基类,例如,在描述图像时,可以用图形Shape作为基类,正方形Square作为派生类,此时有正方形属于图形的从属关系。
需要注意的是,C#不支持多重继承。意思就是一个派生类只能继承一个基类。当然,基类也可以继承自其它的基类。
继承语法如下:
class BaseClass{
//BaseClass基类成员
}
class DerivedClass : BaseClass{
//DerivedClass派生类成员
}
一般会在派生类名后添加冒号加基类名,表示派生类继承于基类。
默认情况下,所有类都可以通过继承得到。但是你可以指定某个类不能作为基类或是指定某个类只能用作基类。例如,指定类A不能作为基类:
public sealed class A {
//A类成员
}
此时,我们使用关键字sealed
将类限定为密封类,sealed
修饰符将阻止其他类继承此类。可以简单理解为,sealed关键字使其他类不能继承于类A。一般称sealed关键字修饰的类为密封类。
当使用关键字abstract修饰某一类,则该类只能作为基类,不能被实例化。例如,指定类B不能被实例化,类C继承于类B:
public abstract class B{
//类B的成员
}
public class C : B{
//类C的成员
}
实际上,abstract
修饰符表明所修饰内容尚未完全实现。abstract
修饰符也可用于函数、属性、索引和事件等,被abstract
限定的类成员或包含在abstract
类中的成员必须由继承此类的子类实现
例如下面的实例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace K1
{
public abstract class Shape //基类Shape表示形状
{
public abstract int Area(); //获取形状面积,成员函数未完全实现
}
public class Square : Shape //派生类Square表示正方形
{
int side;
public Square()
{
side = 0;
}
public Square(int side)
{
this.side = side;
}
public override int Area() //在派生类中成员函数Area实现。
{
return side * side;
}
}
public class myCaller
{
public static void Main(string[] args)
{
string side = Console.ReadLine();
Square s = new Square(Convert.ToInt32(side));
Console.WriteLine("Square Area:" + s.Area());
Console.ReadKey();
}
}
}
需要注意,子类的访问级别不能高于基类,上面的基类Shape与派生类Square均为public访问级别,符合规则。上面实例输入输出如下所示:
>>>10
Square Area:100
代码中,定义的基类Shape以及其中成员函数Area使用abstract修饰,故Shape类必须作为基类,且成员函数Area必须在派生类Square中实现。
abstract类可称为抽象类,抽象类有如下性质:
sealed
修饰,sealed
和abstract
作用意义互相冲突。使用sealed
修饰的类不能被继承,使用abstract
修饰的类只能被继承;在函数或属性声明处使用abstract
修饰符,用来表明该函数或属性不可实现。abst\fract
修饰的函数有如下几个特点:
;
结尾,不需使用{}
;具体的实现过程将会在子类中,使用override
标志;static
或virtual
;virtual method
。 抽象属性表现类似于抽象方法,除了声明和调用语法的不同之外:
static
修饰)属性中不可使用abstract
修饰符;override
重写。上面的实例,在派生类Square中的成员函数Area中,涉及到另外一个关键字override
,override
修饰符用来拓展或修改继承自基类中的方法、属性、索引或事件,其中基类中的方法、属性、索引或事件使用abstract
或virtual
修饰。
其中,override
重写实现的方法声明必须与基类中方法拥有相同的函数签名。
override
不能重写非虚方法或静态方法,抽象方法是一种隐式的虚方法,在基类中的方法必须使用virtual
、abstract
或override
修饰。
使用override
修饰方法声明不能更改继承基类虚方法的访问修饰符,两者需保持一致。
在使用override
修饰的方法时不能够使用修饰符new
、static
或virtual
。使用override
修饰的属性类似。
多态可理解为多种形态,在派生于同一个类的不同子类对象上执行任务操作时,多态性十分的高效,使用的代码最少。只要在继承层次中有一个相同多的类,那么就可使用多态性进行处理。
下面的代码实例可体现多态使用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace K2
{
public abstract class Shape //基类Shape
{
public abstract int Area(); //获取形状面积
}
//派生类圆形Circular类和正方形Square类
public class Circular : Shape
{
int radius; //成员radius表示圆形半径
public Circular(int radius)
{
this.radius = radius;
}
public override int Area() //重写基类成员函数
{
return radius * radius * 3;
}
public void radiusSet(int radius)
{
this.radius = radius;
}
}
public class Square : Shape
{
int side; //成员side表示正方形边长
public Square(int side)
{
this.side = side;
}
public override int Area()
{
return side * side;
}
}
public class myCaller
{
public static void Main(string[] args)
{
Circular c = new Circular(4);
Square s = new Square(4);
Shape shape1 = s; //子类对象可被当做基类对象使用
Shape shape2 = c;
Console.WriteLine("Square Area:" + shape1.Area()); //多个子类便可提供多种Area实现
Console.WriteLine("Circular Area:" + shape2.Area());
}
}
}
多态有多种应用场景:
在运行时,子类对象可被当做基类对象使用,例如当对象用于方法参数、集合或数组的时候,该对象的声明类型不再与运行时类型相同。例如上面的实例中,Shape类对象接受Circular类对象、Square类对象无需强制类型转换。此时对象shape2不能使用Circular类中的成员函数RadiusSet,如果想要调用,可以使用强制类型转换将Shape类对象转换回Circular类对象。
Circular c2=(Circular)shape2;
c2.RadiusSet(10);
使用继承的时候,子类可使用override
重写基类中定义和实现的虚方法,多个子类便可提供多种实现。在运行时,当客户端代码调用该方法的时,CLR查询对象的运行时类型,会调用重写的虚方法。所以,源码中可通过调用基类中的方法,具体执行的则是子类中已重写的方法。
例如上面的实例,子类便使用override重写了基类中定义的Area方法,此后Shape类对象分别接受s、c,调用Area方法时,Shape1使用的是Square类中实现的方法,Shape2使用的是Circular类实现的方法。