C#学习笔记【继承】

- C# 继承

继承是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。

当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类。

继承的思想实现了 属于(IS-A) 关系。例如,哺乳动物 属于(IS-A) 动物,狗 属于(IS-A) 哺乳动物,因此狗 属于(IS-A) 动物。

- 基类和派生类

一个类可以派生自多个类或接口,这意味着它可以从多个基类或接口继承数据和函数。

C# 中创建派生类的语法如下:

1
2
3
4
5
6
7
8
<访问修饰符> class <基类>
{
...
}
class <派生类> : <基类>
{
...
}

假设,有一个基类 Shape,它的派生类是 Rectangle:

实例

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
30
31
32
33
34
35
36
37
38
39
40
41
using System;
namespace InheritanceApplication
{
class Shape
{
public void setWidth(int w)
{
width = w;
}
public void setHeight(int h)
{
height = h;
}
protected int width;
protected int height;
}

// 派生类
class Rectangle: Shape
{
public int getArea()
{
return (width * height);
}
}

class RectangleTester
{
static void Main(string[] args)
{
Rectangle Rect = new Rectangle();

Rect.setWidth(5);
Rect.setHeight(7);

// 打印对象的面积
Console.WriteLine("总面积: {0}", Rect.getArea());
Console.ReadKey();
}
}
}

当上面的代码被编译和执行时,它会产生下列结果:

总面积: 35

- 基类的初始化

派生类继承了基类的成员变量和成员方法。因此父类对象应在子类对象创建之前被创建。您可以在成员初始化列表中进行父类的初始化。

下面的程序演示了这点:

实例

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System;
namespace RectangleApplication
{
class Rectangle
{
// 成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
public double GetArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("长度: {0}", length);
Console.WriteLine("宽度: {0}", width);
Console.WriteLine("面积: {0}", GetArea());
}
}//end class Rectangle
class Tabletop : Rectangle
{
private double cost;
public Tabletop(double l, double w) : base(l, w)
{ }
public double GetCost()
{
double cost;
cost = GetArea() * 70;
return cost;
}
public void Display()
{
base.Display();
Console.WriteLine("成本: {0}", GetCost());
}
}
class ExecuteRectangle
{
static void Main(string[] args)
{
Tabletop t = new Tabletop(4.5, 7.5);
t.Display();
Console.ReadLine();
}
}
}

当上面的代码被编译和执行时,它会产生下列结果:

长度: 4.5
宽度: 7.5
面积: 33.75
成本: 2362.5

- C# 多重继承

多重继承指的是一个类别可以同时从多于一个父类继承行为与特征的功能。与单一继承相对,单一继承指一个类别只可以继承自一个父类。

C# 不支持多重继承。但是,您可以使用接口来实现多重继承。下面的程序演示了这点:

实例

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System;
namespace InheritanceApplication
{
class Shape
{
public void setWidth(int w)
{
width = w;
}
public void setHeight(int h)
{
height = h;
}
protected int width;
protected int height;
}

// 基类 PaintCost
public interface PaintCost
{
int getCost(int area);

}
// 派生类
class Rectangle : Shape, PaintCost
{
public int getArea()
{
return (width * height);
}
public int getCost(int area)
{
return area * 70;
}
}
class RectangleTester
{
static void Main(string[] args)
{
Rectangle Rect = new Rectangle();
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// 打印对象的面积
Console.WriteLine("总面积: {0}", Rect.getArea());
Console.WriteLine("油漆总成本: ${0}" , Rect.getCost(area));
Console.ReadKey();
}
}
}

当上面的代码被编译和执行时,它会产生下列结果:

总面积: 35
油漆总成本: $2450

- override 与 new 对父类virtual方法使用区别

“public override void ParVirMethod()” 表示重写父类的虚方法,即子类1对父类的ParVirMethod方法进行了重写。

而”public new void ParVirMethod()” 表示在子类2中定义了一个与父类同名的方法,但是不是重写父类的虚方法,而是隐藏父类的虚方法。

在调用ParVirMethod()时,子类1会调用自己重写的方法,输出”子类1的方法…”,而子类2会调用自己隐藏的方法,输出”子类2的方法…”。

所以重要区别是,重写是针对父类的虚方法的,而隐藏则是在子类中定义一个同名方法,与父类的虚方法无关。

实例

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
namespace LearningProject
{
class Program
{
/// <summary>
/// 父类
/// </summary>
public class ParentClass
{
public virtual void ParVirMethod()
{
Console.WriteLine("父类的方法...");
}
}

/// <summary>
/// 子类1
/// </summary>
public class ChildClass1 : ParentClass
{
public override void ParVirMethod()
{
Console.WriteLine("子类1的方法...");
}
}

/// <summary>
/// 子类2
/// </summary>
public class ChildClass2 : ParentClass
{
public new void ParVirMethod()
{
Console.WriteLine("子类2的方法...");
}

public void Test()
{
Console.WriteLine("子类2的其他方法...");
}
}

static void Main(string[] args)
{
ChildClass1 re1 = new ChildClass1();
ChildClass2 re2 = new ChildClass2();
re1.ParVirMethod();
re2.ParVirMethod();
re2.Test();
Console.ReadKey();
}
}
}

运行结果:

子类1的方法...
子类2的方法...
子类2的其他方法...

- 访问隐藏的基类成员

如果想要使得派生类能够完全访问被隐藏的继承成员,就可以使用基类访问表达式访问被隐藏的继承成员。

基类访问表达式由关键字base后面跟一个点和成员的名称组成。例如:

1
Console.WriteLine("{0}",base.Field1);

上面一行代码中的 base.Fields1 就属于基类访问。

实例

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

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


class Program
{
static void Main()
{
OtherClass oc = new OtherClass(); //实例化
oc.PrintFields1(); //执行oc的PrintFields1()方法
}
}

以上代码就是访问基类隐藏成员的实例,运行的结果是输出:

Fields -- In the derived class
Fields -- 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
using System;

namespace ConsoleApp1
{
class Program
{
class father
{
public father()
{
Console.WriteLine("father");
}
}
class son : father
{
public son()
{
Console.WriteLine("son");
}
}
static void Main(string[] args)
{
son s = new son();
Console.ReadKey();
}
}
}

输出结果:

father
son