C#程序设计.唐大仕.03.面向对象的C#语言

面向对象的C#语言

1. 类、字段、方法

  • 类(class)最基本的要素
    • 字段(field):变量
    • 方法(method): 函数
1
2
3
4
5
6
7
8
9
10
class Person {
public string name;
public int age;
public void SayHello() {
Console.WriteLine("Hello! My name is " + name );
}
public string GetInfo() {
return "Name: " + name + ", Age: " + age;
}
}

构造方法

  • 构造方法(constructor)
  • 构造方法的主要作用是完成对象的初始化工作
    • 构造方法的方法名与类名相同
    • 构造方法没有返回类型,也不能写 void
1
2
3
4
5
class Person {
public Person(string n, int a){
name = n; age = a;
}
}
  • 默认(default)构造方法
    • 如果用户没有定义任何构造方法,则系统会自动产生一个
1
public Person() {}
  • 构造方法不能显式地直接调用,而是用 new 来调用
1
Person p = new Person("Liming", 20 );

析构方法

1
2
3
class Person {
~Person() {}
}
  • 由于 C# 自动进行对象的释放,所以用户一般不定义析构方法

方法的重载

  • overloading
1
2
3
4
5
6
7
8
public void SayHello(){
Console.WriteLine("Hello! My name is " + name );
}

public void SayHello( Person another ){
Console.WriteLine("Hello," + another.name
+ "! My name is " + name );
}
  • 系统通过方法签名来区分
  • 方法签名:方法名参数个数及类型构成(参数名不算)

this

  • this指这个对象本身

  • 常用于

    • 访问这个对象的字段及方法
      • 用于 VS 自动补全
    • 区分字段与局部变量

    1
    2
    3
    4
    public Person(int age, string name){
    this.age = age;
    this.name = name;
    }

    • 用于构造方法调用另一个构造方法,注意其位置

    1
    2
    3
    public Person() : this( 0, "" ) {
    // 构造方法的其他语句;
    }

应用实例:银行系统

  • 可在 vs 中,添加 “ 类关系图 ”(类图)

2. 属性、索引

属性

使用属性

  • button1.Text = "说你好";
    • 含义相当于button1.SetText( “说你好”);
  • string s = button1.Text;
    • 含义相当于 s = button1.GetText();

定义属性

1
2
3
4
5
6
7
8
9
private string _name;
public string Name {
get {
return _name;
}
set {
_name = value; // value 关键字, 便是传进来的参数
}
}
  • C#3 之后可以简写
1
public string Name { set; get; }
一个例子
1
2
3
4
5
6
7
8
9
10
11
12
13
class Person {
public string Name {
set; get;
}
}

// 使用属性
class xxx {
public void xxx() {
Person a = new Person();
a.Name = "banbao";
}
}
  • 编译器对于上面的属性生成如下方法
    • 可以通过反汇编看到生成的方法
1
2
void set_Name(string value);
string get_Name();

属性和字段的对比

  • 属性实际上是方法

属性的优点

  • 可以只读或只写

    • 只读:只有 get,没有 set

    1
    2
    3
    4
    5
    class Person {
    public string Name {
    get;
    }
    }

    • 只写:只有 set,没有 get
  • 可以进行有效性检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person {
private string _name;
public string Name {
get {
return _name;
}
set {
if (value == "") {
Console.WriteLine("Name can not be an empty string!");
return;
}
_name = value;
}
}
}
  • 可以是计算得到的数据
1
2
3
4
5
public string Info{
get{
return "Name :" + Name + ",Age :" + Age;
}
}
  • 可以定义抽象属性

索引

  • string s="abcde";
  • 求出第0个字符:s[0]
    • 含义上相当于 s.Get(0)

定义索引

1
2
3
4
修饰符 类型名 this [参数列表] {
set {}
get {}
}

使用索引

  • 对象名[参数]
  • 编译器自动产生两个方法,以供调用
1
2
T get_Item(P);
void set_Item(P, T value);

课程示例

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
using System;
class IndexerRecord {
private string[] data = new string[6];
private string[] keys = {
"Author", "Publisher", "Title", "Subject", "ISBN", "Comments"
};
// 定义索引
public string this[int idx] {
set {
if (idx >= 0 && idx < data.Length) data[idx] = value;
}
get {
if (idx >= 0 && idx < data.Length) return data[idx];
return null;
}
}
// 两种方式访问
public string this[string key] {
set {
int idx = FindKey(key);
this[idx] = value;
}
get {
return this[FindKey(key)];
}
}
private int FindKey(string key) {
for (int i = 0; i < keys.Length; i++) {
if (keys[i] == key) {
return i;
}
}
return -1;
}

static void Main() {
IndexerRecord record = new IndexerRecord();
// 使用索引
record[0] = "马克-吐温";
record[1] = "Crox出版公司";
record[2] = "汤姆-索亚历险记";
Console.WriteLine(record["Title"]);
Console.WriteLine(record["Author"]);
Console.WriteLine(record["Publisher"]);
}
}

