深入剖析C#继承机制--访问与隐藏基类成员

上一篇 / 下一篇  2007-08-10 17:11:25 / 个人分类:C#

JA6G _LHB,G(|8H0(1)访问基类成员51Testing软件测试网#J2{7EyDD%] {
   通过base 关键字访问基类的成员:51Testing软件测试网 l0V.J6C^*vM

51Testing软件测试网5X:s*nP-EEr(Ac

   调用基类上已被其他方法重写的方法。51Testing软件测试网^0DS(vll
   指定创建派生类实例时应调用的基类构造函数。51Testing软件测试网PA:p[a,z)W,w3l/T6\+\
   基类访问只能在构造函数、实例方法或实例属性访问器中进行。51Testing软件测试网Kcb[b#Q|
   从静态方法中使用 base 关键字是错误的。51Testing软件测试网3[,{-t0x$E*Y)F x

51Testing软件测试网 fOv _r@

51Testing软件测试网.E:OeK@|A+i
  示例:下面程序中基类 Person 和派生类 Employee 都有一个名为 Getinfo 的方法。通过使用 base 关键字,可以从派生类中调用基类上的 Getinfo 方法。

`:j+z:ilg7L EK0

Be_+^8r%pEC0using System ;
%Tf `8]`rvV'RB3R0public class Person51Testing软件测试网-X&A0p9c Qp0v)^c A
{51Testing软件测试网N"Q+?w2x
protected string ssn = "111-222-333-444" ;51Testing软件测试网MK`R0c SC
protected string name = "张三" ;51Testing软件测试网;i;yL.VVI4}l|-u
public virtual void GetInfo() {51Testing软件测试网s*i,e h:@|CN
Console.WriteLine("姓名: {0}", name) ;51Testing软件测试网R7YZhMx!Hb4C6N
Console.WriteLine("编号: {0}", ssn) ;51Testing软件测试网FJat+GGFf jt1R
}
[ C:X/DMv|V8|0}51Testing软件测试网B Znp*Tln'm
class Employee: Person51Testing软件测试网pkE(|D%^/c,l
{
Wy3KP4K.mpi MjD0public string id = "ABC567EFG23267" ;51Testing软件测试网rEf4e?C"y9`l
public override void GetInfo() {51Testing软件测试网c0C;e ]3T3` O]x[
// 调用基类的GetInfo方法:51Testing软件测试网| q3z1c6p)}0]m
base.GetInfo();
Zt3rp:X0Console.WriteLine("成员ID: {0}", id) ;51Testing软件测试网 b be NN
}51Testing软件测试网rTw&VF/zn
}
[K9e,NM(J0class TestClass {51Testing软件测试网IxC%L;_ X
public static void Main() {
tA+`-} Q b2bu0Employee E = new Employee() ;
2eu*Qm,Cs$Hu0E.GetInfo() ;51Testing软件测试网%|(C tfR\~8x
}51Testing软件测试网bq(vA5weZ M)T
}51Testing软件测试网m rE7GD

51Testing软件测试网zu`9}2au ?9D2To


7r#?d#ZM^x)S0  程序运行输出:51Testing软件测试网 qF#di EIv

X ^-N$D9Cs!zlH0
bt$s c1J$wbK4U0   姓名: 张三51Testing软件测试网o-L&_.B@*f1J'x
   编号: 111-222-333-444
:R}*K.XS9e0   成员ID: ABC567EFG2326751Testing软件测试网+Q;ax"]B'{K-c
   示例:派生类同基类进行通信。51Testing软件测试网 _oBY Y@-jFG!b

51Testing软件测试网9k/T*k&JL_v

 51Testing软件测试网 u.]&a[&BJ/WMs"p5H(@

9Z}0Z E-h$M(e Ha0using System ;51Testing软件测试网6O+kp3[0z"^g~P(X!o
public class Parent
#Le z{0gcve }z0{
.n R%U3yP o0string parentString;51Testing软件测试网P8~1K0ct @5Ya
public Parent( )
{4I6X]Sr0{ Console.WriteLine("Parent Constructor.") ; }
E0Lgb)n0public Parent(string myString) {51Testing软件测试网!f/s8kSD @U
parentString = myString;51Testing软件测试网$Pww8O[-T6[
Console.WriteLine(parentString) ;51Testing软件测试网2su!g5un'pf8?R
}51Testing软件测试网 C dWE\![T~({
public void print( )51Testing软件测试网 ryw? M
{ Console.WriteLine("I''m a Parent Class.") ; }
q Ogl?ZeC2t Qi0}
_G E9Qc@0public class Child : Parent
0gs*~mp4G]!Zh0{
_n^V3g0public Child( ) : base("From Derived")
u1A&qa\7Y_ ~ l0{ Console.WriteLine("Child Constructor.") ; }51Testing软件测试网9x~2K-N9k q `
public void print( ) {
9Bz"\M;?s0base.print( ) ;51Testing软件测试网_/`!maaI7D
Console.WriteLine("I''m a Child Class.") ;51Testing软件测试网2Mm:qBw!s
}51Testing软件测试网)^WF([ gz4Y!O cX5s
public static void Main( ) {51Testing软件测试网4PD@:R_&O5h
Child child = new Child( ) ;
l`6cFwq5G2hc/B8F0child.print( ) ;
WMPnM$K@0((Parent)child).print( ) ;
LROgnb"i0}51Testing软件测试网W-]y/fl!`0fK
}51Testing软件测试网6@,g(}/S$Xf#W'O:Z)_1J

51Testing软件测试网F+@0F5jR


k!kDTS1A"j)K0  程序运行输出:

9NDg7a4Tft0

;e1BR } ag051Testing软件测试网@ q,I8c;@5|JD\&d
From Derived
K%_5H0Ze*_d_0Child Constructor.
O0s:cvx6}{j0I''m a Parent Class.51Testing软件测试网}B(S ^O6XL"[y
I''m a Child Class.51Testing软件测试网6F] b+|i%] J
I''m a Parent Class.

V;r}-_2]u051Testing软件测试网5e Qab#k/\


