博客园  :: 首页  :: 新随笔  :: 订阅 订阅  :: 管理

老田: 所谓虚成员就是指在基类中申明了方法、属性、索引器或事件,也做一个最简单的处理,但是无法预料在派生类中该方法是否需要被改写。那使用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 修饰符的属性声明,可在派生类中重写虚拟继承属性。

 

另外,上面这段代码中有一处注释是错误的,找出来。

 

本文章为天轰穿原创作品,转载请注明出处及作者。