属性和索引

属性 索引器
通过名称标识 通过参数列表进行标识
通过简单名称来访问 通过 [] 运算符来访问
可以用 static 修饰 不能用 static 修饰
属性的 get 访问器没有参数 索引的 get 访问器具有与索引相同的参数
属性的 set 访问器包含隐式 value 参数 除了 value 参数外,
索引的 set 访问器还具有与索引相同的参数列表

3. 类的继承

  • 窗体程序
1
2
3
public class Form1 : System.Windows.Forms.Form {
// ...
}
  • 继承使用 : 表示
  • 子类 subclass、父类 baseclass
  • C# 中采用单继承
  • 所有的类都是通过直接或间接地继承 object(即System.Object)得到的
    • 如果一个类明显显式声明继承自哪一个类,系统认为其继承于 object
    • 默认有 ToString() 等方法
1
class SubClass : BaseClass {}
  • 子类自动地从父类那里继承所有的字段、方法、属性、索引器等成员作为自己的成员
  • 除了继承父类的成员外,子类还可以
    • 添加新的成员
    • 隐藏或修改父类的成员

字段的继承、添加与隐藏

1
2
3
4
5
6
7
8
9
10
class A {
public int a;
public int b;
}

class B : A {
// 继承字段 a
new public int b; // 隐藏
public int c; // 添加
}

方法的继承、添加

  • 方法的继承(自动)
  • 方法的添加(多定义一些方法)

与父类同名的方法

  • 一是定义同名、但参数列表(签名)与父类不同的方法,这称为对父类方法的重载(Overloading
  • 二是定义同名且参数列表也与父类相同的方法,这称为新增加一种方法 ,用 new 表示
  • 三是定义同名且参数列表也与父类相同的方法,而且父类的方法用了 abstract 或 virtual 进行了修饰,子类的同名方法用了 overridnewe 进行了修饰,这称为虚方法的覆盖(Overriding

base 关键字

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
using System;
class Base {
public int A;
public Base(int A) {
Console.WriteLine("Base: constructor!");
this.A = A;
}
public void SayHello() {
Console.WriteLine("Base: Hello!");
}
}

class Derived : Base {
public int B;
// 通过 base 关键字调用父类构造函数
public Derived(int A, int B): base(A) {
Console.WriteLine("Derived: constructor!");
this.B = B;
}

// new 关键字表示隐藏(否则会报警告)
new public void SayHello() {
base.SayHello(); // 通过 base 关键字调用父类方法
Console.WriteLine("Derived: Hello!");
}
public static void Main() {
Derived d = new Derived(0 ,0);
d.SayHello();
}
}


/* output
Base: constructor!
Derived: constructor!
Base: Hello!
Derived: Hello!
*/

父类与子类的转换

1
2
3
4
5
6
Person p1 = new Person();
Person p2 = new Student();
Student s1 = new Student();
Student s2 = new Student();

statement A;
  • 上述 statement A 分别为如下语句
1
2
3
4
5
6
7
p1 = s1; // 可以, 因为 Person 类型的变量可以引用 Student 对象

s2 = p1; // 不行, 因为会产生编译错误

s2 = (Student) p1; // 编译时可以通过, 运行时则会出现类型不能转换的异常

s2 = (Student) p2; // 正确,因为 p2 引用的正好是 Student 对象实例

as 运算符

  • 如果不能转换,则值为null
1
2
Student s3 = p1 as Student;  // 结果 s3 为 null
Student s4 = p2 as Student; // s4 被赋值
  • 与强制类型转换的差别
    • as 只能针对引用型变量
    • 如果不能转换,as 运算不会引起异常,只是值为 null

is 运算符

1
2
3
if(p is Person) {
// ...
}
  • 判断一个对象是不是某个类(及其子类)的实例

typeof 运算符

  • 变量则使用 GetType()
  • 类型则使用 typeof()
  • 返回类型为 System.Type
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
using System;

class Base { }

class Derived : Base {
public static void Main() {
Derived d1 = new Derived();
// Derived d2 = new Base(); // 编译错误
Base b1 = new Derived();
Base b2 = new Base();

Console.WriteLine("d1: " + d1.GetType());
// Console.WriteLine("d2: " + d2.GetType());
Console.WriteLine("b1: " + b1.GetType());
Console.WriteLine("b2: " + b2.GetType());

Console.WriteLine("");
Console.WriteLine(typeof(Base));
Console.WriteLine(typeof(Derived));
}
}

/* output
d1: Derived
b1: Derived
b2: Base

Base
Derived
*/

属性、索引的继承

  • 属性、索引也是可以继承的