第3章面向对象编程基础_第1页
第3章面向对象编程基础_第2页
第3章面向对象编程基础_第3页
第3章面向对象编程基础_第4页
第3章面向对象编程基础_第5页
已阅读5页,还剩70页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第3章 面向对象编程基础理解基本概念理解类使用构造函数学习方法学习属性掌握1姓名、性别、年龄等:属性学习课程、参加社会活动:方法3.1

面向对象编程概念2对象——具有属性和操作(方法)的实体。 属性:对象所处的状态方法:用于改变对象状态以达到特定的功能对象有一个唯一的标识名类——在对象之上的抽象,一种抽象的数据类型类是对象的模板,对象是类的具体化或类的实例示例:学生——类各位同学——对象对象与类属性型号价格里程行为起动停车行驶属性车轮数量档的数量行为加速换档刹车属性名称颜色品种行为吃东西犬

吠摇尾巴34面向对象程序设计在处理对象时,必须遵循的三个原则:1.封装单元。是父类和子类之工具,对象则是封装的基本创建分等级层次的类,继承用一个框架把数据和代码组合在一起,形成一个对象。类是支持对象封装的继承利用继承可以间共享数据和方法的机制,通常把父类称为基类,子类称为派生类。单继承:一个基类可以有任意数目的派生类,即一父多子

多继承:一个类若有两个或两个以上的直接基类,即一子多父,称为多重继承,C#通过接口来实现多态性同一个操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。分为静态多态和动态多态根据参数的差别直接调用对象方法根据基类间接调用对象方法53.2

类3.2.1

类的声明语法形式:[属性集信息]

[类修饰符] class

类名

[:

类基]{[类主体]}属性集信息——是C#语言为程序中定义的各种实体附加一些说明信息,是C#语言的一个重要特征。属于高级编程内容,一般涉及不到。类名:唯一标识名类基:直接基类或接口。默认基类是object类,多个接口之间用逗号相隔类修饰符——访问权限、类的类型等,注意同一修饰符不允许出现多次,见表3.16表3.1

类修饰符修饰符作用说明public

表示不限制对类的访问。类的访问权限省略时默认为publicprotected表示该类只能被这个类的成员或派生类成员访问private表示该类只能被这个类的成员访问,很少用internal表示该类能够由程序集中的所有文件使用,而不能由程序集之外的对象使用new只允许用在嵌套类它表示所修饰的类abstract表示这是一个抽象因此不能被实例化sealed表示这是一个密封显然密封类不能同中,会隐藏继承下来的同名成员类,该类含有抽象成员,,只能用作基类类,不能从这个类再派生出其他类。时为抽象类示例:class

Student{//类主体}3.2.2

类的成员类体(类主体)用于定义该类的成员用一对花括号{

}括起来,由两部分组成:类体中以类成员声明形式引入的类成员包括:常数声明、字段声明、方法声明、属性声明、事件声明、索引器声明、运算符声明、构造函数声明、析构函数声明、静态构造函数、类型声明等直接从基类继承而来的成员7一、成员类型:静态成员在字段、方法、属性、事件、运算符和构造函数声明中含有public

static修饰符实例成员在字段、方法、属性、事件、运算符和构造函数声明中不含有public

static修饰符二、默认成员访问修饰符——private8类成员注意点:class

Program{class

AClass{public

static

int

x=1;public

int

y=2;}static

void

Main(string[]

args){AClass

ab

=

new

AClass();Console.WriteLine("y={0},x={1}",ab.y,AClass.x);}}9101.常数声明语法形式:[属性集信息] [常数修饰符] const类型

标识符

=

常数表达式

[,

…]常数修饰符——new、public、protected、private

、internalpublic:访问不受限制protected:访问仅限于包含类或从包含类派生的类型internal:访问仅限于当前程序集。

private:访问仅限于包含类。类型——sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal、bool、string、枚举类型或引用类型。常数表达式的值类型应与目标类型一致,或者通过隐式转换规则转换成目标类型。11例如:class

A_const//默认访问修饰符private{public

const

int

X=10;const

double

PI=3.14159;const

double

Y=

0.618+3.14;}注意1.常数表达式的值是一个可以在编译时计算的值注意2.常数声明不允许使用static修饰符,只能通过类访问。class

Test{public

static

void

Main(

){A_const

m

=

new

A_const

(

);Console.WriteLine

(“X={0},

PI={1},Y={2}”,

A_const.X

,

A_const.PI,A_const.Y);}}122.字段声明语法形式:[属性集信息] [字段修饰符]

类型

