继承是面向对象编程中一个非常重要的特性,它也是另一个重要特性——多态的基础。
4.1 继承概念的引入
现实生活中的事物都归属于一定的类别。比如,狮子是一种动物。为了在计算机中模拟这种关系,面向对象的语言引入了继承(inherit)的特性。
构成继承关系的两个类中,动物称为父类(parent class)或基类(base class),狮子称为子类(child class)。
提示:
在一些书中,将父类称为超类(super class)。
“继承”关系有时又称为“派生”关系,“B继承自A”,可以说为“B派生自A”,或反过来说,“A派生出B”。
父类与子类之间拥有以下两个基本特性:
① 是一种(IS_A)关系:子类是父类的一种特例。
② 扩充(Extends)关系:子类拥有父类所没有的功能。
以下C#代码实现了狮子类与动物类之间的继承关系:
class Animal
{
}
class Lion:Animal
{
}
可以看到,C#中用一个冒号间隔开父类和子类。
4.2 类成员的访问权限
面向对象编程的一大特点就是可以控制类成员的可访问性。当前主流的面向对象语言都拥有以下三种基本的可访问性:
可访问性 | C#关键字 | 含义 |
公有 | public | 访问不受限制 |
私有 | private | 只有类自身成员可以访问 |
保护 | protected | 子类可以访问,其它类无法访问 |
(1)public和private
public和private主要用于定义单个类的成员存取权限。
请看以下示例代码:
public class A
{
public int publicI;
private int privateI;
protected int protectedI;
}
当外界创建一个A的对象后,只能访问A的公有实例字段publicI:
A a=new A();
a.publicI=100; //OK!
类A的私有实例字段privateI只能被自身的实例方法所使用:
public class A
{
public int publicI;
private int privateI;
protected int protectedI;
private void f()
{
privateI=100; //OK!
}
}
上述代码中,类A的私有方法f()访问了私有字段privateI。注意,只要是类A直接定义的实例方法,不管它是公有还是私有的,都可以访问类自身的私有实例字段。
(2)protected
在形成继承关系的两个类之间,可以定义一种扩充权限——protected。
当一个类成员被定义为protected之后,所有外界类都不可以访问它,但其子类可以访问。
以下代码详细说明了子类可以访问父类的哪些部分:
class Parent
{
public int publicField=0;
private int privateFiled=0;
protected int protectedField=0;
protected void protectedFunc()
{}
}
class Son:Parent
{
public void ChildFunc()
{
publicField=100; //正确!子类能访问父类的公有字段
privateFiled=200; //错误!子类不能访问父类的私有字段
protectedField=300; //正确!子类能访问父类的保护字段
protectedFunc(); //正确!子类能访问父类的保护字段
}
}
当创建子类对象后,外界可以访问子类的公有成员和父类的公有成员,如下所示:
Son obj=new Son();
//可以调用子类的公有方法
obj.ChildFunc();
//可以访问父类的公有字段
obj.publicField=1000;
由此可见,可以通过子类对象访问其父类的所有公有成员,事实上,外界根本分不清楚对象的哪些公有成员来自父类,哪些公有成员来自子类自身。
小结一下继承条件下的类成员访问权限:
① 所有不必让外人知道的东西都是私有的。
② 所有需要向外提供的服务都是公有的。
③ 所有的“祖传绝招”,“秘不外传”的都是保护的。
(3)internal
C#中还有一种可访问性,就是由关键字internal所决定的“内部”访问性。
internal有点像public,外界类也可以直接访问声明为internal的类或类的成员,但这只局限于同一个程序集内部。
读者可以简单地将程序集理解为一个独立的DLL或EXE文件。一个DLL或EXE文件中可以有多个类,如果某个类可被同一程序集中的类访问,但其他程序集中的类不能访问它,则称此类具有internal访问性。
例如类库项目ClassLibrary1可以生成一个独立的程序集(假定项目编译后生成ClassLibrary1.DLL),其中定义了两个类A和B:
namespace ClassLibrary1
{
internal class A
{
internal int InternalI=0;
}
public class B
{
public void f()
{
A a=new A(); //OK!
a.InternalI=100; //OK!
}
}
}
由于类B和类A属于同一个程序集,所以,B中的代码可以创建A的对象,并访问A的声明为internal的成员InternalI。
在程序集ClassLibrary1.DLL之外,外界只能创建声明为public的类B的对象,不能创建声明为internal的类A的对象。
internal是C#的默认可访问性,这就是说,如果某个类没有任何可访问性关键字在它前面,则它就是internal的。
比如上面的类A也可以写成:
class A
{
internal int InternalI=0;
}
它完全等同于
internal class A
{
internal int InternalI=0;
}
但要注意,在类中,如果省略成员的可访问性关键字,则默认为private的。
例如:
class A
{
int InternalI=0;
}
相当于
internal class A
{
private int InternalI=0;
}
使用场合 | C#2.0 | Visual Basic 2005 | 说明 |
Type(指类,接口等类型) | public | Public | 访问不受限制 |
internal | Friend | 访问范围仅限于同一程序集 | |
Member(指类型中的成员,比如类中的字段) | public | Public | 访问不受限制 |
internal | Friend | 访问范围仅限于同一程序集 | |
protected | protected | 访问范围仅限于自己或派生出来的子类型 | |
protected internal | Protected Friend | 在同一程序集内访问不受限制,在不同程序集中仅由此类型派生出来的子类型可访问 | |
private | Private | 仅自己可以访问 |