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

面向对象的C#语言

4. 修饰符

访问控制符

访问控制符 同类中 相同程序集的
子类
相同程序集的
非子类
不同程序集的
子类
不同程序集的
非子类
public \(\checkmark\) \(\checkmark\) \(\checkmark\) \(\checkmark\) \(\checkmark\)
protected internal \(\checkmark\) \(\checkmark\) \(\checkmark\) \(\checkmark\)
protected \(\checkmark\) \(\checkmark\) \(\checkmark\)
internal \(\checkmark\) \(\checkmark\) \(\checkmark\)
private \(\checkmark\)
  • protected 保证子类(逻辑),internal 保证相同程序集(物理)
  • 程序集:项目编译成同一个 exe 和 dll 文件,这一组文件称为一个程序集

static

  • static的字段、方法、属性是属于整个类的
  • static 方法中,不能访问实例变量
    • 例如不能使用 this
  • 调用 static 方法时,直接用类名访问
1
Console.WriteLine("")
  • static 变量可以用来表示全局变量
    • 定义一个类,里面的所有变量都是 static 的,当作全局变量使用
  • 在 c#2.0 中,类名也可以用 static 来修饰
    • 称为 static 类
    • 所有字段、方法都是 static 的
1
2
3
4
static class StaticClass {
public static int A;
// public int B; // error CS0708: "StaticClass.B": 不能在静态类中声明实例成员
}

static 构造方法

  • static 构造方法只会调用一次,但其调用时间是不确定的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;

class Person {
public static int TotalNum;
static Person() {
Console.WriteLine("Person: static constructor");
TotalNum = 100;
}

public Person() {
Console.WriteLine("Person: constructor");
}

public static void Main() {
Person p = new Person();
}
}

/* output
Person: static constructor
Person: constructor
*/

const 与 readonly

  • const 相当于静态常量
    • Math.PI
  • readonly 相当于不可改量,只能赋一次值
    • String.Empty
    • 在构造方法中赋值,或者在声明时就赋值
  • const 只能用于基本类型及 string
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;

class Person {
public string Name;
public Person(string name) {
this.Name = name;
}

public static void Main() {
Person p1 = new Person("a");
// const Person p2 = p1; // error CS0133: 指派给 "p2" 的表达式必须是常量

const string s1 = "string";
// s1 = "s"; // error CS0131: 赋值号左边必须是变量、属性或索引器
}
}
  • readonly 只能修饰字段,而 const 还可以修饰局部变量

sealed 和 abstract

  • sealed类,不可继承(也有利于编译优化)
    • 如 String Console Math Convert Graphics Font
  • abstract类,不可实例化(new)
    • 如 Array, RandomNumberGenerator
    • abstract 的方法体,不用{},用;
      • abstract 类型 方法名(参数列表);
      • abstract 类型 属性名{get;set;}
1
2
3
4
5
6
7
8
9
// 通过编译
abstract class AbstractClass {
public AbstractClass() {
Console.WriteLine("AbstractClass: constructor");
}
public abstract void fun1();
public void fun2() { }
public int A;
}

5. 接口

  • 接口实际上是一个约定
    • 如:ICloneable、IComparable
  • 接口是抽象成员的集合
    • ICloneable 含有方法 clone()
    • IComparable 含有方法 compare()
  • 接口是一个引用类型,比抽象类更抽象

接口能够帮助实现多重继承

  • 例子

接口的用处

  • 实现不相关类的相同行为
    • 不需要考虑这些类之间的层次关系
  • 通过接口可以了解对象的交互界面,而不需了解对象所对应的类
1
2
3
4
public sealed class String :
IComparable, ICloneable, IConvertible, IEnumerable {
// ...
}

定义一个接口

  • 所有的接口都是 public、abstract 的,但是不需要显式声明
    • 不能显式写出来
1
2
3
4
5
public interface IStringList {
void Add(string s);
int Count { get; }
string this[int index] { get;set; }
}

实现接口

1
2
3
4
5
6
class 类名 : [父类, ] 接口, 接口, ..., 接口 {
// 实现接口的方法
public 方法 () {
// ...
}
}

显式接口成员实现

  • 在实现多个接口时,如果不同的接口有同名的方法,
  • 为了消除歧义,需要在方法名前写接口名
1
2
3
void IWindow.Close () {
// ...
}
  • 调用时,只能用接口调用(强制类型转换)
1
((IWindow)f).Close();
  • 例子
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
using System;
class InterfaceExplicitImpl {
static void Main() {
FileViewer f = new FileViewer();
f.Test();
((IWindow)f).Close();
IWindow w = new FileViewer();
w.Close();
}
}

