07. 类和继承

来源:互联网 发布:mac搜狗输入法用不了 编辑:程序博客网 时间:2024/05/16 10:58

7.1 类继承

通过继承我们可以定义一个新类,新类纳入一个已经声明的类并进行扩展。

  • 可以使用一个已经存在的类作为新类的基础。已存在的类称为基类(base class),新类称为派生类(derived class)。派生类成员的组成如下:
    • 自己声明中的成员。
    • 基类的成员。
  • 要声明一个派生类,需要在类名后加入基类规格说明。基类规格由冒号和后面跟着用作基类的类的名称组成。派生类被描述为直接继承自列出的基类。
  • 派生类被描述为扩展它的基类,因为它包含了基类的成员,加上在它自己的声明中的任何附加功能。
  • 派生类不能删除它所继承的任何成员。
7.2 访问继承的成员
继承的成员可以被访问,就像它们是派生类自己声明的一样。(继承的结构有些不同,后面阐述)

所有类都派生自object类

除了特殊的类object,所有的类都是派生类,即使它们没有基类规格说明。类object是唯一的非派生类,因为它是继承层次结构的基础。

没有基类规格说明的类隐式地直接派生自类object。不加基类规格说明只是指定object为基类的简写。

关于类继承的其他重要内容如下:

  • 一个类声明的基类规格说明中只能有一个单独的类。这称为单继承。
  • 虽然类只能直接继承一个基类,但继承的层次没有限制。也就是说,作为基类的类可以派生自另外一个类,而它又派生自另外一个类,一直下去,直至最终到达object。
基类和派生类是相对的术语。所有的类都是派生类,要么派生自object,要么派生自其他的类。所以,通常当我们称一个类为派生类时,我们的意思是它直接派生自某类而不是object。

7.3 隐藏基类的成员
虽然派生类不能删除它继承的任何成员,但它可以隐藏它们。
  • 要隐藏一个继承的数据成员,需要声明一个新的相同类型的成员,并使用相同的名称。
  • 通过在派生类中声明新的带有相同签名的函数成员,可以隐藏或掩盖继承的函数成员。请记住,签名由名称和参数列表组成,但不包括返回类型。
  • 要让编译器知道你在故意隐藏继承的成员,使用new修饰符。没有它,程序可以成功编译,但编译器会期待你隐藏了一个继承的成员。
  • 也可以隐藏静态成员。
7.4 基类访问
有时,派生类需要访问被隐藏的继承成员。可以使用基类访问表达式访问隐藏的基类成员。基类访问表达式由关键字base后面跟着一个点和成员的名称组成。

7.5 使用基类的引用
派生类的实例由基类的实例加上派生类附加的成员组成。派生类的引用指向整个类对象,包括基类部分。
如果有一个派生类对象的引用,就可以获取该对象基类部分的引用,使用类型转换运算符把该引用转换为基类类型。类型转换去处符旋转在对象引用的前面,由圆括号括起的要被转换成的类名组成。

虚方法和覆写方法
当使用基类引用访问派生类对象时,得到的是基类的成员。虚方法可以使基类的引用访问“升至”派生类内。
可以使用基类引用调用派生类(derived class)的方法,只需满足下面的条件:
    • 派生类的方法和基类的方法有相同的签名和返回类型。
    • 基类的方法使用virtual标注。
    • 派生类的方法使用override标注。
其他关于virtual和override修饰符的重要信息如下:
    • 覆写和被覆写的方法必须有相同的可访问性。换一种说法,被覆写的方法不能是private等,而覆写方法是public。
    • 不能覆写static方法或非虚方法。
    • 方法、属性和索引,以及称为事件的另一种成员类型,它们都可以被声明为virtual和override。
覆写标记为override的方法
覆写方法可以在继承的任何层次出现。
    • 当使用对象基类部分的引用调用一个覆写的方法时,方法的调用被沿派生层次上溯执行,一直到标记为override的方法的最派生(most-derived)版本。
    • 如果在更高的派生级别有该方法的其他声明,但没有被标记为override,那么它们不会被调用。
7.6 构造函数的执行

要创建对象的基类部分,基类的一个构造函数被作为创建实例过程的一部分被调用。

继承层次链中的每个类在执行它自己的构造函数体之前执行它的基类的构造函数。

警告:在构造函数中调用虚方法是极不推荐的。在基类的构造函数被执行时,基类的虚方法会调用派生类的覆写方法,但这是在派生类的构造函数方法体被执行之前,因此,调用会在派生类没有完全初始化之前传递到派生类。

构造函数初始化语句

默认情况下,在对象被构造时,蕨类的无参数构造函数被调用。但构造函数可以被重载,所以基类可能有一个以上的构造函数。如果希望派生类使用一个指定的基类构造函数而不是无参数构造函数,必须在构造函数初始化语句中指定它。

有两种形式的构造函数初始化语句:

    • 第一种形式使用关键字base并指明使用哪一个基类构造函数。
    • 第二种形式使用关键字this并指明应该使用当前类的哪一个另外的构造函数。
基类构造初始化语句放在冒号后面,冒号紧跟着类的构造函数声明的参数列表。构造函数初始化语句由关键字base和要调用的基类构造函数的参数列表组成。
public MyDerivedClass(int x,string s) :base(s,x)
当声明一个不带构造函数初始化语句的构造函数时,它是带有base()组成的构造函数初始化语句的形式的简写。
另一种构造函数初始化语句的形式指示构造函数使用同一类的不同构造函数。
public MyClass(int x):this(x,"Using Default String"){...}

