欢迎光临
我们一直在努力

深入剖析C#继承机制(1)-ASP教程,ASP应用

建站超值云服务器,限时71元/月

内容导航

一、继承基础知识

二、c#的继承规则

三. 访问与隐藏基类成员

四、多级继承

五、继承与访问修饰符

  一. 继承基础知识

  为了提高软件模块的可复用性和可扩充性,以便提高软件的开发效率,我们总是希望能够利用前人或自己以前的开发成果,同时又希望在自己的开发过程中能够有足够的灵活性,不拘泥于复用的模块。c#这种完全面向对象的程序设计语言提供了两个重要的特性–继承性inheritance 和多态性polymorphism。

  继承是面向对象程序设计的主要特征之一,它可以让您重用代码,可以节省程序设计的时间。继承就是在类之间建立一种相交关系,使得新定义的派生类的实例可以继承已有的基类的特征和能力,而且可以加入新的特性或者是修改已有的特性建立起类的新层次。

  现实世界中的许多实体之间不是相互孤立的,它们往往具有共同的特征也存在内在的差别。人们可以采用层次结构来描述这些实体之间的相似之处和不同之处。

图1 类图

  上图反映了交通工具类的派生关系。最高层的实体往往具有最一般最普遍的特征,越下层的事物越具体,并且下层包含了上层的特征。它们之间的关系是基类与派生类之间的关系。

  为了用软件语言对现实世界中的层次结构进行模型化,面向对象的程序设计技术引入了继承的概念。一个类从另一个类派生出来时,派生类从基类那里继承特性。派生类也可以作为其它类的基类。从一个基类派生出来的多层类形成了类的层次结构。

  注意:c#中,派生类只能从一个类中继承。这是因为,在c++中,人们在大多数情况下不需要一个从多个类中派生的类。从多个基类中派生一个类这往往会带来许多问题,从而抵消了这种灵活性带来的优势。

c#中,派生类从它的直接基类中继承成员:方法、域、属性、事件、索引指示器。除了构造函数和析构函数,派生类隐式地继承了直接基类的所有成员。看下面示例:

using system ;

class vehicle //定义交通工具(汽车)类

{

protected int wheels ; //公有成员:轮子个数

protected float weight ; //保护成员:重量

public vehicle( ){;}

public vehicle(int w,float g){

wheels = w ;

weight = g ;

}

public void speak( ){

console.writeline( "交通工具的轮子个数是可以变化的! " ) ;

}

} ;

class car:vehicle //定义轿车类:从汽车类中继承

{

int passengers ; //私有成员:乘客数

public car(int w , float g , int p) : base(w, g)

{

wheels = w ;

weight = g ;

passengers=p ;

}

}

  vehicle 作为基类,体现了"汽车"这个实体具有的公共性质:汽车都有轮子和重量。car 类继承了vehicle 的这些性质,并且添加了自身的特性:可以搭载乘客

二、c#中的继承符合下列规则:

  1、继承是可传递的。如果c从b中派生,b又从a中派生,那么c不仅继承了b中声明的成员,同样也继承了a中的成员。object 类作为所有类的基类。

  2、派生类应当是对基类的扩展。派生类可以添加新的成员,但不能除去已经继承的成员的定义。

  3、构造函数和析构函数不能被继承。除此以外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类能否访问它们。

  4、派生类如果定义了与继承而来的成员同名的新成员,就可以覆盖已继承的成员。但这并不因为这派生类删除了这些成员,只是不能再访问这些成员。

  5、类可以定义虚方法、虚属性以及虚索引指示器,它的派生类能够重载这些成员,从而实现类可以展示出多态性。

  6、派生类只能从一个类中继承,可以通过接吕实现多重继承。

  下面的代码是一个子类继承父类的例子:

 

using system ;

public class parentclass

{

public parentclass( )

{ console.writeline("父类构造函数。"); }

public void print( )

{ console.writeline("im a parent class。") ; }

}

public class childclass : parentclass