interface IWindow {
void Close();
}

interface IFileHandler {
void Close();
}

class FileViewer : IWindow, IFileHandler {
void IWindow.Close () {
Console.WriteLine( "Window Closed" );
}

void IFileHandler.Close() {
Console.WriteLine( "File Closed" );
}

public void Test() {
((IWindow)this).Close();
}
}

6. 结构及枚举

结构 struct

  • 结构常用来表示较简单的多个分量(字段)
  • Point、Color、Size、DateTime、Int32
1
2
3
4
5
public struct Int32 :
IComparable, IFormattable, IConvertible,
IComparable<int>, IEquatable<int> {
// ...
}
  • 可以有方法、属性等其他成员

结构的例子

1
2
3
4
5
6
7
8
9
10
struct Point {
public double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double R() {
return Math.Sqrt(x*x+y*y);
}
}

结构和类的区别

  • struct 是值类型
    • 结构不能包含无参数构造方法
    • 每个字段在定义时,不能给初始值
    • 构造方法中,必须对每个字段进行赋值
  • struct 是 sealed 的,不能被继承
  • 实例化时,使用 new,但与引用型变量的内存是不同的
1
Point p = new Point(100,80);
  • 值类型变量在赋值时,实行的是字段的 copy
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
using System;

struct MPoint {
// public double x = 1.0, y = 1.0;
// error CS0573: "MPoint": 结构中不能实例属性或字段初始值设定项

public double x, y;

// error CS0568: 结构不能包含显式的无参数构造函数
// public MPoint() {}

public MPoint(double x, double y) {
this.x = x;
// 如果注释掉上面一行的赋值语句则报错
// error CS0171: 在控制返回调用方之前,字段"MPoint.x"必须完全赋值
this.y = y;
}
}

// 接口列表中的类型 "MPoint" 不是接口
// error CS0527: struct MMPoint : MPoint {}

interface AA {}
// OK
struct MMM : AA {}

class TestStruct {
public static void Main() {
MPoint p1 = new MPoint(1.0, 1.0);
MPoint p2 = p1;
p1.x = 2.0;
Console.WriteLine("p1.x: " + p1.x);
Console.WriteLine("p2.x: " + p2.x);
}
}

/* output
p1.x: 2
p2.x: 1
*/

枚举 enum

  • 枚举实际上是有意义的整数
    • FontStyle、GraphicsUnit、KnownColor、DockStyle、DialogResult
  • 例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;

enum MColor {
Red,
Green = 1,
Blue = 2
}

class TestStruct {
public static void Main() {
Console.WriteLine(MColor.Red);
}
}

/* output
Red
*/
  • 用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 赋值
MColor c = MColor.Red;

// 比较
if(c == MColor.Red) {
// ...
}

// 选择
switch(c) {
case MColor.Red:
// ..
break;
// ...
}
  • 与字符串的转化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;

enum MColor {
Red,
Green = 1,
Blue = 2
}

class TestStruct {
public static void Main() {
MColor c = MColor.Red;
Console.WriteLine(c.ToString());
Console.WriteLine(
(MColor) Enum.Parse(typeof(MColor), "Red")
);
}
}

/* output
Red
Red
*/

结构和枚举比较

  • 结构主要用来表示多个分量
  • 枚举主要用来表示符号化的常量
  • 它们都是值类型

7. 面向对象编程

  • Object Oriented 方法的三大特点
    • 继承 inheritance
      • 子类继承父类的成员,还可增加、调用、隐藏
      • 提高软件模块的可重用性和可扩充性
    • 封装 encapsulation
      • 使用接口,而不关心具体的类
      • 使用属性,而将字段设为private
    • 多态 polymorphism
      • 相同的方法,不同的参数
      • 自动调用子类相应的方法(虚方法调用,以后讲)

8. UML 类图

VS 中的类关系图

  • 在项目上,点右键,”查看类关系图“
  • 添加新项,其他项,类关系图
    • 然后将相应的类文件拖动到该 “类关系图” 中
  • UML 工具
    • IBM Rational XDE for .NET
    • Borland Together
    • Microsoft Visio
  • 正向工程,由UML图自动产生代码
  • 反向工程,由代码自动产生UML图

9. 编程规范

  • 数字要有意义,不能天上掉下来
  • 常用手段
    • 使用变量 delta = 100
    • 使用常量 const int MAX_LEN = 1000
    • 使用系统常量 Math.PI
    • 使用枚举