类继承

通过继承可以定义一个新类,新类纳入一个已经声明的类并进行扩展。已存在的类称为基类(base class),新类称为派生类(derived class)。

  • 派生类扩展它的基类,因为它包含了基类的成员,还有派生类本身声明中的新增功能。
  • 派生类不能删除它所继承的任何成员。
1
2
3
4
class OtherClass : SomeClass
{
//...
}

访问继承的成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class SomeClass    //基类
{
public string Field1 = "base class field ";
public void Method1( string value ) {
Console.WriteLine($"Base class -- Method1: { value }");
}
}

class OtherClass: SomeClass { //派生类
public string Field2 = "derived class field";
public void Method2( string value ) {
Console.WriteLine($"Derived class -- Method2: { value }");
}
}

class Program {
static void Main() {
OtherClass oc = new OtherClass();

oc.Method1( oc.Field1 ); //以基类字段为参数的基类方法
oc.Method1( oc.Field2 ); //以派生字段为参数的基类方法
oc.Method2( oc.Field1 ); //以基类字段为参数的派生方法
oc.Method2( oc.Field2 ); //以派生字段为参数的派生方法
}
}
1
2
3
4
Base class -- Method1:    base class field
Base class -- Method1: derived class field
Derived class -- Method2: base class field
Derived class -- Method2: derived class field

所有类都派生自object类

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

没有基类规格说明的类隐式地直接派生自类 object。不加基类规格说明只是指定 object 为基类的简写。这两种形式是语义等价的。
class SomeClass { ... } 与 class SomeClass : object { ... } 语义等价。

  • 一个类声明的基类规格说明中只能有一个单独的类。这称为单继承。
  • 基类和派生类是相对的术语。所有的类都是派生类,要么派生自object,要么派生自其他的类。所以,通常称一个类为派生类时,我们的意思是它直接派生自某类而不是object
1
2
3
4
5
6
7
8
9
10
class SomeClass
{ ... }

class OtherClass: SomeClass
{ ... }

class MyNewClass: OtherClass
{
//...
}

屏蔽基类的成员

虽然派生类不能删除它继承的任何成员,但可以用与基类成员名称相同的成员来屏蔽(mask)基类成员。这是继承的主要功能之一,非常实用。

  • 要屏蔽一个继承的数据成员,需要声明一个新的相同类型的成员,并使用相同的名称。
  • 通过在派生类中声明新的带有相同签名的函数成员,可以屏蔽继承的函数成员。请记住,签名由名称和参数列表组成不包括返回类型
  • 要让编译器知道你在故意屏蔽继承的成员,可使用new修饰符。否则,程序可以成功编译,但编译器会警告你隐藏了一个继承的成员。
  • 也可以屏蔽静态成员。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class SomeClass    //基类
{
public string Field1 = "SomeClass Field1";
public void Method1(string value)
{
Console.WriteLine($"SomeClass.Method1: { value }");
}
}

class OtherClass : SomeClass //派生类
{
new public string Field1 = "OtherClass Field1"; //屏蔽基类成员
new public void Method1(string value) //屏蔽基类成员
{
Console.WriteLine($"OtherClass.Method1: { value }");
}
}

class Program
{
static void Main()
{
OtherClass oc = new OtherClass(); //使用屏蔽成员
oc.Method1(oc.Field1); //使用屏蔽成员
}
}
1
OtherClass.Method1: OtherClass Field1

基类的访问

如果派生类必须访问被隐藏的继承成员,可以使用基类访问(base access)表达式。基类访问表达式由关键字 base 后面跟着一个点和成员的名称组成。Console.WriteLine("{0}", base.Field1);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SomeClass {
public string Field1 = "Field1 -- In the base class";
}

class OtherClass : SomeClass {
public string Field1 = "Field1 -- In the derived class";
public void PrintField1()
{
Console.WriteLine(Field1); // 访问派生类
Console.WriteLine(base.Field1); // 访问基类
}
}

class Program {
static void Main()
{
OtherClass oc = new OtherClass();
oc.PrintField1();
}
}
1
2
Field1 -- In the derived class
Field1 -- In the base class

使用基类的引用

派生类的实例由基类的实例和派生类新增的成员组成。派生类的引用指向整个类对象,包括基类部分。
如果有一个派生类对象的引用,就可以获取该对象基类部分的引用(使用类型转换运算符把该引用转换为基类类型)。
类型转换运算符放置在对象引用的前面,由圆括号括起的要被转换成的类名组成。
将派生类对象强制转换为基类对象的作用是产生的变量只能访问基类的成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class MyBaseClass
{
public void Print()
{
Console.WriteLine("This is the base class.");
}
}