变量声明列表;变量声明列表——标识符或用逗号“,”分隔的多个标识符,并且变量标识符还可用赋值号“=”设定初始值。字段修饰符——new、public、protected、private、internal、static、readonly、volatilestatic:静态变量。利用类直接访问静态变量,利用类的实例(对象)访问非静态变量readonly:只读变量,声明时或在构造函数中赋值,一旦赋值不可改变。与const常量不同,const常量编译时赋值计算。volatile:表示字段可能被多个并发执行的进程修改。13【例3.1】通过构造函数给只读字段赋值//Radius是只读字段//通过构造函数对radius赋值usingSystem;public

class

Area{public

readonly

double

Radius;private

double

x,

y

;public

double

Size

;public

static

double

Sum=0.0

;public

Area

(

){Radius=1.0;}}class

Test{publicstaticvoidMain(){Area

s1

=

new

Area(

);//s1.Radius

=

2;Console.WriteLine

("Radius={0},Size={1},Sum={2}",s1.Radius,s1.Size

,

Area.Sum);//静态字段通过类访问Area.Sum,实例字段通过对象访问s1.SizeConsole.Read();}}出错了!无法修改只读字段3.2.3

构造函数和析构函数构造函数实现对象初始化分为实例构造函数和静态构造函数函数调用时机:new1)实例构造函数的声明语法形式:[属性集信息]

[构造函数修饰符]

标识符

(

[参数列表]

)[:

base

(

[参数列表]

)

] [:

this

(

[参数列表]

)

]{构造函数语句块}public与类名相同没有返回值构造函数重载:参数个数或类型不同调用基类同参数列表的构造函数14调用本类同参数列表的构造函数15构造函数修饰符——public、protected、internal、private、extern一般地,构造函数总是public类型的。如果是private类型的,表明类不能被外部类实例化。标识符([参数列表]opt)——构造函数名 必须与这个类同名,无返回类型和返回值构造函数可以有0~n个参数——构造函数重载用new运算符创建一个类的对象时,类名后的一对圆括号提供初始化列表,系统根据这个初始化列表的参数个数、参数类型和参数顺序调用不同的构造函数。实例构造函数注意点:16class

Test{static

void

Main(){Time

t1,

t2,

t3,

t4

;//对t1,t2,t3,t4分别调用不同的构造函数t1=

new

Time

();t2

=

new

Time(8);t3

=

new

Time(8,

30);

t4

=

new

Time(8,30,30);}}【例3.2】Time类的构造函数及其重载using

System;public

class

Time{private

int

hour,

minute,

second;public

Time

(

){hour=minute=second=0;}public

Time

(int

h){hour=h;minute=second=0;}public

Time

(int

h,

int

m){hour=h;minute=m;second=0;}public

Time

(int

h,

int

m,

int

s){hour=h;nute=m;cond=s;}}mi

public

string

getTime()se

{returnhour.ToString()+":"+minute.ToString()+":"+second.ToString();}Console.WriteLine("t1={0}",t1.getTime());17【例3.3】构造函数初始化usingSystem;class

Point{public

double

x,

y;public

Point(

){x

=

0; y

=

0;}public

Point(double

x,

double

y){this.x

=

x;this.y=

y;//当this在实例构造函数中使用时,//它的值就是对该构造的对象的引用}}class

Test{public

static

void

Main(

){Point

a

=

new

Point(

);Point

b=new

Point(3,

4);//

用构造函数初始化对象Console.WriteLine

("a.x={0},

a.y={1}",

a.x,

a.y

);Console.WriteLine

("b.x={0},

b.y={1}",

b.x,

b.y

);Console.Read

();}}a.x=0,

a.y=0b.x=3,

b.y=4构造函数注意点:若类中没有提供构造函数,则CLR会自动提供一个默认的构造函数一旦类中提供了自定义的构造函数,系统则不提供默认的构造函数不必显示调用构造函数实例构造函数不能被继承18派生类构造函数的调用调用时机:创建派生类的对象时,即new构造函数不被继承派生类构造函数在执行前,首先显式或隐式地调用基类构造函数默认调用基类的无参数构造函数除非显式地指定调用有参数构造函数this关键字引用类的当前实例base关键字用于从派生类中访问基类的成员调用基类上已被其他方法重写的方法指定创建派生类实例时应调用的基类构造函数19【例3.4】派生类构造函数及其调用using

System;class

Point{private

int

x,

y;public

Point(){x

=

0; y

=

0;Console.WriteLine

("Point()

constructor

:

{0}

",

this

);}public

Point(int

x,

int

y){this.x

=

x;this.y

=

y;);Console.WriteLine

("Point(x,y)

constructor

:

{0}

",

this}}class

Circle

:

Point{//默认约定调用基类的无参构造函private

double

radius;public

Circle

(){数Point()this

);{0}

",

this

);20Console.WriteLine

("Circle

()

constructor

:

{0}

",

this

);}public

Circle

(double

radius

):

base

(

){this.radius

=

radius;Console.WriteLine

("Circle

(radius)

constructor

:

{0}

",}public

Circle

(int

x,

int

y,

double

radius

):

base

(x,

y

){this.radius

=

radius;Console.WriteLine

("Circle

(x,

y,

radius)

constructor

:}}class

Test{static

void

Main(){Point

a

=

new

Point();Circle

b

=

new

Circle

(3.5);Circle

c

=

new

Circle

(1,

1,

4.8);Console.Read

();132}}54212)静态构造函数的声明语法形式:[属性集信息] [静态构造函数修饰符]

标识符(){静态构造函数体}静态构造函数修饰符——[extern]static

或者static

[extern]。如有extern修饰,则是一个外部静态构造函数,不提供任何实际的实现,静态构造函数体仅仅是一个分号标识符——是静态构造函数名,必须与类同名且不能有参数静态构造函数体——只能对静态数据成员进行初始化22静态构造函数注意点:静态构造函数不可继承,不能直接调用调用时机:创建类的实例引用类的任何静态成员至多被执行一次若类没有声明静态构造函数,而又包含带有初始设定的静态字段时,编译器会自动生成一个默认的静态构造函数232.析构函数语法形式:[属性集信息] [

extern

]~标识符

(

){析构函数体}标识符——必须与类名相同,前面需加“~”析构函数——不能写返回类型,也不能带参数,一个类最多只能有一个析构函数析构函数注意点析构函数不能由程序显式地调用,而是由系统在释放对象时自动调用派生类对象的析构函数调用链:执行派生类的析构函数执行基类的析构函数,直到调用object类的析构函数为止24与执行构造函数的顺序相反第3章 面向对象编程基础理解基本概念理解类使用构造函数学习方法学习属性掌握25回顾对象——具有属性和操作(方法)的实体类——对象的抽象,抽象的数据类型类是对象的模板对象是类的具体化或类的实例26类的成员271.类体中以类成员声明形式引入的类成员包括:常数声明、字段声明、构造函数声明、析构函数声明、静态构造函数、方法声明、

属性声明等2.直接从基类继承而来的成员访问修饰符28public访问不受限制protected只能被本身或派生类的实例访问private只能被本身访问static静态,非实例构造函数29构造函数——实例构造函数与类名同名在类进行new运算(即类实例化时)this

:引用当前类的实例base:用于从派生类中访问基类的成员调用基类上已被其他方法重写的方法指定创建派生类实例时应该调用的基类构造函数调用顺序先基类后派生类303.3

方法3.3.1

方法的声明语法形式:[属性集信息] [方法修饰符]

返回类型

方法名(

[形参表]){方法体}C#中所有的变量、方法都必须封装在类中对象间、类间只能通过类所提供的方法进行互操作从方法的声明、方法的参数、静态方法与实例方法、方法的重载与覆盖等方面理解31表3.2

方法修饰符修饰符作用说明new在一个继承结构中,用于隐藏基类同名的方法public表示该方法可以在任何地方被访问,默认publicprotected表示该方法可以在它的类体或派生类类体中被访问,但不能在类体外访问private表示该方法只能在这个类体内被访问internal表示该方法可以被同处于一个工程的文件(程序集)访问static表示该方法属于类本身,而不属于某特定对象virtual表示该方法可在派生类中重写,来更改该方法的实现abstract表示该方法仅仅定义了方法名及执行方式,但没有给出具体实现,所以包含这种方法的类是抽象类,有待于派生类的实现override表示该方法是将从基类继承的virtual方法的新实现sealed表示这是一个密封方法,它必须同时包含override修饰,以防止它的派生类进一步重写该方法extern表示该方法从外部实现32表3.3

修饰符的无效组合修饰符不能与下列选项一起使用staticvirtual、abstract

和overridevirtualstatic、abstract

和overrideoverridenew、static和virtualabstractvirtual

和staticnewoverrideextern

abstract返回类型——方法可以返回值也可以不返回值。返 回值类型为任意一种C#的数据类型,通过return语句返回。如果方法不返回值,则它的返回类型可标为void,默认情况下为void方法名:除Main()、关键字以外33【例3.6】下面程序中的StackTp类定义了几个方法以模拟实现一个压栈操作。using

System;class

StackTp{int

MaxSize;int

Top;int

[

]

StkList;//构造函数public

StackTp

(

){MaxSize=100;Top=0;StkList=new

int

[MaxSize];构造函数}public

StackTp(int

size)

//{MaxSize=size;Top=0;StkList=new

int

[MaxSize];//方法}public

bool

isEmptyStack(){if

(Top==0)elsereturn

true;return

false;}public

bool

isFullStack(){if(Top==MaxSize)

returntrue;else return

false;}public

void

push(int

x){StkList[Top]=x;Top++;}}class

Test{public

static

void

Main(){StackTp

ST

=

new

StackTp(20);string

s1;if(ST.isEmptyStack())//调用方法s1="Empty";elses1="not

Empty";Console.WriteLine

("Stack

is

"

+

s1);for

(int

i=0;

i<20;

i++)ST.push(i+1);if(ST.isFullStack())//调用方法s1="Full";elses1="not

Full";Console.WriteLine

("Stack

is

"

+

s1);Console.Read

();}}343.3.2

方法的参数形参(形式参数):声明方法时包含的方法参数说明实参(实际参数):调用方法时给出的实际参数在C#中实参与形参有4种传递方式:值参数引用参数——ref输出参数——out参数数组——params1.值参数在方法声明时不加修饰的形参就是值参数,它表明实参与形参之间按值传递在方法中对形参的修改不影响外部的实参属于输入参数35using

System;class

Myclass{public

void

Sort

(intx,

inty,int

z){int

tmp;//tmp是方法Sort的局部变量//将x,

y,z按从小到大排序

if(x>y){tmp=x;

x=y; y=tmp;

}if(x>z){tmp=x;

x=z; z=tmp;

}if(y>z){tmp=y;

y=z; z=tmp;

}//Console.WriteLine(“x={0},y={1},

z={2}"

x,y,z);}}class

Test{static

void

Main(){Myclass

m

=

new

Myclass

(

);int

a,

b,

c;a=30;

b=20;

c=10;m.Sort

(a,

b,

c);Console.WriteLine

("a={0},b={1},

c={2}"

,

a,

b,

c

);Console.Read

();}}【例3.7】演示了当方法Sort传递的是值参数时,对形参的修改不影响其实参。36是值参数传递方式,形参另外分配一块内存,接受实参的引用值副本对引用值的修改不会影响实参

如果改变的是所引用的对象,则将会影响实参所引用的对象值参数——传递引用对象内存首地址37class

Test{static

void

Main(){Myclass

m

=

new

Myclass

(

);int

[

]

score

={

87,89,56,90,100,75,64,45,

80,

84}

;m.SortArray(score);for

(int

i=0;i<score.Length;i++){Console.Write

("score[{0}]={1},"

,

i

,

score[i]

);if

(i==4)

Console.WriteLine

();}Console.Read

();}}【例3.8】程序演示的是当方法传递的是一个引用对象(如数组)时,对形参的修改会影响到实参using

System;class

Myclass{public

void

SortArray

(int

[

]

a){int

i,

j,

pos,tmp;for

(i=0;

i<a.Length-1

;

i++){for

(pos=j=i;

j<a.Length;

j++)if(a

[pos]>a

[j])pos=j;

查询最小值if

(pos

!=

i

){tmp=a[i];a[i]=a[pos];a[pos]=tmp;}}}}改变了引用的对象832.

引用参数引用参数——形参的改变影响实参1.例3.7中实际变量a、b、c是不随形参x、y、z的,但如果想改变实际变量a、b、c的值,则用引用参数39using

System;class

Myclass{public

void

Sort

(intx,

inty,int

z){int

tmp;//tmp是方法Sort的局部变量//将x,

y,z按从小到大排序

if(x>y){tmp=x;

x=y; y=tmp;

}if(x>z){tmp=x;

x=z; z=tmp;

}if(y>z){tmp=y;

y=z; z=tmp;

}//Console.WriteLine(“x={0},y={1},

z={2}"

x,y,z);}}class

Test{static

void

Main(){Myclass

m

=

new

Myclass

(

);int

a,

b,

c;a=30;

b=20;

c=10;m.Sort

(a,

b,

c);Console.WriteLine

("a={0},b={1},

c={2}"

,

a,

b,

c

);Console.Read

();}}【例3.7】演示了当方法Sort传递的是值参数时,对形参的修改不影响其实参。42.

引用参数0引用参数——形参的改变影响实参例3.7中实际变量a、b、c是不随形参x、y、z的,但如果想改变实际变量a、b、c的值,则用引用参数C#用ref修饰符声明引用参数引用参数不创建新的存储单元,与方法调用中的实际参数变量同处一个存储单元——传递内存地址属于输入参数【例3.9】将例3.7程序中Sort方法的值参数传递方式改成引用参数传递,这样在方法Sort中对参数x、y、z按从小到大的排序影响了调用它的实参a、b、c。using

System;class

Myclass{public

void

Sort

(ref

int

x, ref

int

y,

refint

z){int

tmp;//tmp是方法Sort的局部变量//

将x,

y,

z按从小到大排序if

(x>y)

{

tmp=x;

x=y; y=tmp;

}if

(x>z)

{

tmp=x;

x=z; z=tmp;

}if

(y>z)

{

tmp=y;

y=z; z=tmp;

}}}class

Test{static

void

Main(){Myclass

m

=

new

Myclass

(

);int

a,

b,

c;a=30;

b=20;

c=10;m.Sort

(ref

a,

ref

b,

ref c);Console.WriteLine

("a={0},

b={1},c={2}",

a,

b,

c

);Console.Read();}}414使用ref声明引用参数的注意:2方法的参数声明中ref关键字仅对紧跟在它后面的参数有效,而不能应用于整个参数表。例如,Sort方法中x、y、z都要加ref修饰在方法的调用中也要用ref修饰实参,且实参必须为变量,实参和形参的数据类型必须完全匹配在方法外(方法调用前),ref参数必须明确赋值ref参数也可以用于传递引用类型的对象第3章面向对象编程基础43输出参数——out参数数组——params方法方法声明——如同C语言的“函数”访问修饰符默认为public方法参数值参数(值类型、引用类型)引用参数——ref方法重载与覆盖方法签名方法覆盖——new

和虚方法virtual

override属性propertyget访问器set访问器443.输出参数45用法与ref参数相似,但为输出参数值只能用于从方法中传出值,因此必须在方法结束之前对out参数赋值在参数前加out修饰符的形式参数out参数也可以用于传递引用类型的对象46class

Test{static

void

Main(){Myclassm=

newMyclass(

);int

[

]

score

={

87,89,56,90,100,75,64,45,

80,

84}

;int

smax,smin;double

savg;m.MaxMinArray(score,outsmax,

out

smin,

out

savg);Console.Write

("Max={0},Min={1},

Avg={2}

"

,

smax,smin,savg

);Console.Read

();}}【例3.10】求一个数组中元素的最大值、最小值和平均值。using

System;class

Myclass{public

void

MaxMinArray

(int

[

]

a,out

int

max,

out

int

min,

out

double

avg

){int

sum

;sum

=

max

=

min

=

a[0]

;

for

(int

i=1;

i<a.Length

;

i++){if

(a[i]>max)

max=a[i];if

(a[i]<min)

min=a[i];sum+=a[i];}avg=sum

/

a.Length;}}47【例3.11】下面程序定义了两个方法,一个是Swap1,一个是Swap2,它们都有两个引用对象作为参数,但Swap2的参数加了ref修饰,调用这两个方法产生的结果是不一样的。usingSystem;class

Myclass{public

void

Swap1

(string

s,

string

t){string

tmp;tmp=s;

s=t;t=tmp;}public

void

Swap2

(ref

string

s,

refstring

t){string

tmp;tmp=s;

s=t;t=tmp;}}class

Test{static

void

Main(){Myclass

m

=

new

Myclass

(

);string

s1="ABCDEFG",

string

s2="134567";m.Swap1(s1,s2);Console.WriteLine

("s1={0}"

,

s1

);Console.WriteLine

("s2={0}"

,

s2

);m.Swap2(ref

s1,

ref

s2);Console.WriteLine

("s1={0}"

,

s1

);Console.WriteLine

("s2={0}"

,

s2

);Console.Read

();}}s1,s2的引用并没有改变s1,s2的引用互相交换了484.参数数组特点:参数为可变长度使用params关键字来指定一个可变长度的参数表注意:一个方法中只能声明一个params参数params参数必须放在参数表最后params参数是一个一维数组,参数数组的参数:0~nparams不可与ref、out修饰符组合使用3.3.3

静态方法与实例方法静态方法用static修饰只能访问类中的静态成员49【例3.12】下面程序演示了Myclass类中的方法MaxMin有一个参数数组类型的参数,在调用这个方法时具有灵活性。usingSystem;class

Myclass{public

void

MaxMin

(out

int

max,

outint

min,

paramsint

[

]

a){//如果可变参数为零个,//取一个约定值或产生异常if

(a.Length==0){max=min=-1;return

;}max=

min

=

a[0]

;for(int

i=1;

i<a.Length

;

i++){if

(a[i]>max)

max=a[i];if

(a[i]<min)

min=a[i];}}}class

Test{static

void

Main(){Myclassm=

newMyclass();int

[]

score

=

{

87,89,56,90,100,75,64,45,

80,

84}int

smax,smin;m.MaxMin

(outsmax,outsmin);//可变参数的个数可以是零个Console.WriteLine

("Max={0},

Min={1}

"

,

smax,smin

);m.MaxMin(outsmax,outsmin,

45,76,89,90);//在4个数之间找最大、最小Console.WriteLine

("Max={0},

Min={1}

"

,

smax,smin

);m.MaxMin

(out

smax,

out

smin,

score);

//可变参数也可接受数组对象Console.WriteLine

("Max={0},

Min={1}

"

,

smaxsmin

);Console.Read

();}}503.3.4

方法的重载与覆盖方法重载同一个类中存在具有相同方法名,但具有不同的形式参数个数、修饰符及类型的两个或两个以上的方法。重载是多态的形式之一。编译时根据调用时给出的实参个数和类型调用相应的方法,以实现多态方法签名:方法名、形参个数、形参修饰符、形参类型一起构成方法签名51【例3.14】下面程序定义的Myclass类中含有4个名为max的方法,但它们或者参数个数不同,或者参数类型不同,在Main调用该方法时,编译器会根据参数的个数和类型确定调用哪个max方法。usingSystem;class

Myclass{public

int

max(int

x,

int

y){return

x>=y

?

x

:

y

;}public

double

max(double

x,

double

y){returnx>=y?x:

y;}public

int

max

(int

x,

int

y,

int

z){returnmax

(

max(x,

y),

z)

;}public

double

max

(

double

x,

double

y,double

z){returnmax

(

max(x,

y),

z)

;}}class

Test{staticvoidMain(){Myclass

m

=newMyclass();int

a,

b,

c;double

e,

f,

g

;a=10;

b=20;

c=30;e=

1.5;

f

=

3.5;

g

=

5.5;Console.WriteLine

("max({0},{1})=

{2}

",a,b,

m.max(a,b));Console.WriteLine

("max({0},{1},{2})={3}

"

,a,b,c,

m.max(a,b,c));Console.WriteLine

("max({0},{1})=

{2}"e,f,m.max(e,f));Console.WriteLine

("max({0},{1},{2})={3}

"

,e,f,g,

m.max(e,f,g));Console.Read

();}}52方法的覆盖——使用new隐藏有相同签名的方法当派生类与基类有相同名称或签名的成员时,在派生类中默认隐藏基类成员,但提示警告在派生类成员声明中加new修饰符,可以取消警告信息基类对象被引用到派生类对象时,访问的仍是基类的方法静态多态53【例3.15】定义了一个基类Shape,含有字段域width和height,分别表示形状的宽和高,并定义了一个area方法,求形状的面积。它的派生类Triangle和Trapezia都用关键字new修饰了area方法using

System;class

Shape{

protected

double

width;protected

double

height;public

Shape(

){

width=height=0;

}public

Shape(double

x){

width=height=x;

}public

Shape(double

w,

double

h

){

width=w;height=h;

}public

double

area

(){ return

width*height;

}//三角形}class

Triangle

:

Shape{public

Triangle

(double

x,

double

y):base(x,y){

}new

public

double

area

()//派生类方法与基类方法同名,编译时会有警告信息{ return

width*height/2

;

}}class

Trapezia:Shape

//梯形{ double

width2

;public

Trapezia(double

w1,

double

w2,

doubleh)

:

base

(w1,

h)

{ width2=w2;

}new

public

double

area

(

)//加new隐藏基类的area方法{ return

(width+width2)*height/2

;

}}class