TaH1j;K+^s0  说明:51Testing软件测试网D7s-A(S [Aj\~kK{

51Testing软件测试网}f7Gf/o8G9THJ


Y+b(U] mu0  1.派生类在初始化的过程中可以同基类进行通信。51Testing软件测试网%}G0w$Wk

X \%D7s1J{Nzv*Y0
GS`B}-ONh0  上面代码演示了在子类的构造函数定义中是如何实现同基类通信的。分号":"和关键字base用来调用带有相应参数的基类的构造函数。输出结果中,第一行表明:基类的构造函数最先被调用,其实在参数是字符串"From Derived"。

1D6iS b{xa051Testing软件测试网Xg*|-t1y#R O4c*T5L0^_


|\Wx3K&w0  2.有时,对于基类已有定义的方法,打算重新定义自己的实现。51Testing软件测试网Q"`k1C5KfJ

51Testing软件测试网;{`5n:dtf9s m

51Testing软件测试网!n qa_`2Hc
  Child类可以自己重新定义print( )方法的实现。Child的print( )方法覆盖了Parent中的 print 方法。结果是:除非经过特别指明,Parent类中的print方法不会被调用。51Testing软件测试网/Y {D \eyQ

51Testing软件测试网%{e4n4|6t.N&H

51Testing软件测试网4qp3Q4W r*g$Dm
  3.在Child类的 print( ) 方法中,我们特别指明:调用的是Parent类中的 print( ) 方法。

g2s+D`I$t_;?5sY051Testing软件测试网*s?nMnR!m#`


$~7Y0wB;K'B:@H0  方法名前面为"base",一旦使用"base"关键字之后,你就可以访问基类的具有公有或者保护权限的成员。 Child类中的print( )方法的执行结果出现上面的第三行和第四行。51Testing软件测试网@:ciA_2W ^atl M

51Testing软件测试网G r3o `&d#@#lM

51Testing软件测试网+N-{7|G%oA
  4.访问基类成员的另外一种方法是:通过显式类型转换。

?8S;l ` }!xM$Z'e051Testing软件测试网 Z!|EEPy Y


}%J+v4j,H1S)m"R0  在Child类的Main( )方法中的最后一条语句就是这么做的。记住:派生类是其基类的特例。这个事实告诉我们:可以在派生类中进行数据类型的转换,使其成为基类的一个实例。上面代码的最后一行实际上执行了Parent类中的 print( )方法。51Testing软件测试网#i$_%Z9v~8E"NX+R

/R$VA0OPr:e/B {P02) 隐藏基类成员

0m(Q0D7@Ha!D$]6y;E051Testing软件测试网;e)t!]1w-W'_

  想想看,如果所有的类都可以被继承,继承的滥用会带来什么后果?类的层次结构体系将变得十分庞,大类之间的关系杂乱无章,对类的理解和使用都会变得十分困难。有时候,我们并不希望自己编写的类被继承。另一些时候,有的类已经没有再被继承的必要。C#提出了一个密封类(sealed class)的概念,帮助开发人员来解决这一问题。51Testing软件测试网 n l*E8F;bn&j2J