class MyDerivedClass : MyBaseClass
{
public int var1;

new public void Print()
{
Console.WriteLine("This is the derived class.");
}
}

class Program
{
static void Main()
{
MyDerivedClass derived = new MyDerivedClass();
MyBaseClass mybc = (MyBaseClass)derived;

derived.Print();
mybc.Print();
}
}
1
2
This is the derived class.
This is the base class.

虚方法和覆写方法

虚方法可以使基类的引用访问“升至”派生类内。
可以使用基类引用调用派生类的方法,只需满足下面的条件:

  • 派生类的方法和基类的方法有相同的签名和返回类型。
  • 基类的方法使用 virtual 标注。
  • 派生类的方法使用 override 标注。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class MyBaseClass
{
virtual public void Print()
{
Console.WriteLine("This is the base class.");
}
}

class MyDerivedClass : MyBaseClass
{
override public void Print()
{
Console.WriteLine("This is the derived class.");
}
}

class Program
{
static void Main()
{
MyDerivedClass derived = new MyDerivedClass();
MyBaseClass mybc = (MyBaseClass)derived;
derived.Print(); // 强制转换成基类
mybc.Print();
}
}
1
2
This is the derived class.
This is the derived class.
  • 覆写和被覆写的方法必须有相同的可访问性。如果被覆写的方法是 private 的,而覆写方法是 public 的,是不可以的。
  • 不能覆写 static 方法或非虚方法。
  • 方法、属性和索引器,以及另一种成员类型——事件,都可以被声明为 virtual 和 override

覆写标记为override的方法

覆写方法可以在继承的任何层次出现。

  • 当使用对象基类部分的引用调用一个被覆写的方法时,方法的调用被沿派生层次上溯执行,一直到标记为 override 的方法的最高派生(most-derived)版本。
  • 如果在更高的派生级别有该方法的其他声明,但没有被标记为 override,那么它们不会被调用。

公共部分(基类与派生类定义)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyBaseClass    // 基类
{
virtual public void Print()
{
Console.WriteLine("This is the base class.");
}
}

class MyDerivedClass : MyBaseClass // 派生类
{
override public void Print()
{
Console.WriteLine("This is the derived class.");
}
}

使用 override 的 SecondDerived时:
无论Print是通过派生类调用还是通过基类调用的,都会调用最高派生类中的方法。当通过基类调用时,调用沿着继承层次向上传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class SecondDerived : MyDerivedClass
{
override public void Print() {
Console.WriteLine("This is the second derived class.");
}
}

class Program
{
static void Main()
{
SecondDerived derived = new SecondDerived(); // 使用 SecondDerived
MyBaseClass mybc = (MyBaseClass)derived; // 使用 MyBaseClass

derived.Print();
mybc.Print();
}
}
1
2
This is the second derived class.
This is the second derived class.

使用 new 的 SecondDerived时:
当通过SecondDerived的引用调用方法Print时,SecondDerived中的方法被执行。
当通过MyBaseClass的引用调用Print方法时,方法调用只向上传递了一级,到达类MyDerived

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class SecondDerived : MyDerivedClass
{
new public void Print()
{
Console.WriteLine("This is the second derived class.");
}
}

class Program
{
static void Main()
{
SecondDerived derived = new SecondDerived(); // 使用 SecondDerived
MyBaseClass mybc = (MyBaseClass)derived; // 使用 MyBaseClass

derived.Print();
mybc.Print();
}
}
1
2
This is the second derived class.
This is the derived class.

覆盖其他成员类型

在属性、事件以及索引器上virtual/override用法也是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class MyBaseClass
{
private int _myInt = 5;
virtual public int MyProperty
{
get { return _myInt; }
}
}

class MyDerivedClass : MyBaseClass
{
private int _myInt = 10;
override public int MyProperty
{
get { return _myInt; }
}
}

class Program
{
static void Main()
{
MyDerivedClass derived = new MyDerivedClass();
MyBaseClass mybc = (MyBaseClass)derived;

Console.WriteLine( derived.MyProperty );
Console.WriteLine( mybc.MyProperty );
}
}
1
2
10
10

析构函数的执行

析构函数初始化语句

类访问修饰符

程序集间的继承

成员访问修饰符

访问成员的区域

公有成员的可访问性

私有成员的可访问性

受保护成员的可访问性

内部成员的可访问性

受保护内部成员的可访问性

成员访问修饰符小结

抽象成员

抽象类

抽象类和抽象方法的示例

密封类

静态类

扩展方法

命名约定