Test{static

void

Main(){Shape

A

=

new

Shape(2,

4);Triangle

B

=

new

Triangle(1,

2);Trapezia

C

=

new

Trapezia(2,

3,4);Console.WriteLine("A.area=

{0}

",

A.area());//调Shape的area方法Console.WriteLine("B.area=

{0}

",

B.area());//调Triangle的area方法Console.WriteLine("C.area=

{0}

",

C.area());//调Trapezia的area方法A

=

B;//在C#中,基类的引用也能够引用派生类对象Console.WriteLine("A.area=

{0}

",

A.area());//调Shape的area方法A

=

C;Console.WriteLine("A.area=

{0}

",

A.area());//调Shape的area方法Console.Read();}}基类方法45虚方法:基类的方法用关键字virtual修饰对基类的虚方法重载:派生类用关键字

override修饰与基类中虚方法有相同签名的方法注意:不能将虚方法声明为静态的(static)不能将虚方法声明为私有的(private)覆盖方法必须与它相关的虚方法匹配一个覆盖方法覆盖的必须是虚方法实现运行时多态(动态绑定)55【例3.16】将例3.15改写,Shape类中的方法area用virtual修饰,而派生类Triangle和Trapezia用override修饰area方法。using

System;class

Shape{protected

double

width;protected

double

height;public

Shape(

){

width=height=0;

}public

Shape(double

x){

width=height=x;

}public

Shape(double

w,

double

h

){ width=w;

height=h;

}public

virtual

double

area

(

)//基类中用virtual修饰符声明一个虚方法{ return

width*height;

}//三角形}class