{

public childclass( )

{ console.writeline("子类构造函数。") ; }

public static void main( ) {

childclass child = new childclass( ) ;

child.print( ) ;

}

}

  程序运行输出:

  父类构造函数。子类构造函数。im a parent class。

  上面的一个类名为parentclass, main函数中用到的类名为childclass。要做的是创建一个使用父类parentclass现有代码的子类childclass。

  1.首先必须说明parentclass是childclass的基类。

  这是通过在childclass类中作出如下说明来完成的:"public class childclass : parentclass"。在派生类标识符后面,用分号":" 来表明后面的标识符是基类。c#仅支持单一继承。因此,你只能指定一个基类。

  2.childclass的功能几乎等同于parentclass。

  因此,也可以说childclass "就是" parentclass。在childclass 的main( )方法中,调用print( ) 方法的结果,就验证这一点。该子类并没有自己的print( )方法,它使用了parentclass中的 print( )方法。在输出结果中的第三行可以得到验证。

  3.基类在派生类初始化之前自动进行初始化。parentclass 类的构造函数在childclass的构造函数之前执行

三. 访问与隐藏基类成员

  (1) 访问基类成员

  通过base 关键字访问基类的成员:

   调用基类上已被其他方法重写的方法。

   指定创建派生类实例时应调用的基类构造函数。

   基类访问只能在构造函数、实例方法或实例属性访问器中进行。

   从静态方法中使用 base 关键字是错误的。

  示例:下面程序中基类 person 和派生类 employee 都有一个名为 getinfo 的方法。通过使用 base 关键字,可以从派生类中调用基类上的 getinfo 方法。

 

using system ;

public class person

{

protected string ssn = "111-222-333-444" ;

protected string name = "张三" ;

public virtual void getinfo() {

console.writeline("姓名: {0}", name) ;

console.writeline("编号: {0}", ssn) ;

}

}

class employee: person

{

public string id = "abc567efg23267" ;

public override void getinfo() {

// 调用基类的getinfo方法:

base.getinfo();

console.writeline("成员id: {0}", id) ;

}

}

class testclass {

public static void main() {

employee e = new employee() ;

e.getinfo() ;

}

}

  程序运行输出:

   姓名: 张三

   编号: 111-222-333-444

   成员id: abc567efg23267

   示例:派生类同基类进行通信。

using system ;

public class parent

{

string parentstring;

public parent( )

{ console.writeline("parent constructor.") ; }

public parent(string mystring) {

parentstring = mystring;

console.writeline(parentstring) ;

}

public void print( )

{ console.writeline("im a parent class.") ; }

}

public class child : parent

{

public child( ) : base("from derived")

{ console.writeline("child constructor.") ; }

public void print( ) {

base.print( ) ;

console.writeline("im a child class.") ;

}

public static void main( ) {

child child = new child( ) ;

child.print( ) ;

((parent)child).print( ) ;

}

}

  程序运行输出:

from derived

child constructor.

im a parent class.

im a child class.

im a parent class.

  说明:

  1.派生类在初始化的过程中可以同基类进行通信。

  上面代码演示了在子类的构造函数定义中是如何实现同基类通信的。分号":"和关键字base用来调用带有相应参数的基类的构造函数。输出结果中,第一行表明:基类的构造函数最先被调用,其实在参数是字符串"from derived"。

  2.有时,对于基类已有定义的方法,打算重新定义自己的实现。

  child类可以自己重新定义print( )方法的实现。child的print( )方法覆盖了parent中的 print 方法。结果是:除非经过特别指明,parent类中的print方法不会被调用。

  3.在child类的 print( ) 方法中,我们特别指明:调用的是parent类中的 print( ) 方法。

  方法名前面为"base",一旦使用"base"关键字之后,你就可以访问基类的具有公有或者保护权限的成员。 child类中的print( )方法的执行结果出现上面的第三行和第四行。

  4.访问基类成员的另外一种方法是:通过显式类型转换。

  在child类的main( )方法中的最后一条语句就是这么做的。记住:派生类是其基类的特例。这个事实告诉我们:可以在派生类中进行数据类型的转换,使其成为基类的一个实例。上面代码的最后一行实际上执行了parent类中的 print( )方法