51Testing软件测试网 EN?-`|5?+o g


U [mZOFO:~0  密封类在声明中使用sealed 修饰符,这样就可以防止该类被其它类继承。如果试图将一个密封类作为其它类的基类,C#将提示出错。理所当然,密封类不能同时又是抽象类,因为抽象总是希望被继承的。51Testing软件测试网0F$l%QI6uQV

51Testing软件测试网*C"I&l5Wk[ ['T

51Testing软件测试网W0I2t`.R!p
  在哪些场合下使用密封类呢?密封类可以阻止其它程序员在无意中继承该类。而且密封类可以起到运行时优化的效果。实际上,密封类中不可能有派生类。如果密封类实例中存在虚成员函数,该成员函数可以转化为非虚的,函数修饰符virtual 不再生效。51Testing软件测试网7J;f5\ee'@0S5B.VT

0xe xjI C-T%a"A rIA0
fN:L+u_0  让我们看下面的例子:

M;fT6H#{051Testing软件测试网{L.ZW-FgT-Z,U

 51Testing软件测试网*R {#sHx9`*i

51Testing软件测试网D6F{0p SW[%C

abstract class A
.o%Wa c] SzA @/u+s0{51Testing软件测试网@:p^+Ta
public abstract void F( ) ;51Testing软件测试网 Zus[S!@6vNc
}51Testing软件测试网.fyU;J!X9nK}+Y8y2z
sealed class B: A51Testing软件测试网$I M!ucA!v.w?r
{
GS@#mf?"YI2w%]0public override void F( )51Testing软件测试网 \ ^*XPn,Y}#R-Cj WV4z}
{ // F 的具体实现代码 }
UGDOh9hTI,^0}51Testing软件测试网(x^y"y8S&w.?okoC-R

51Testing软件测试网Jn6B@#h6b!`0B

51Testing软件测试网#BcUu2Zk
  如果我们尝试写下面的代码51Testing软件测试网u7mB*U'P'AsT

+|6S"? Ua4fT051Testing软件测试网{8J'~.f&e.ZJ/u+t
class C: B{ }51Testing软件测试网%f;n5o^/Y&L

51Testing软件测试网Xhf:{+lav


z\MUQ9H1H%T]5h&x!ff+?0  C#会指出这个错误,告诉你B 是一个密封类,不能试图从B 中派生任何类。

S$v]&D7M5f G0

@AL*P0X4e051Testing软件测试网;U.i-| P4V8d.]
  (3) 密封方法

/YV`FE.@0

)]L+J&pTE0  我们已经知道,使用密封类可以防止对类的继承。C#还提出了密封方法(sealedmethod) 的概念,以防止在方法所在类的派生类中对该方法的重载。对方法可以使用sealed 修饰符,这时我们称该方法是一个密封方法。51Testing软件测试网L`v(a%wn:xN

eiY4R2E8Rsa051Testing软件测试网8R!z c/Gm
  不是类的每个成员方法都可以作为密封方法密封方法,必须对基类的虚方法进行重载,提供具体的实现方法。所以,在方法的声明中,sealed 修饰符总是和override 修饰符同时使用。请看下面的例子代码:

^;Q(dK w051Testing软件测试网[him;uaH4S,lU

 

5X8w.cW`%~0

O.v4}&DpV\)L0using System ;
)Zc*^LZv0class A
i6Ic)E1i4B4S p3@ t%C"O0{
K$f]GW&e;CPf0public virtual void F( )51Testing软件测试网Z6g1c&F)[pW@C)w
{ Console.WriteLine("A.F") ; }
_:B x l.t p0public virtual void G( )
a myX*B$]0{ Console.WriteLine("A.G") ; }51Testing软件测试网-Z n-~/q(q \T7b I
}51Testing软件测试网G*HJ^ V1HL*sb
class B: A51Testing软件测试网+MBW({B$D'vH
{
!vo1g%e~:{uQ5M0sealed override public void F( )51Testing软件测试网)n$A M e C#v N u
{ Console.WriteLine("B.F") ; }
dsGK wD0override public void G( )
)yRR|(ac&q \'_0{ Console.WriteLine("B.G") ; }51Testing软件测试网;p6e tY|e
}51Testing软件测试网tK2]A G
class C: B51Testing软件测试网wA[2H\cvY]cA#}
{
,Ao:ahT or |0override public void G( )51Testing软件测试网Goo?l@t3L$ioDd&OF
{ Console.WriteLine("C.G") ; }
0cTr;o6w@+Lj0}51Testing软件测试网r6u"?l"q#n:[

51Testing软件测试网4x6i4b$SC\


!o3Ox}cR,|0  类B 对基类A 中的两个虚方法均进行了重载,其中F 方法使用了sealed 修饰符,成为一个密封方法。G 方法不是密封方法,所以在B 的派生类C 中,可以重载方法G,但不能重载方法F。51Testing软件测试网 Rh,o `!y1|

2pTd.^*H~5_4Q$]#[0
(K"|i$e1c/f_i0  (4) 使用 new 修饰符隐藏基类成员

(C Q J[TxV2h051Testing软件测试网G2DByBq0drZ-B

51Testing软件测试网n7k;g/l,gvA
  使用 new 修饰符可以显式隐藏从基类继承的成员。若要隐藏继承的成员,请使用相同名称在派生类中声明该成员,并用 new 修饰符修饰它。

5n"LO L A#uhH0

1~YNcU(L]0
G2A.o0AN:B;I1x0  请看下面的类:

%h},NB\p-A0

pb nW2[0 51Testing软件测试网 ]&K+\}%b;^_Z

&f"i5Q5wq9z0public class MyBase
pa4imb0{
YUv@.G@0public int x ;
;CTr.l!m S&k0public void MyVoke() ;
k1QC:y9OTK0}

"w,C6^ nC3M0N0

*kZv2p` LoC(J0
Rk7W3n:{*h!z5K_0  在派生类中用 MyVoke名称声明成员会隐藏基类中的 MyVoke方法,即:

@I!j@ m0

xl4EDr:fF0 

W0zF-l6gSd m#S0

%_R4I|"Vf0public class MyDerived : MyBase51Testing软件测试网3KC J4~`"s|7v7S a
{ new public void MyVoke (); }51Testing软件测试网9vp"f!N H3P

51Testing软件测试网VEbMy

51Testing软件测试网P,w%C"?3xOc
  但是,因为字段 x 不是通过类似名隐藏的,所以不会影响该字段。

$iWp1b,`]%b"h6V051Testing软件测试网H5hsZ3o;u


D?u+Xcej0  通过继承隐藏名称采用下列形式之一:

lmy5N\LR051Testing软件测试网 z D/O8E$`

51Testing软件测试网8ob*AT#`0B+Q E
   a、引入类或结构中的常数、指定、属性或类型隐藏具有相同名称的所有基类成员。51Testing软件测试网s'c(l`{dC

hRoQZ qAO HK0
2Dtb-A L ^X!K0   b、引入类或结构中的方法隐藏基类中具有相同名称的属性、字段和类型。同时也隐藏具有相同签名的所有基类方法。51Testing软件测试网L6I3L1Y.M1}cP

Kh:l8c}%q-N&{iH0
NSdTp"t:GC0   c、引入类或结构中的索引器将隐藏具有相同名称的所有基类索引器。51Testing软件测试网-sb.X s6S.nQa S H @1DW

51Testing软件测试网X#E2k/Jv?p{*Y1t


.u["x(|.x w8hPK+I0  注意:在同一成员上同时使用 new 和 override 是错误的。同时使用 new 和 virtual 可保证一个新的专用化点。在不隐藏继承成员的声明中使用 new 修饰符将发出警告。51Testing软件测试网3krbgDT)q(M

D G7`]r/K(~-o!g051Testing软件测试网 l@ ZX:G Ka
  示例1:在该例中,基类 MyBaseC 和派生类 MyDerivedC 使用相同的字段名 x,从而隐藏了继承字段的值。该例说明了 new 修饰符的使用。同时也说明了如何使用完全限定名访问基类的隐藏成员。

!P:M,Z!Q9T6dy051Testing软件测试网-`$] k0S(y-S*W8a

 

1Dt2Z9dVf&cj4X051Testing软件测试网 `Bd)V%{R&g-Mz.e

using System ;
!]h2B5yQ1@0public class MyBase51Testing软件测试网7u8p&j9{5O F
{51Testing软件测试网6x.Q+n+v?!Ko4jd
public static int x = 55 ;
:U.}0I#Cx a Y0public static int y = 22 ;51Testing软件测试网s)[[ d1o4r
}51Testing软件测试网"pPi$I7_.K
public class MyDerived : MyBase51Testing软件测试网5v5v&E6t9LmK-k m
{51Testing软件测试网+w5w*q6sCz
new public static int x = 100; // 利用new 隐藏基类的x
l$v%h(A1['z0public static void Main()51Testing软件测试网{2[X4PUY$x
{
5@"{1j Cp"`1e}5j R0// 打印x:
h})\&y L&s0Console.WriteLine(x);
X1~S4}/{] H$ewl0//访问隐藏基类的 x:51Testing软件测试网(u"Zb/l_-^-Ok
Console.WriteLine(MyBase.x);
aQ'~9V'N,QO's-s0//打印不隐藏的y:51Testing软件测试网Wn JdQi&p
Console.WriteLine(y);51Testing软件测试网 @"Gs1?:~ c!M
}51Testing软件测试网5R(hiSm
}51Testing软件测试网^R+]-|G9?

51Testing软件测试网!y9Ky{s@


5\w SOo6t0  输出: 100 55 2251Testing软件测试网/zzu!~0W0f

Nn*cxJsw+J0
_"dC@$R"Iz0  如果移除 new 修饰符,程序将继续编译和运行,但您会收到以下警告:

R5c8m,p-Vu+Q"Ia7Rc r ~#Q051Testing软件测试网7[t!E0x@

51Testing软件测试网:_&y8]!a o9[p+Q
The keyword new is required on ''MyDerivedC.x'' because it hides inherited member ''MyBaseC.x''.

2n%p T!Wa:q/\A0

k NX2u-dKT,}051Testing软件测试网"wL n D A"d+o J
  如果嵌套类型正在隐藏另一种类型,如下例所示,也可以使用 new 修饰符修改此嵌套类型。 

g z&Oe1j051Testing软件测试网'xTB|#J

TAG:

 

评分:0

我来说两句

Open Toolbar