Triangle:

Shape{public

Triangle

(double

x,

double

y):base(x,y){

}public

override

double

area

(

)//派生类中用override修饰符覆盖基类虚方法{ return

width*height/2

;

}}class

Trapezia:Shape//梯形{ double

width2

;public

Trapezia(double

w1,

double

w2,

double

h)

:base

(w1,

h){

width2=w2;}public

override

double

area

(

)派生类中用override修饰符覆盖基类虚方法{ return

(width+width2)*height/2

;

}class

Test{static

voidMain(){Shape

A

=

new

Shape(2,

4);Triangle

B

=

new

Triangle(1,

2);Trapezia

C

=

new

Trapezia(2,

3,

4);Console.WriteLine("A.area=

{0}

",A.area());

//调Shape的area方法Console.WriteLine("B.area=

{0}

",B.area());

//调Triangle的area方法Console.WriteLine("C.area=

{0}

",C.area());

//调Trapezia的area方法A

=B;Console.WriteLine("A.area=

{0}

",A.area());

//调派生类Triangle的area方法

A=C;Console.WriteLine("A.area=

{0}

",A.area());

//调派生类Trapezia的area方法

Console.Read();}}派生类方法563.4

属性property1.回顾一下例3.2(P51)。①为了实现良好的数据封装和数据隐藏,类的字段成员的访问属性一般设置成

private(默认)或protected②利用public

方法来访问私有的或受保护的字段57public

string

getTime(){returnhour.ToString()+":"+minute.ToString()+":"+second.ToString();}【例3.2】Time类的构造函数及其重载using

System;public

class

Time{private

int

hour,

minute,

second;public

Time

(

){hour=minute=second=0;}public

Time

(int

h){hour=h;minute=second=0;}public

Time

(int

h,

int

m){hour=h;minute=m;second=0;}public

Time

(int

h,

int

m,

int

s){hour=h;minute=m;second=s;}}class

Test{static

void

Main(){Time

t1,

t2,

t3,

t4

;t1=

new

Time

();

t2

=

new

Time(8);t3

=

new

Time(8,

30);

t4

=

new

Time(8,30,30);t1.getTime();

t2.getTime();t3.getTime();

t4.getTime();}}583.4

属性1.

保持原有的数据封装和数据隐藏,类的字段成员的访问属性仍然设置成private(默认)或protected?????2.

不利用public方法来访问私有的或受保护的字段属性(property)①属性值的读/写与字段域语法相同;②利用get和set访问器实现属性值的读/写如何做??59语法形式:[属性集信息] [属性修饰符]

类型

成员名{访问器声明}属性修饰符——与方法修饰符(public,private,protected,…)相同,包括new、static、virtual、abstract、override和4种访问修饰符的合法组合,遵循相同的规则。访问器声明——声明属性的访问器,get访问器或set访问器,或者两个语法形式:get

//读访问器{…

//访问器语句块}set

//写访问器{…

//访问器语句块}属性的声明方法60get访问器返回值类型与属性的类型相同在语句块中的return语句必须有一个可隐式转换为属性类型的表达式set访问器没有返回值有一个隐式的值参数value,类型与属性的类型相同。属性的种类读/写属性:同时包含get和set访问器的属性只读/只写属性:只包含get或set访问器的属性属性中的访问器使用注意61【例3.17】对TextBox类的text、fontname、fontsize、multiline域提供属性方式的读/写访问。using

System;class

TextBox{private

string

text;private

int

fontsize;

private

bool

multiline;public

TextBox

(

){text="text1";fontsize=12;fontname="宋体";multiline=false;}public

string

Text{

//

Text属性,可读可写get

{

return

text;

}set

{

text=value;

}}public

string

FontName{ //

FontName属性,只读属性get

{ return

fontname;

}}public

int

FontSize{

//FontSize属性,可读可写

get{return

fontsize;}

set{fontsize=value;}}public

bool

MultiLine{

//MultiLine属性,只写

set{multiline=value;}}}class

Testprivate

string

fontname;

{static

void

Main(){TextBox

Text1=

new

TextBox

();//调用Text属性的get访问器Console.WriteLine("Text1.Text={0}",Text1.Text);Text1.Text="这是文本框";//调用Text属性的set访问器Console.WriteLine

("Text1.Text=

{0}

"

,

Text1.Text);Console.WriteLine

("Text1.Fontname=

{0}

"

,Text1.FontName);Text1.FontSize=36;Text1.MultiLine

=

true;Console.WriteLine

("Text1.FontSize=

{0}

"

,Text1.FontSize);Console.Read

();}}62【例3.18】定义Label类,设置Width和Heigh属性,分别存放两点之间在水平坐标轴和垂直坐标轴上的投影长度。using

System;class

Point{int

x,

y;public

int

X{get

{

return

x;

}}public

int

Y{get{

return

y;

}}public

Point

(

){

x=y=0;

}public

Point

(int

x,

int

y){this.x=x;this.y=y;}}class

Label{Point

p1=new

Point

(

);Point

p2=new

Point

(5,

10);public

int

Width//计算两点之间在水平坐标轴上的投影长度{get

{

return

p2.X-p1.X;

}}public

int

Height//计算两点之间在垂直坐标轴上的投影长度{ get

{

return

p2.Y-p1.Y;

}}}class

Test{static

void

Main(){Label

Label1=

new

Label

();Console.WriteLine

("Label1.Width=

{0}

",

Label1.Width);Console.WriteLine

("Label1.Height=

{0}

",

Label1.Height);Console.Read

();}}633.5

综合应用实例【例3.19】学生成绩管理程序。根据学生选修的课程及课程学分和课程成绩计算GPA,最后按GPA的值对学生进行排序。基本思路:本程序的学生总人数、课程名、课程学分可以由控制台输入,为叙述简单,假定每个学生所选修的课程相同。Course类中定义了课程名、课程学分字段域,Name属性、构造函数。

Student类中定义字段:学生姓名、学号、选修课程数、Course类对象数组、成绩和GPA等属性:CourseNum静态属性、GPA属性、Name属性方法:SetCourse方法——设置课程名,静态方法AddData方法——给每个学生加入姓名、学号、成绩。

ComputeGPA

方法——计算学生成绩的GPAstuSwap方法——对两个Student对象内容进行交换。64//课程名//课程学分class

Course{string

courseName;int

courseMark;public

Course(){}public

Course(string

Name,

int

Mark){courseName=Name;courseMark=Mark;}//Name属性,课程名可读可写public

string

Name{getreturn

courseName;}{set{courseName=value;

}//Mark属性,课程学分可读可写returncourseMark;}courseMark=value;}}public

int

Mark{get{set{}}65class

Student{string

stuName;//学生姓名string

stuID;

//学生学号static

int

numberOfCourse;static

Course

[

]

list

;int[]stuScore;//每个学生对象的各课程成绩//GPA值double

stuGPA;public

Student(){list=new

Course[numberOfCourse];for

(int

i=0;

i<numberOfCourse;i++)list[i]=new

Course();stuScore=new

int[numberOfCourse];}加static修饰符表明这个域为所有学生类对象共享Course类对象数组,用于设置每门课程名、课程学分66//将CourseNum定义成静态属性是因为它只对静态域进行操作public

static

intCourseNum{get{set{return

numberOfCourse;

}numberOfCourse=value;

}//GPA属性是只读属性}public

double

GPA{get{ return

stuGPA;

}//Name属性可读可写}public

string

Name{getreturn

stuName;}{set{stuName=value;}}67//将SetCourse设为静态方法,是因为它仅访问静态数据域//不需要创建Student类对象就可直接用Student类名调用//它的形参是一个参数数组,这样调用时就可根据实际选修的课程数来设置public

static

void

SetCourse(params

Course

[

]

topic){for

(int

i=0;

i<topic.Length

;

i++){list[i].Name

=topic[i].Name;list[i].Mark

=topic[i].Mark;}}//AddData方法将一个学生的数据添加到学生类对象数组中

public

void

AddData(string

name,

string

Id,

int[]score){stuName=name;stuID=Id;for

(int

i=0;i<score.Length;

i++)stuScore[i]=score[i];}68//根据课程的学分以及学生成绩计算GPApublic

void

ComputeGPA()}{int

i;double

sMark,

sumMark=0,

sumGP=0;for

(i=0;i<stuScore.Length;i++){if

(stuScore[i]>=95)

sMark=4.5;else

if

(stuScore[i]>=90)

sMark=4;else

if

(stuScore[i]>=85)

sMark=3.5;else

if

(stuScore[i]>=80)

sMark=3;else

if

(stuScore[i]>=75)

sMark=2.5;else

if

(stuScore[i]>=70)

sMark=2;else

if

(stuScore[i]>=65)

sMark=1.5;else

if

(stuScore[i]==60)

sMark=1;else

sMark=0;sumGP+=list[i].Mark*sMark;sumMark+=list[i].Mark;}stuGPA=sumGP/sumMark;69//stuSwap方法提供两个Student类对象的交换操作,注意形参被修饰为refpublic

void

stuSwap(ref

Student

stu1,ref

Student

stu2){string

name,Id;int

i;int

[]

score=new

int

[Student.CourseNum];double

gpa;name=stu1.Name;Id=stu1.stuID;gpa=stu1.GPA;f

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论