1、C#网络应用编程基础,主讲教师:李璟 联系电话:13806419626 E-mail: ,第三讲 C#程序设计基础(二),理解和掌握C#面向对象基本编程方法。 掌握继承的编程方法。 掌握虚方法多态性编程。 重点和难点:C#面向对象基本编程方法、继承编程方法、虚方法多态性编程。,补充:C#数组的声明与使用,数组用于存储一组同类型的数据。 数组是按照数组名、元素类型和维数来描述的。一维数组在编程中经常用到,下面仅介绍C#中一维数组的声明与使用。 (1)一维数组的声明 语法为:数据类型 数组名; 如:int myArray; 注意:区分C语言的数组定义语法为: 数据类型 数组名元素个数; 如:int
2、 myArray10;,(2)一维数组的初始化,C#语言中数组在访问之前必须先初始化。不能像下面这样访问数组或给数组赋值: int myIntArray; /只声明但没有初始化。 myIntArray10=5;/未初始化,不能访问数组元素 数组的初始化有三种方式: 以字面形式指定数组的完整内容。如:string strArray1=“C“, “C+“, “C#“;Int intArr2=5,6,7; 指定数组的大小,并使用关键字new初始化所有的数组元素,会自动给所有元素赋予同一个默认值。如: string strArray2=new string3;int intArr1=new int4;
3、默认值规则如下: 数值型:如int、double等,初始化为0。 bool类型:初始化为false. 引用类型:初始化为null。,一维数组的初始化(续),还可以使用以上两种方式的组合,使用该方式,数组大小必须与元素个数相匹配。如:int intArr1=new int40,1,2,3;string mystring=new string3 “first”,”second”,”third”;,(3)一维数组的使用,数组元素的下标是从0开始索引的。如,访问共有32个元素数组integers中的单个元素时, integers0=23; /为第1个元素赋值 integers31=67; /为第32个
4、元素赋值 integersi=90; /为第i+1个元素赋值 在C#中,由Array类提供创建、操作、搜索和排序数组的方法,因而在公共语言运行库中用作所有数组的基类。 Array类包括许多方法和属性用于数组的操作,其中经常使用的一个重要属性是数组的长度Length。调用形式:数组名.Length。如: int Integers=2,6,-1,10,12; int ArrayLength=Integers.Length;,使用数组举例,static void Main(string args) string friendNames=“Robert“, “Mike“, “Bob“;int i;Co
5、nsole.WriteLine(“Here are 0 of my friends:“,friendNames.Length);for (i=0;ifriendNames.Length;i+) Console.WriteLine(friendNamesi); Console.ReadLine(); 运行结果: Here are 3 of my friends. Robert Mike Bob,3 C#程序设计基础(二),3.1C#类和对象的概念、定义和使用 3.2C#面向对象高级编程,3.1C#类和对象的概念、定义和使用,3.1.1类的声明 3.1.2类成员 3.1.3访问修饰符 3.1.4字
6、段 3.1.5构造函数 3.1.6方法 3.1.7属性 3.1.8类和对象定义和使用举例,3.1.1 类的声明,用class定义类,声明类的简单形式为: class 类名 /类体字段声明构造函数属性方法 ,类声明举例 /下例声明了两个类,一个是Child类,另一个是用于数据处理的Program类。 class Child private string name; /字段private string sex; /字段/ 构造函数public Child(string name, string sex) this.name = name;this.sex=sex;/方法public void Pr
7、intChild() Console.WriteLine(“0, 1.“, name,sex); ,class Programstatic void Main(string args)Child child1=new Child(“亮亮“,“男“);child1.PrintChild();Console.ReadLine(); 运行结果: 亮亮,男.,3.1.2 类成员,在类中定义的数据和函数统称为类的成员。类的成员包括数据成员和函数成员。 数据成员:是指在类中定义的数据,包括常量和字段。 函数成员:是指在类中定义的用于操作类中数据的函数,主要包括构造函数、方法、属性。,类的常用成员描述,3.
8、1.2类成员,类成员按是否为类所有对象共有,又分为静态成员和实例成员。 静态成员:属于类,为类所有对象所共有,在内存中只有一份,要等到应用程序结束时才会退出内存。定义时,有static关键字。如:public static int j;/静态字段 public static int GetNextSerialNo/静态方法 实例成员:属于类的实例,是只有创建了类的实例才能够使用的成员。定义时,没有static关键字,如:public int j; /实例字段public int GetSerialNo() /实例方法,3.1.3 访问修饰符,类是封装了数据及施加于数据上的操作的封装体,其封装性
9、体现在外部类对本类成员的访问权限是受到限制的,这种限制通过本类成员的访问修饰符来实现。 C#语言中主要有以下几种访问修饰符。,类成员访问修饰符举例 class Childpublic int age; /公有字段private string name; /私有字段private string sex; /私有字段/ 构造函数public Child(string name, int age,string sex)this.name = name;this.age = age;this.sex=sex;/公有方法public void PrintChild() Console.WriteLine
10、(“0, 1 ,2岁.“, name,sex,age);,class Programstatic void Main(string args)Child child1=new Child(“亮亮“,1,“男“);child1.PrintChild();child1.age=2;/age是公有字段,可以在外部访问child1.PrintChild();Console.ReadLine(); 运行结果: 亮亮,男,1岁 亮亮,男,2岁,3.1.4 字段,字段:指类一级定义的变量。分静态字段和实例字段。 字段的完整引用形式为: 引用静态字段,形式为:类名.字段名 引用实例字段,形式为:实例名.字段名
11、 注意:区分静态字段和局部变量。 局部变量:指在构造函数、方法内定义的变量,局部变量的引用形式为:局部变量名 若字段名与局部变量不重名,则可以直接引用字段名。,字段与局部变量举例,using System; namespace ConsoleAppVariable class Program public static int j=20; /静态字段public static int k=50; /静态字段public int i; /实例字段public static void Main() int j=30; /局部变量int i=10; /局部变量Console.WriteLine(“局
12、部变量:i=0,j=1“,i,j); /局部变量:i=10,j=30Program pro=new Program();pro.i=5;Console.WriteLine(“字段:i=0,j=1,k=2“,pro.i,Program.j,k); /字段:i=5,j=20,k=50Console.ReadLine(); 运行结果: 局部变量:i=10,j=30 字段:i=5,j=20,k=50,3.1.5 构造函数,构造函数是一个特殊的方法,用于在建立对象时进行对对象的字段进行初始化操作。在C#中,每当创建一个对象时,都会先自动调用类中定义的构造函数。创建对象的形式如下:类名 对象名=new 构
13、造函数(参数列表)如:Child child1=new Child(“亮亮”,1,”男”)使用构造函数的好处是它能够确保每一个对象在被使用之前都适当地进行了初始化。在C#中,没有析构函数,该对象所占的内存将被CLR在适当时间自动回收(垃圾回收机制)。,构造函数基本特点,构造函数具有以下基本特点: 1) 每个类至少有一个构造函数。若程序代码中没有构造函数则系统会自动提供一个默认的构造函数。 2) 一个构造函数必须和它的类名相同。3) 构造函数不包含任何返回值。,构造函数默认构造函数,1. 默认构造函数 (1)如果在类中不定义构造函数,系统会提供一个默认的构造函数。 (2)默认构造函数没有参数。
14、(3)默认构造函数自动将实例字段初始化为:数值型:如int、double等,初始化为0。bool类型:初始化为false.引用类型:初始化为null。 (4)如果自己定义了构造函数,则不会自动进行初始化。,构造函数重载构造函数,2. 重载构造函数实际情况中,构造函数中需要传入的参数的数目和类型不同。解决这个问题最好的办法就是:重载(Overloading)构造函数。方法重载是指允许在同一类中采用同一名称不同签名声明多个方法。当编译一个重载方法的调用时,编译器会自动找到最佳匹配自变量的方法,否则报错。注:方法的签名由它的名称、参数的数目、每个参数的类型组成。返回值类型不是方法签名的组成部分。,构
15、造函数举例,class Programpublic Program() Console.WriteLine(“null“); public Program(string str) Console.WriteLine(“string str=0“,str); public Program(int x) Console.WriteLine(“int x=0 “,x); static void Main() Program aa = new Program();Program bb = new Program(“How are you!“);Program cc = new Program(10);
16、Console.ReadLine(); 运行结果: null How are you! int x=10,3.1.6 方法,方法(Method)是一种用于操作对象或类的数据成员完成某种功能的函数成员。 1. 方法的定义与使用 (1)方法必须放在某个类中。 (2)定义方法的语法形式为:访问修饰符 返回值类型 方法名(参数列表)语句序列 ,方法可以有参数,也可以没有参数,但是小括号都是必需的。若参数有多个,用逗号分开。 无返回值时,必须声明方法的返回值类型为void。 如果声明一个非void类型的方法,则方法中必须至少有一个return语句并返回一个值。,定义方法的注意事项,类方法举例: clas
17、s Program public int IntMethod() Console.WriteLine(“this is IntMethod.“);int i = 10;return i ;public void VoidMethod() Console.WriteLine(“this is VoidMethod.“); static void Main() Program method = new Program();int j = 5;method.VoidMethod(); Console.WriteLine(“j=0.“, j);j = method.IntMethod(); Conso
18、le.WriteLine(“j=0.“, j);Console.ReadLine(); 运行结果: this is VoidMethod. j=5 this is IntMethod. j=10,方法-参数传递,2. 方法中的参数传递 1)值参数2)引用参数3)输出参数4)参数数组前三种用于参数个数确定的情况。第四种用于参数类型相同、参数个数不确定的情况,用一个参数数组传递多个同类型的参数。,方法-参数传递,值参数 格式: 参数类型 参数名值参数用于输入参数的传递。值参数相当于一个局部变量,它的初始值是从为该参数所传递的自变量或常量获得的。对值参数的修改不会影响所传递的自变量。,值参数举例,c
19、lass Program public static void AddOne(int a) a+; static void Main() int x = 3;Console.WriteLine(“调用AddOne之前,x=0“, x);/x=3AddOne(x);Console.WriteLine(“调用AddOne之后,x=0“, x);/x=3 Console.ReadLine(); 运行结果: 调用AddOne之前, x=3 调用AddOne之后, x=3,方法-参数传递,2) 引用参数格式为: ref 参数类型 参数名用于输入和输出参数的传递。用于引用参数的自变量必须是一个变量,并且在
20、方法执行期间,引用参数和自变量所表示的是同一个存储位置。因此该引用参数既是输入参数又是输出参数。,参数传递举例,class Program public static void AddOne(ref int a) a+; static void Main() int x = 3;Console.WriteLine(“调用AddOne之前,x=0“, x);AddOne(ref x);Console.WriteLine(“调用AddOne之后,x=0“, x);Console.ReadLine(); 运行结果: x=3 x=4,方法参数传递,3) 输出参数 格式为: out 参数类型 参数名用于
21、输出参数的传递。输出参数类似于引用参数,不同之处在于调用方提供的自变量并不作为输入参数,其初始值无关紧要。,输出参数举例,class Program public static void Divide(int x,int y,out int result, out int remainder) result = x/y;remainder = x%y;static void Main() int res, rem;Divide (10,3,out res, out rem);Console.WriteLine(“调用Divide之后,res=0,rem=1“,res, rem);Console
22、.ReadLine(); 运行结果: 调用Divide之后,res=3,rem=1,方法-参数传递,4) 参数数组(传递个数不确定的同类型参数) 格式为: params 参数数组类型 数组名参数数组允许将可变长的自变量列表传递给方法。注意:采用params关键字声明参数的个数是不确定的。,参数数组举例,class Program public static float Average(params long data) long total=0, i;for (i = 0; i data.Length; +i)total += datai;return (float)total / data.
23、Length;static void Main() float x = Average(1, 2, 3, 5);Console.WriteLine(“1+2+3+5的平均值为0“, x);x = Average(4, 5, 6, 7, 8);Console.WriteLine(“4+5+6+7+8的平均值为0“, x);Console.ReadLine(); 运行结果: 1+2+3+5的平均值为2.75 4+5+6+7+8的平均值为6,方法-静态方法与实例方法,静态方法 若一个方法声明中含有static修饰符,则称该方法为静态方法。 静态方法不对实例成员进行操作,只能访问静态成员。 静态方法调
24、用形式:类名.静态方法名 实例方法 若一个方法声名中没有static修饰符,则称为该方法为实例方法。 实例方法既能访问静态成员,也能访问实例成员。 实例方法调用形式:实例名.实例方法名 静态方法的好处在于不用创建类实例仅通过类就能调用。,例:设计一个Entity类(实体类),设置一个静态字段保存下一个序列号(初始值为1000),设置一个实例字段保存实例当前的序列号,每个类对象在创建时自动获取一个唯一序列号并使下一个序列号自动加1。 分析: 设计两个字段: 静态字段nextSerialNo ,保存下个序列号。 实例字段serialNo,保存当前实例序列号。 设计以下方法: 构造方法Entity(
25、 ),初始化当前实例的序列号。 实例方法GetSerialNo( ),获取当前实例序列号。 静态方法SetNextSerialNo(int value),设置下一个序列号初值。 静态方法GetNextSerialNo( ),获取下一个序列号,静态方法和实例方法举例,class Entity static int nextSerialNo; /静态字段,保存下个序列号int serialNo; /实例字段,保存当前实例序列号public Entity( ) /构造函数 serialNo=nextSerialNo+; public int GetSerialNo( ) /实例方法 return s
26、erialNo; /获取当前实例序列号 public static int GetNextSerialNo( ) /静态方法 return nextserialNo; /获取下个序列号 public static void SetNextSerialNo(int value) /静态方法 nextserialNo=value; /设置下个序列号初值 ,静态方法和实例方法举例,static void Main() Entity.SetNextSerialNo(1000);Entity e1=new Entity();Entity e2=new Entity(); Console.WriteLin
27、e(e1.GetSerialNo();Console.WriteLine(e2.GetSerialNo();Console.WriteLine(Entity.GetNextSerialNo(); 运行结果: 1000 1001 1002,静态方法和实例方法举例,方法-方法重载,4. 方法重载有时会遇到完成相似的功能,但传入的参数列表不同的情况,C#使用方法重载来实现统一操作。 方法重载是指具有相同的方法名,但签名不同的多个方法可以同时出现在一个类中。当编译一个重载方法的调用时,编译器会自动找到最佳匹配自变量的方法,否则报错。,方法重载举例,class Program public static
28、 int Add(int i, int j) return i + j; public static string Add(string s1, string s2) return s1 + s2; public static long Add(long x) return x + 5; static void Main() Console.WriteLine(Add(1, 2);Console.WriteLine(Add(“1“, “2“);Console.WriteLine(Add(10);Console.ReadLine(); 运行结果: 3 12 15,3.1.7 属性,一个设计良好的
29、类不仅仅要将类的实现部分隐藏起来,还会限制外部对类中非公有字段的存取权限。在C#中,可以通过属性来实现。,1.属性的声明,属性的声明类似字段,不同之处在于属性的声明以定界符之间的get访问器和/或set访问器结束,而不是分号。 get访问器和set访问器不是必须同时有的,因需而定。 get访问器相当于一个具有属性类型返回值的无参数方法,用来有限制地读取类中非公有字段的值。 set访问器相当于一个具有单个名为value的参数和无返回类型的方法,用来有限制地设置类中非公有字段的值。,属性举例,class MyClass private int number = 0;public int Numbe
30、rgetreturn number;set if (value 0) /value是关键字,其值是调用set访问器时传入的新值。 number = value; ,属性举例,class Program public static void Main()MyClass me = new MyClass();Console.WriteLine(me.Number);/调用get访问器me.Number = 5;/调用set访问器,value=5Console.WriteLine(me.Number); /调用get访问器Console.ReadLine(); 运行结果: 0 5,2. 属性与方法的
31、区别,1) 属性不使用括号,但方法一定要使用括号。 2) 属性不能指定参数,方法可以指定参数。 3) 属性不能使用void类型,方法则可以使用void类型。,3.1.8 类和对象定义和使用举例,设计一个公司雇员类(employee),需要存储这些雇员的姓名name、编号(编号初始值缺省为1000),并设计以下功能: 为姓名name定义属性,设置其get访问器和set访问器。 实现无参构造方法,完成从键盘上输入雇员姓名的功能。 实现方法,完成从键盘上重新设置编号初始值的功能(setEmployeeNo)。 实现方法显示雇员编号和姓名(displayStatus)。,class employee
32、private string name; /姓名private int individualEmpNo; /个人编号private static int employeeNo=1000; /雇员编号目前最大值public string Name getreturn name; set if(value!=“)name=value; ,public employee() /构造方法 Console.Write(“请输入一个雇员的姓名:“);name=Console.ReadLine();individualEmpNo=employeeNo+;static public void setEmplo
33、yeeNo() Console.Write(“请输入雇员编号目前的初始值:“);employeeNo=Convert.ToInt32(Console.ReadLine(); public void displayStatus() /显示人员信息 Console.WriteLine(“编号:”+individualEmpNo+“,姓名:“+name); ,class Program static void Main(string args) employee.setEmployeeNo(); employee employee1=new employee();employee1.displayS
34、tatus();employee employee2=new employee();employee2.displayStatus();Console.Write(“请重新输入最后一个雇员的姓名:“);employee2.Name=Console.ReadLine();employee2.displayStatus();Console.ReadLine(); 运行结果: 请输入雇员编号目前的初始值:1000 请输入一个雇员的姓名:王丽 编号:1000,姓名:王丽 请输入一个雇员的姓名:刘强 编号:1001,姓名:刘强 请重新输入最后一个雇员的姓名:张刚 编号:1001,姓名:张刚,3.2 面向
35、对象的高级编程,3.2.1封装 3.2.2继承 3.2.3多态性,3.2.1 封装,在面向对象编程中,封装是指把数据和处理这些数据的代码封装在一个类中,尽可能隐藏实现的细节,只提供给调用者需要知道的操作和数据。 封装使得外部类并不能直接访问类内部数据,只有类内部操作才能访问类内部数据,所以设计者修改实现细节时,只要不改变对外接口,可以不影响调用者与类的交互方式。,类封装举例:设计一个银行账户类,通过属性读取账户余额但不可任意修改账户余额,通过构造方法初始化账户余额,并可以通过方法扣减账户余额,在控制台应用程序中测试该类的功能。,分析:根据题意设计一个名为BankAccount的类,该类有: 一
36、个私有字段accountBalance保存账户余额。 一个名为AccountBalance的属性,只提供get访问器不提供set访问器。 一个名为BankAccount的构造方法,可初始化帐户余额accountBalance。 一个名为Withdraw的公有方法,可以扣减帐户余额accountBalance的值。,class BankAccount private decimal accountBanlance;public decimal AccountBanlance getreturn accountBanlance;public BankAccount(decimal startAmo
37、unt) accountBanlance=startAmount; public void Withdraw(decimal money) if(accountBanlance=money)accountBanlance-=money;Console.WriteLine(“扣掉0:C“,money); ,class Program static void Main()BankAccount ZhangSan=new BankAccount(1000);Console.WriteLine(“账户的余额为0:C“,ZhangSan.AccountBanlance);ZhangSan.Withdra
38、w(100);Console.WriteLine(“账户的余额为0:C“,ZhangSan.AccountBanlance);Console.ReadLine(); 运行结果: 帐户的余额为¥1,000.00 扣掉¥100.00 帐户的余额为¥900.00,若上题中增加一个修改:若一次扣款超过500元,就提示“每次扣款额不能超过500元!”,则仅需修改类内方法Withdraw方法,修改如下: public void Withdraw(decimal money) if(accountBanlance=money)if(money=500.00)Console.WriteLine(“每次扣款额不
39、能超过500元! “);return; else accountBanlance-=money;Console.WriteLine(“扣掉0:C“,money); ,3.2.2 继承,继承的概念来自于遗传与分类, 遗传:每个人的特征或多或少都来自于父母的遗传,但每个人都不可能完全与父母一模一样,有着自己与众不同的特征。 分类:现实世界中的事物都是相互联系的,人们在认识过程中,根据他们的实际特征,抓住其共同特征和细小差别,利用分类的方法进行分析和描述他们。,3.2.2 继承,3.2.2.1 继承的概念 3.2.2.2 C#派生类的声明 3.2.2.3 C#派生类中的构造方法,3.2.2.1继承的
40、概念,在现实世界中,许多实体或概念不是孤立的,它们具有共同的特征,但也有细微的差别,人们使用层次分类的方法来描述这些实体或概念之间的相似点和不同点。 继承是面向对象程序设计中最重要的机制,它支持层次分类的观点。 继承使得程序员可以在一个较一般的类的基础上很快地建立一个新类,而不必从零开始设计每个类。,基类和派生类的关系,在OOP中,继承使得派生类具有基类的特征,同时又允许在派生类的声明时增加自己的特征,或修改基类的特征。,3.2.2.2 C#派生类的声明,声明一个C#派生类的一般格式为: class 派生类名: 基类名 /派生类新增的数据成员和成员函数 ; 其中,“基类名”是一个已经定义的类的
41、名称,“派生类名”是继承原有类的特性而生成的新类的名称。,3.2.2.3 C#派生类中的构造方法,基类的构造函数不能被继承,派生类必须自身的构造函数。 在派生类中对新增成员的初始化工作,是由派生类的构造函数完成的。 在派生类中对基类成员的初始化工作,还是由基类的构造函数完成。 我们必须在派生类的构造函数中对基类的构造函数所需要的参数进行设置。 通常情况下,当创建派生类对象时,首先执行基类的构造函数,随后再执行派生类的构造函数。,派生类构造函数的一般格式,派生类名(参数总表):base(参数表) / 派生类新增成员的初始化语句 默认情况下,派生类的所有构造函数会自动调用基类的无参构造函数。 当派
42、生类的有参构造函数需要调用基类的有参构造函数时,需要在定义派生类构造函数时,缀上基类名base及其有参构造函数的初始化列表。如: public Car(int w, float g, int p, string id):base(w,g,p,id),例 派生类构造函数默认情况下自动调用基类的无参构造函数,class Animal protected string _name;public Animal() Console.WriteLine(“你好,我是一只动物!“); public Animal(string name) _name = name;Console.WriteLine(“你好,
43、我是一只动物!我的名字叫“ + _name + “!“);public void Eat() Console.WriteLine(_name + “在吃食!“); class Cat : Animal public Cat() Console.WriteLine(“你好,我是一只猫!“); public Cat(string name) _name = name;Console.WriteLine(“你好,我是一只猫!我的名字叫“ + _name + “!“); ,class Program static void Main() Cat e = new Cat(“小白“);e.Eat();Ca
44、t h = new Cat();Console.ReadLine(); 运行结果: 你好,我是一只动物! 你好,我是一只猫!我的名字叫小白! 小白在吃食! 你好!我是一只动物! 你好!我是一只猫! 说明:该例中Cat的两个构造函数都自动调用了基类Animal的无参构造Animal()。,例 派生类构造函数调用指定的基类有参构造函数,class Animal protected string _name;public Animal() Console.WriteLine(“你好,我是一只动物!“); public Animal(string name) _name = name;Console.
45、WriteLine(“你好,我是一只动物!我的名字叫“ + _name + “!“);public void Eat() Console.WriteLine(_name + “在吃食!“); class Cat : Animal public Cat() Console.WriteLine(“你好,我是一只猫!“); public Cat(string name):base(name) _name = name;Console.WriteLine(“你好,我是一只猫!我的名字叫“ + _name + “!“); ,class Program static void Main() Cat e =
46、 new Cat(“小白“);e.Eat();Cat h = new Cat();Console.ReadLine(); 运行结果: 你好,我是一只动物!我的名字叫小白! 你好,我是一只猫!我的名字叫小白! 小白在吃食! 你好!我是一只动物! 你好!我是一只猫! 说明: 在该例中,Cat(string name)调用了基类Animal中的有参构造函数Animal(string name)。,3.2.3 多态性,C#最重要的特征之一就是代码重用。多数情况下,越通用的代码越能够重用。 多态性是通过提高代码的通用性来实现代码重用的。 直观地讲,多态性是指类族中具有相似功能的不同函数使用同一名称来实现
47、,从而可以使用相同的调用方式来调用这些具有不同功能的同名函数,即通过用相同的接口访问不同功能的函数,来实现“一个接口,多种方法”的通用性。 (注:类族是指由直接或间接派生自共同基类的所有派生类和共同基类组成的类的集合。) 从消息的角度说,多态性是指同样的消息被不同类型的对象接收时导致完全不同的行为。,C#多态性的实现方式,主要有以下几种实现多态性的方式。 第一种方式是通过继承实现多态性,分为两种: 1. 虚方法 2. 隐藏基类的方法 第二种方式是通过抽象类实现多态性。,3.2.3 多态性,3.2.3.1 虚方法 3.2.3.2 隐藏基类的方法 3.2.3.3 抽象类,3.2.3.1 虚方法,在基类中,如果想让某个方法被派生类重写,可以使用修饰符virtual声明该方法为虚方法,形式如下:public virtual 返回值类型 方法名()/程序代码派生类则用override重写,形式如下:public override返回值类型 方法名()/程序代码,虚函数,例如,如果在基类中,如果想让方法myMethod被派生类重写,可以使用修饰符virtual声明:public virtual void myMethod()/程序代码派生类则用override重写:public override void myMethod()/程序代码,