老田: 所谓虚成员就是指在基类中申明了方法、属性、索引器或事件,也做一个最简单的处理,但是无法预料在派生类中该方法是否需要被改写。那使用virtual关键字将成员申明为虚成员。这就使它们可以在派生类中被重写。例如,此方法可被任何继承它的类重写。
小天:可以来个比较实际,好用的实例不?你上面的那个汽车类(4.9.1的第二个示例),我想了很久才想到用一个汽车经销商卖车情况来做示例。
老田:好嘛,前面不是写了一个计算面积的类吗?不过那个类太简单了,下面我们做一个可以计算多种形状面积的类库。类关系图如图4-29
图4-29
根据上面的实例,尝试自己先写写,然后在对比下面的代码:
//----------------------基类---------------------------- public class Dimensions { public const double PI = Math.PI; protected double x, y; public Dimensions() {//一个无参数的构造函数 //如果没有这个构造函数,CRL是否会自动添加一个空的呢? } public Dimensions(double x, double y) {//构造函数 this.x = x; this.y = y; }
public virtual double Area() {//计算面积,虚方法,等待被重写 return x * y; } } //---------------------类Circle-继承Dimensions------------------- public class Circle : Dimensions {//圆 public Circle(double r) : base(r, 0) { }
public override double Area() { return PI * x * x; } } //---------------------类Sphere-继承Dimensions------------------- class Sphere : Dimensions {//球体 public Sphere(double r) : base(r, 0) { }
public override double Area() { return 4 * PI * x * x; } } //---------------------类Cylinder-继承Dimensions------------------- class Cylinder : Dimensions {//圆柱体 public Cylinder(double r, double h) : base(r, h) { }
public override double Area() { return 2 * PI * x * x + 2 * PI * x * y; } } |
根据上面那个计算面积的实例稍微改变来实现使用上面这个类,需要注意的是,这个实例中的输入文本框使用了是maskedTextBox控件(怎么用就不说了,自己看控件的mask属性),界面如图4-30
图4-30
在“计算”按钮的事件中代码如下:
private void button1_Click(object sender, EventArgs e) { //将界面上两个TextBox中的字符接收过来并转换为double类型 double x = double.Parse(mtb_x.Text); double y = double.Parse(mtb_y.Text); //注意,这里是一次申明,但是在分别用不同的类来实例化而实现调用不同类中重写的方法 Dimensions ds; //注意这里申明的是一个基类型的变量 switch (cb_type.Text) { case "X方形": ds = new Dimensions(x, y); //用基类型实例化变量 //下面调用的方法是Dimensions类中的Area方法 lbl_result.Text = ds.Area().ToString(); break; case "圆": ds = new Circle(x); //用Dimensions的派生类Circle来实例化 //下面调用的方法是Circle类中的Area方法 lbl_result.Text = ds.Area().ToString(); break; case "球体": ds = new Sphere(x); //用Dimensions的派生类Sphere来实例化 //下面调用的方法是Sphere类中的Area方法 lbl_result.Text = ds.Area().ToString(); break; case "圆柱": ds = new Cylinder(x, y); //用Dimensions的派生类Cylinder来实例化 //下面调用的方法是Dimensions类中的Area方法 lbl_result.Text = ds.Area().ToString(); break; default: MessageBox.Show("请选择计算类型"); cb_type.Focus();//将焦点设置到选择类型的下拉框上去 break; } } |
注意读下上面的代码中,实际上类对象我们只是用基类Dimensions申明了一个。而最终调用方法也是完全一样的。剩下不一样的只在于对于不同的计算类型使用了不同的类来实现。于是乎就实现了不同类型的计算方式采用不同的计算方法(换句话说是调用了不同类中的Area方法)。其实C#在调用虚方法时,将为重写成员检查该对象的运行时类型。将调用大部分派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员。
默认情况下,方法是非虚拟的。不能重写非虚方法。 virtual 修饰符不能与 static、abstract、private 或 override 修饰符一起使用。
除了声明和调用语法不同外,虚拟属性的行为与抽象方法一样。
- 在静态属性上使用 virtual 修饰符是错误的。
- 通过包括使用 override 修饰符的属性声明,可在派生类中重写虚拟继承属性。
另外,上面这段代码中有一处注释是错误的,找出来。
本文章为天轰穿原创作品,转载请注明出处及作者。