C#程序设计第3章.docx
《C#程序设计第3章.docx》由会员分享,可在线阅读,更多相关《C#程序设计第3章.docx(53页珍藏版)》请在冰豆网上搜索。
C#程序设计第3章
第3章面向对象编程
3.1概述
⏹面向过程的程序设计(POP,Procedure-OrientedProgramming)
◆包含一个主过程和若干个子过程;
◆代码和数据是分离的,程序的可维护性差;
◆面向过程的程序设计语言:
C、Pascal、Fortran、Basic。
⏹面向对象的程序设计(OOP,Object-OrientedProgramming)
◆将数据及处理这些数据的操作都封装(Encapsulation)到一个称为类(Class)的数据结构中。
◆使用这个类时,只需要定义一个类的变量即可,这个变量叫做对象(Object)。
◆封装性(类)、继承性、多态性。
◆优点:
程序可维护性好;程序易于修改;对象可以使用多次,即可重用性好。
3.2类
类:
是面向对象编程的基本构造块,是一种包含数据成员、函数成员、嵌套类型的数据结构。
⏹数据成员:
常量、域(field)、事件
⏹函数成员:
包含可执行代码的成员,包括方法、属性、操作符、索引指示器(indexer)、构造函数、析构函数。
3.2.1类的声明
类的声明的格式:
[类修饰符]class类名[:
基类类名]
{
类的成员;
};
⏹类修饰符:
◆new:
新建类,表明由基类中继承而来的、与基类中同名的成员。
◆public:
公有类,表示外界可以不受限制地访问。
◆protected:
保护类,表示可以访问该类或从该类派生的类型。
◆internal:
内部类,表明仅能访问本程序。
◆private:
私有类,表明只有该类才能访问。
◆abstract:
抽象类,说明该类是一个不完整的类,只有声明而没有具体的实现。
一般只能用做其它类的基类,不能单独使用。
◆sealed:
密封类,说明该类不能作其它类的基类,不能再派生新的类。
以上类修饰符可以两个或多个组合起来使用,但需要注意下面几点:
(1)在一个类声明中,同一类修饰符不能出现多次。
(2)new类修饰符仅允许在嵌套类中表示类声明时使用,表明类中隐藏了由基类中继承而来的、与基类中同名的成员。
(3)在使用public、protected、internal、private这些类修饰符时,要注意这些类修饰符不仅表示所定义类的访问特性,而且还表明类中成员声明时的访问特性,并且它们的可用性也会对派生类造成影响。
(4)抽象类修饰符abstract和密封类修饰符sealed都是受限类修饰符。
◆抽象类只能作其它类的基类,不能建立抽象类的实例,即对抽象类使用new操作符是错误的。
。
◆密封类不能作其它类的基类,可以由其它类继承而来但不能再派生其它类。
◆一个类不能同时使用抽象类修饰符和密封类修饰符。
(5)如果省略类修饰符,则默认为私有修饰符private。
(6)对于具有继承关系的类才有基类。
◆如果一个类没有从任何类继承,就不需要基类类名选项。
◆在C#中,只允许单继承,不允许多继承。
◆如果一个类想继承多个类的特点,可以采用接口的方法实现。
3.2.2类的成员
(1)类的成员:
按照类的成员是否为函数,分为以下类型:
⏹成员变量(不以函数形式出现)
◆常量:
代表与类相关的常量值。
◆变量:
类中的变量,相当于C++中的成员变量。
◆事件:
由类产生的通知,用于说明发生了什么事情。
◆类型:
属于类的局部类型。
⏹成员函数(以函数形式出现)
◆方法:
完成类中各种计算或功能的操作。
◆属性:
定义类的值,并对它们提供读、写操作。
◆索引器:
允许编程人员在访问数组时,通过索引器访问类的多个实例。
又称下标指示器。
◆运算符:
定义类的实例能使用的运算符。
◆构造函数:
在类被实例化时首先执行的函数,主要是完成对象初始化操作。
◆析构函数:
在对象被销毁之前最后执行的函数,主要完成对象结束时的收尾操作。
(2)类的成员:
按照类的成员设定的访问修饰符,分为以下类型:
⏹公有成员:
public,允许类的内部或外界直接访问。
⏹私有成员:
private,外界不能直接访问该成员变量或成员函数。
⏹保护成员:
protected,对于外界该成员是隐藏的,但这个类的派生类可以访问。
⏹内部成员:
internal,这种成员对同一包中的应用程序或库是透明的,但在包之外是禁止访问的。
//ch3_1.cs
usingSystem;
classClass1
{
publicstrings;//公有成员
protectedinti;//保护成员
privatedoubled;//私有成员
publicvoidF1()
{
s="Welcometoyou!
";//正确,允许访问自身成员
i=1000;//正确,允许访问自身成员
d=168.68;//正确,允许访问自身成员
}
}
classClass2:
Class1//从Class1派生类Class2
{
intx;//私有成员
publicvoidF2()
{
x=2000;//正确,允许访问自身成员
s="Hello";//正确,允许访问类Class1的公有成员
d=188.88;//错误,不能访问类Class1的私有成员
i=3000;//正确,允许访问类Class1的保护成员
}
}
classClass3
{
publicvoidF3()
{
Class1c=newClass1();//声明类Class1的实例C
c.s="Welcome";//正确,允许访问类Class1的公有成员
c.d=209.99;//错误,不能访问类Class1的保护成员
c.i=5000;//错误,不能访问类Class1的私有成员
}
}
3.2.3this关键字
this关键字只能在类的构造函数、类的方法和类的实例中使用,它的含义是:
⏹在类的构造函数中出现的this表示对正在构造的对象本身的引用。
⏹在类的方法中出现的this表示对调用该方法的对象的引用
⏹在结构的构造函数中出现的this表示对正在构造的结构的引用
⏹在结构的方法中出现的this表示对该方法的结构的引用。
//ch3_2.cs
usingSystem;
publicclassEmployee
{
publicstringname;//员工姓名
publicdecimalsalary;//员工薪水
//构造函数
publicEmployee(stringname,decimalsalary)
{
//用this关键字给正在构造的对象的name和salary赋值
this.name=name;
this.salary=salary;
}
//显示员工姓名及薪水
publicvoidDiaplayEmployee()
{
Console.WriteLine("姓名:
{0}",name);
Console.WriteLine("薪水:
{0}元",salary);
//用this方法将当前对象传给Tax.CalcTax()方法
Console.WriteLine("个人所得税:
{0}元",Tax.CalcTax(this));
}
}
publicclassTax
{
//CalcTax()方法计算个人所得税
publicstaticdecimalCalcTax(EmployeeE)
{
return(0.14m*(E.salary-800.0m));
}
}
publicclassSample
{
publicstaticvoidMain()
{
//声明类Employee的实例e
Employeee=newEmployee("木林森",3168.0m);
e.DiaplayEmployee();//显示员工姓名和薪水
}
}
程序输出结果:
姓名:
木林森
薪水:
3168元
个人所得税:
331.52元
3.2.4静态成员和非静态成员
类的静态成员:
用static声明,属于类所有,不必产生类的实例就可访问它,为类的所有实例所共享。
类的非静态成员:
属于类的实例所有,每创建一个类的实例,都在内存中为非静态成员开辟了一块区域。
//ch3_3.cs
usingSystem;
classEmployee{
publicstaticdecimalSalary;//静态成员
publicstringName;//非静态成员
publicstaticvoidSetSalary(decimalB)
{
Salary=B;//正确,等价于Employee.Salary=B
}
publicvoidSetName(stringN)
{
Name=N;//正确,等价于this.Name=N
}
}
classSample
{
publicstaticvoidMain()
{
Employee.Salary=6000.0m;//正确,静态成员可以按类名访问
Employee.SetSalary(6000.0m);//正确,静态成员可以按类名访问
Employeee=newEmployee();//建立类Employee的一个实例
e.Name="高越明";//正确,非静态成员必须通过实例访问
e.SetName("高越明");//正确,非静态成员必须通过实例访问
//注意,e.Name不能写成Employee.Name,即非静态成员不能按类名访问
//Employee.Salary也不能写成e.Salary,即静态成员不能通过类的实例访问
e.WriteLine("员工姓名:
{0}\n薪水:
{1}元",e.Name,Employee.Salary);
}
}
程序输出结果:
员工姓名:
高越明
薪水:
6000元
3.2.5成员常量
成员常量:
表示与类相关联的常量值。
//ch3_4.cs
usingSystem;
classCircle{
publicconstdoublePI=3.1415926;//表示圆周率的成员常量PI
publicdoubleRadius;//表示半径
//构造函数
publicCircle(doubleRadius)
{
this.Radius=Radius;
}
//计算圆的面积
publicdoubleArea()
{
return(PI*Radius*Radius);
}
}
classSample
{
publicstaticvoidMain()
{
//声明类Circle的一个实例c
Circlec=newCircle(6.6);
Console.WriteLine("圆的半径是{0}",c.Radius);
Console.WriteLine("圆的面积是{0}",c.Area());
}
}
程序执行结果:
圆的半径是6.6
圆的面积是136.847773656
3.2.6构造函数和析构函数
1、构造函数
⏹构造函数的函数名和类的名称一样。
⏹由于系统在对象实例化的同时自动调用构造函数,因此构造函数适合于为需要赋初始值的变量赋初值。
⏹构造函数可以重载(函数名相同,但参数类型或参数个数不同)。
⏹当某个类没有构造函数时,系统将自动为其创建构造函数,这种构造函数称为默认构造函数。
⏹构造函数的类型修饰符总是public。
如果是private,则表示这个类不能实例化,这通常用于只含有静态成员的类中。
⏹构造函数由于不需要显式调用,因而不用声明返回类型。
⏹构造函数可以带参数也可以不带参数。
//ch3_6.cs
usingSystem;
publicclassEmployee
{
publicstringname;//员工姓名
publicdecimalsalary;//员工薪水
//第一个构造函数不带参数
publicEmployee(){;}
//第二个构造函数可接受员工姓名
publicEmployee(stringname)
{
this.name=name;
}
//第三个构造函数可接受员工姓名和薪水
publicEmployee(stringname,decimalsalary)
{
this.name=name;
this.salary=salary;
}
}
publicclassSample
{
publicstaticvoidMain()
{
//没有传入任何参数,则调用第一个构造函数
Employeee1=newEmployee();
Console.WriteLine("员工姓名={0}",e1.name);
Console.WriteLine("薪水={0}元\n",e1.salary);
//传入一个员工姓名参数,则调用第二个构造函数
Employeee2=newEmployee("木林森");
Console.WriteLine("员工姓名={0}",e2.name);
Console.WriteLine("薪水={0}元\n",e2.salary);
//传入员工姓名和薪水参数,则调用第三个构造函数
Employeee3=newEmployee("木林森",6000);
Console.WriteLine("员工姓名={0}",e3.name);
Console.WriteLine("薪水={0}元",e3.salary);
}
}
程序执行结果:
员工姓名=
薪水=0元
员工姓名=木林森
薪水=0元
员工姓名=木林森
薪水=6000元
2、析构函数
⏹析构函数的名称:
在类名前加一个~符号。
⏹析构函数在对象销毁时运行,常用来处理对象用完后的收尾工作(对象在运行过程中动态申请内存的回收工作)。
⏹析构函数不能带有参数,不能拥有访问修饰符,不能显式地被调用。
⏹析构函数不能被继承,也不能被用户显式调用。
⏹如果类没有析构函数,系统自动调用默认的析构函数,如果有则需要用户来完成内存的回收和其他操作。
3.3方法
在C#中,任何数据和操作都必须封装到类中,不再有全局变量、全局常数和全局方法。
用户对类的操作大部分体现在对方法的使用上。
3.3.1方法的声明
方法是类中完成某一个或几个操作行为的成员。
它的声明格式为:
方法修饰符返回类型方法名(方法形式参数列表)
{
方法实现部分;
};
方法修饰符:
new、public、protected、internal、private、static、virtual、sealed、override、abstract、extern。
⏹static:
表明这个方法只能访问类中的静态成员,没有static的方法可以访问类中任意成员。
⏹virtual:
有virtual的方法称为虚方法,反之,称为非虚方法。
虚方法的执行方式可以被派生类改变,这种改变是通过重载实现。
⏹extern:
外部方法。
⏹返回类型:
合法的C#数据类型。
在方法的实现部分,可以通过return语句返回该数据类型。
⏹形参表:
值参数(不含任何修饰符)、引用参数(以ref修饰符声明)、输出参数(以out修饰符声明)、数组参数(以params修饰符声明)。
3.3.2值参数和引用参数
值参数:
传递参数时,编译器将实参的值做一个副本传给方法。
当在方法中改变了值,所作的变动只影响副本,而不影响实参本身。
引用参数:
传递参数时,编译器将实际值在内存中的地址传给方法。
当在方法中改变了值时,实参的值也将改变。
//ch3_7.cs
usingSystem;
classSample
{
//按值传递参数
staticvoidSwapVal(inta,intb)
{
inttmp=a;
a=b;
b=tmp;
}
//按引用传递参数
staticvoidSwapRef(refinta,refintb){
inttmp=a;
a=b;
b=tmp;
}
staticvoidMain(){
intx=10,y=20;
SwapVal(x,y);//调用SwapVal()方法
Console.WriteLine("按值传递后,x={0},y={1}",x,y);
SwapRef(refx,refy);//调用SwapRef()方法
Console.WriteLine("按引用传递后,x={0},y={1}",x,y);
}
}
程序运行结果:
按值传递后,x=10,y=20
按引用传递后,x=20,y=10
3.3.3输出参数
输出参数:
用于传递方法返回的数据。
与引用参数类似,输出参数也不必开辟新的内存区域。
与引用参数的区别是,调用方法时无需对输出参数的实参进行初始化。
//ch3_8.cs
usingSystem;
classSample
{
//SplitPath()方法用于将路径path分解为文件夹fldr和
//文件名fname。
path是值参数,而fldr和fname是输出参数
staticvoidSplitPath(stringpath,outstringfldr,outstringfname)
{
inti=path.Length;
while(i>0){
charc=path[i-1];
if(c=='\\'||c=='/'||c==':
')break;
i--;
}
fldr=path.Substring(0,i);
fname=path.Substring(i);
}
staticvoidMain(){
stringpath,fldr,fname;
path="c:
\\MyDoc\\AspNet\\temp.cs";
SplitPath(path,outfldr,outfname);
Console.WriteLine("将{0}分解后,得到",path);
Console.WriteLine("文件夹{0}",fldr);
Console.WriteLine("文件名{0}",fname);
}
}
程序运行结果:
将c:
\MyDoc\AspNet\temp.cs分解后,得到
文件夹c:
\MyDoc\AspNet\
文件名temp.cs
3.3.4数组参数
数组参数要用修饰符params。
当形参表中包含数组参数时,该参数必须位于最后面。
数组参数只能是一维的,如string[]和string[][]都可以作为数组参数,而string[,]则不能作为数组参数。
//ch3_9.cs
usingSystem;
classSample
{
//Sort()方法用于对数组进行排序
staticvoidSort(paramsint[]args)
{
inttmp;
for(inti=0;ifor(intj=0;jif(args[i]tmp=args[i];
args[i]=args[j];
args[j]=tmp;
}
}
staticvoidMain(){
int[]a={11,7,9,1,5,3};
Sort(a);
Console.Write("11,7,9,1,5,3的排序结果是:
");
for(inti=0;iConsole.Write("{0}",a[i]);
Console.WriteLine();
}
}
程序执行结果:
11,7,9,1,5,3的排序结果是:
1357911
3.3.5静态方法与非静态方法
⏹静态方法:
使用了static修饰符的方法,它不属于某一个具体的实例,只能访问类中的静态成员。
⏹非静态方法:
没使用static修饰符的方法,它可访问类中的任何成员。
//ch3_10.cs
classSample
{
intx;
staticinty;
//F()是非静态方法
voidF(){
x=1;//正确,等价于this.x=1
y=1;//正确,等价于Sample.y=1
}
//G()是静态方法
staticvoidG(){
x=1;//错误,在静态方法中不能访问非静态成员x
y=1;//正确,在静态方法能访问静态成员y
}
//Main()是静态方法
staticvoidMain(){
Samples=newSample();
s.x=1;//正确
s.y=1;//错误,不能在类的实例中访问静态成员y
Sample.x=1;//错误,不能用类名访问非静态成员x
Sample.y=1;//正确,能通过类名访问静态成员y
}
}
3.3.6方法重载
方法重载:
方法名相同,但参数类型或参数个数不同。
与构造函数重载相似。
//ch3_11.cs
usingSystem;
publicclassEmployee
{
publicstringname;//员工姓名
publicintage;//员工年龄
publicdecimalsalary;//员工薪水
//构造函数可接受员工姓名、年龄和薪水
publicEmployee(stringname,intage,decimalsalary)
{
this.name=name;
this.age=age;
this.salary=salary;
}
//比较员工的年龄大小
publicintmax(intx,inty)
{
if(x>y)
returnx;
else
returny;
}
//比较员工的薪水高低
publicdecimalmax(decimalx,decimaly)
{
if(x>y)
returnx;
else
returny;
}
publicstaticvoi