2) 隐藏基类成员

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

  密封类在声明中使用sealed 修饰符,这样就可以防止该类被其它类继承。如果试图将一个密封类作为其它类的基类,c#将提示出错。理所当然,密封类不能同时又是抽象类,因为抽象总是希望被继承的。

  在哪些场合下使用密封类呢?密封类可以阻止其它程序员在无意中继承该类。而且密封类可以起到运行时优化的效果。实际上,密封类中不可能有派生类。如果密封类实例中存在虚成员函数,该成员函数可以转化为非虚的,函数修饰符virtual 不再生效。

  让我们看下面的例子:

 

bstract class a

{

public abstract void f( ) ;

}

sealed class b: a

{

public override void f( )

{ // f 的具体实现代码 }

}

  如果我们尝试写下面的代码

class c: b{ }

  c#会指出这个错误,告诉你b 是一个密封类,不能试图从b 中派生任何类。

  (3) 密封方法

  我们已经知道,使用密封类可以防止对类的继承。c#还提出了密封方法(sealedmethod) 的概念,以防止在方法所在类的派生类中对该方法的重载。对方法可以使用sealed 修饰符,这时我们称该方法是一个密封方法。

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

using system ;

class a

{

public virtual void f( )

{ console.writeline("a.f") ; }

public virtual void g( )

{ console.writeline("a.g") ; }

}

class b: a

{

sealed override public void f( )

{ console.writeline("b.f") ; }

override public void g( )

{ console.writeline("b.g") ; }

}

class c: b

{

override public void g( )

{ console.writeline("c.g") ; }

}

  类b 对基类a 中的两个虚方法均进行了重载,其中f 方法使用了sealed 修饰符,成为一个密封方法。g 方法不是密封方法,所以在b 的派生类c 中,可以重载方法g,但不能重载方法f。

  (4) 使用 new 修饰符隐藏基类成员

  使用 new 修饰符可以显式隐藏从基类继承的成员。若要隐藏继承的成员,请使用相同名称在派生类中声明该成员,并用 new 修饰符修饰它。

  请看下面的类:

public class mybase

{

public int x ;

public void myvoke() ;

}

  在派生类中用 myvoke名称声明成员会隐藏基类中的 myvoke方法,即:

public class myderived : mybase

{ new public void myvoke (); }

  但是,因为字段 x 不是通过类似名隐藏的,所以不会影响该字段。

  通过继承隐藏名称采用下列形式之一:

   a、引入类或结构中的常数、指定、属性或类型隐藏具有相同名称的所有基类成员。

   b、引入类或结构中的方法隐藏基类中具有相同名称的属性、字段和类型。同时也隐藏具有相同签名的所有基类方法。

   c、引入类或结构中的索引器将隐藏具有相同名称的所有基类索引器。

  注意:在同一成员上同时使用 new 和 override 是错误的。同时使用 new 和 virtual 可保证一个新的专用化点。在不隐藏继承成员的声明中使用 new 修饰符将发出警告。

  示例1:在该例中,基类 mybasec 和派生类 myderivedc 使用相同的字段名 x,从而隐藏了继承字段的值。该例说明了 new 修饰符的使用。同时也说明了如何使用完全限定名访问基类的隐藏成员。

using system ;

public class mybase

{

public static int x = 55 ;

public static int y = 22 ;

}

public class myderived : mybase

{

new public static int x = 100; // 利用new 隐藏基类的x

public static void main()

{

// 打印x:

console.writeline(x);

//访问隐藏基类的 x:

console.writeline(mybase.x);

//打印不隐藏的y:

console.writeline(y);

}

}

  输出: 100 55 22

  如果移除 new 修饰符,程序将继续编译和运行,但您会收到以下警告:

the keyword new is required on myderivedc.x because it hides inherited member mybasec.x.

  如果嵌套类型正在隐藏另一种类型,如下例所示,也可以使用 new 修饰符修改此嵌套类型

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » 深入剖析C#继承机制(1)-ASP教程,ASP应用
分享到: 更多 (0)

相关推荐

  • 暂无文章