类访问修饰符

类可以被系统中其他类看到并访问。

术语可见的有时用作术语可访问的。它们可以被互换使用。类的可访问性有两个级别:publicinternal

    • 标记为public的类可以被系统内任何程序集中的代码访问。
    • 标记为internal的类只能被它自己所在的程序集内的类看到。
      • 这是默认的可访问级别,所以,除非在类的声明中显式地指定修饰符public,程序集外部的代码不能访问该类。
      • 可以使用internal访问修饰符显式地声明一个类为内部的。

7.7 程序集间的继承

C#也允许从一个在不同的程序集内定义的基类派生类。要做到这一点,必须满足下列条件:

  • 基类必须被声明为public,这样它才能被从它所在的程序集外部访问
  • 必须在visual Studio工程中包括对包含该基类的程序集的引用。
为了使引用其他程序集中的类和类型更容易,不使用它们的完全限定名称,在源文件的顶部放置一个using指令,并带上将要访问的类或类型所在的命名空间。

7.8 成员访问修饰符
类的可访问性描述了类的可见性;成员的可访问性描述了类成员的可见性。
声明在类中的每个成员对系统的不同部分可见,这依赖于类声明中指派给它的访问修饰符。

在观察成员访问性的细节之前,首先有一些通用内容需要阐述:
  • 所有显示声明在类的声明中的成员都是互相可见的,无论它们的访问性说明如何。
  • 继承的成员不在类的声明中显式声明,所以,如你应该看出,继承的成员对派生类的成员可以是可见的,也可以是不可见的。
  • 有两个成员访问级别:
    • 公有的
    • 私有的
    • 受保护的
    • 内部的
    • 受保护内部的
  • 必须对每个成员指定成员访问级别。如果不指定某个成员的访问级别,它的隐式访问级别是private。
  • 成员不能比它的类更可访问。也就是说,如果一个类的可访问性限于它所在的程序集,那么类的成员个体也不能从程序集的外部看到,无论它们的访问修饰符是什么。
访问级别基于两个关于正在声明的类的特征:
  • 类是否派生自正在声明的类。
  • 类是否和正在声明的类在同一程序集。
成员访问修饰符的总结

成员访问修饰符修饰符含 义private只在类的内部可访问internal对该程序集内所有类可访问protected对所有继承该类的类可访问protected internal对所有继承该类或在该程序集内声明的类可访问public对任何类可访问


7.9 抽象成员

抽象成员是被设计来被覆写的函数成员。抽象成员有以下特征:

  • 它被用abstract修饰符标记。
  • 它没有实现代码块。抽象成员的代码块被分号代替。
关于抽象成员的其他重要事实如下:
  • 尽管抽象方法必须在派生类中用相应的方法覆写,但不能把virtual修饰符附加到abstract修饰符。
  • 就像虚方法,派生类中抽象方法的实现必须指定override修饰符。
  • 抽象成员只能被声明在抽象类中。

比较虚成员和抽象成员 虚成员抽象成员关键字virtualabstract实现体有实现体没有实现体,被分号取代在派生类中被覆写能被覆写,使用override必须被覆写,使用override成员的类型方法
属性
事件
索引方法
属性
事件
索引

7.10 抽象类

抽象类只能被用作其他类的基类。抽象类就是被设计来被继承的。

  • 不能创建抽象类的实例
  • 抽象类使用abstract修饰符声明。
  • 抽象类可以包含抽象成员,但不是必须的。抽象类的成员可以是抽象成员和普通带实现的成员的任意组合。
  • 抽象类自己可以派生自另一个抽象类。
  • 任何派生自抽象类的类必须使用override关键字实现该类所有的抽象成员,除非派生类自己也是抽象类。

7.11 密封类

抽象类必须被用作基类,它不能像独立的类那样被实例化。密封类与它相反。

  • 密封类只能被用作独立的类,它不能被用作基类。
  • 密封类使用sealed修饰符标注。
7.12 静态类
静态类是这样一种类,在那里所有成员都是静态的。静态类用于分组不受实例数据影响的数据和函数。静态类的一个普通的用途可能就是创建一个包含一组数学方法的数学库。
关于静态类需要了解的重要事情如下:
  • 类本身必须标记为static
  • 类的所有成员必须是静态的。
  • 类可以有一个静态构造函数,但没有实例构造函数,不能创建该类的实例。
  • 不能继承静态类,它们是密封的。
像访问其他静态成员那样访问它的成员。使用类名和成员名。

7.13 扩展方法

在迄今为止的内容中,你看到的每个方法都和声明它的类关联。C# 3.0的扩展方法特征扩展了这个边界,允许编写和声明它的类之外的类关联的方法。

扩展方法重要的需求如下:

  • 扩展方法必须被声明为static。
  • 扩展方法声明所在的类也必须被声明为static。
  • 扩展方法必须包含关键字this作为它的第一个参数类型,并在后面跟着它所扩展的类的名称。
7.14 外部方法
外部方法(external method)是在声明中没有实现的方法。其实现常常是用C#之外的语言编写的。
  • 外部方法使用extern修饰符标记,而且在类的声明中没有实现。它的实现被分号取代。
  • 声明和实现的连接是依赖实现的,但常常使用DllImport特性完成。

0 0