深入剖析C#继承机制

2016-02-19 16:55 10 1 收藏

下面图老师小编跟大家分享深入剖析C#继承机制,一起来学习下过程究竟如何进行吧!喜欢就赶紧收藏起来哦~

【 tulaoshi.com - 编程语言 】

  一. 继承基础知识

  为了提高软件模块的可复用性和可扩充性,以便提高软件的开发效率,我们总是希望能够利用前人或自己以前的开发成果,同时又希望在自己的开发过程中能够有足够的灵活性,不拘泥于复用的模块。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("I'm a Parent Class。") ; }}public class ChildClass : ParentClass{public ChildClass( ){ Console.WriteLine("子类构造函数。") ; }public static void Main( ) {ChildClass child = new ChildClass( ) ;child.print( ) ;}} 

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

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

  (2) 隐藏基类成员

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

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

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

  让我们看下面的例子:

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  

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 隐藏基类的xpublic 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 修饰符修改此嵌套类型。

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  四、多级继承

  一些面向对象语言允许一个类从多个基类中继承,而另一些面向对象语言只允许从一个类继承,但可以随意从几个接口或纯抽象类中继承。

  只有C++支持多级继承,许多程序员对此褒贬不一。多级继承常会引起继承来的类之间的混乱,继承而来的方法往往没有唯一性,所以C#中类的继承只可以是一个,即子类只能派生于一个父类,而有时你必须继承多个类的特性,为了实现多重继承必须使用接口技术,下面是对接口的多重继承进行介绍:

  

using System ;//定义一个描述点的接口interface IPoint{int x {get ;set ;}int y {get ;set ;}}interface IPoint2{int y {get ;set ;}}//在point中继承了两个父类接口,并分别使用了两个父类接口的方法class Point:IPoint,IPoint2{//定义两个类内部访问的私有成员变量private int pX ;private int pY ;public Point(int x,int y) {pX=x ;pY=y ;}//定义的属性,IPoint接口方法实现public int x{get{ return pX ; }set{ pX =value ; }}//IPoint1接口方法实现public int y{get{ return pY ; }set{ pY =value ; }}}class Test{private static void OutPut( IPoint p ){ Console.WriteLine("x={0},y={1}",p.x,p.y) ; }public static void Main( ) {Point p =new Point(15,30) ;Console.Write("The New Point is:") ;OutPut( p ) ;string myName =Console.ReadLine( ) ;Console.Write("my name is {0}", myName) ;}} 

  五、继承与访问修饰符

  访问修饰符是一些关键字,用于指定声明的成员或类型的可访问性。类的继承中有四个访问修饰符: public protected internal private。使用这些访问修饰符可指定下列五个可访问性级别: public protected internal internal protected private。

  声明的可访问性意义public访问不受限制。protected访问仅限于包含类或从包含类派生的类型。internal访问仅限于当前项目。protected internal访问仅限于从包含类派生的当前项目或类型。private访问仅限于包含类型。

  1、继承中关于可访问域的一些问题

  基类的所有成员(实例构造函数、析构函数和静态构造函数除外)都由派生类型继承。这甚至包括基类的私有成员。但是,私有成员的可访问域只包括声明该成员的类型的程序文本。在下面的示例中

  

class A{int x ;static void F(B b) {b.x = 1 ; // 对}}class B: A{static void F(B b) {b.x = 1 ; // 错误}} 

  类 B 继承类 A 的私有成员 x。因为该成员是私有的,所以只能在 A 的"类体"中对它进行访问。因此,对 b.x 的访问在 A.F 方法中取得了成功,在 B.F 方法中却失败了。

  2、继承中关于属性的一些问题

  和类的成员方法一样,我们也可以定义属性的重载、虚属性、抽象属性以及密封属性的概念。与类和方法一样,属性的修饰也应符合下列规则:

  属性的重载

  1. 在派生类中使用修饰符的属性,表示对基类中的同名属性进行重载。

  2. 在重载的声明中,属性的名称、类型、访问修饰符都应该与基类中被继承的属性一致。

来源:http://www.tulaoshi.com/n/20160219/1613546.html

延伸阅读
一、什么是 C#? C# 是由Microsoft开发的一种新型编程语言,由于它是从C和C++ 中派生出来的,因此具有C++的功能。同时,由于是Microsoft公司的产品,它又同 VB一样简单。对于web开发而言,C#象 Java,同时具有Delphi的一些优点。Microsoft宣称:C#是开发.NET框架应用程序的最好语言。 二、Java与 C# 要学习C#,不能不首先看一看Java语言。 相...
五、C# 编辑器 编写C#程序可以在文本编辑器中进行,或者在集成开发环境Visual Studio中进行。市场上还有一些第三方编辑器,其中一些是免费的。更多信息请查看这里。 六、C#的程序结构 一个C#程序包含一个类,这个类中至少有一个公用的静态方法Main,这个方法对程序进行初始化并终止程序。在Main方法中创建子对象、执行方法并实现软件的逻辑...
Java与C#的事件处理都是实现了事件源-事件响应者机制,但又不完全相同。Java实现的是一种事件源与事件响应者两级实体对象方式,这里的事件响应者也是事件监听者,而C#实现的是一种事件源-代理-事件响应者三级实体对象方式。下面就这两种方式来具体说明。 Java事件处理 从概念上讲,事件是一种在"源对象"和"监听者...
九、C#编程实战演习ABC  1、用Visual Studio .NET编写C#程序 Visual Studio.NET 是Microsoft新一代的旗舰开发环境,在这个环境中,我们能够看到Microsoft将所有开发工具都集成到一个IDE中。我们惊喜地发现,我们拥有了一个所有编程语言都适用的代码编辑器。而且,这个环境中还具一个HTML 编辑器、一个XML编辑器、一个SQL Server界面以及一...
项目DALFactory是采用工厂模式设计的,设计模式的书我也曾看过java的设计模式,理解也不太深刻,但对工厂模式还是较为熟悉,使用它可以根据需求返回不同的实例对象,在DALFactory项目中使用反射机制来实现依赖注入,当然,它的实现还是没有java中的spring那样灵活,强大,部分代码如下: // <summary> /// 抽象工厂模式创建DAL。 /// web.co...

经验教程

327

收藏

71
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部