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

面向对象的C#语言

4. 修饰符

访问控制符

访问控制符 同类中 相同程序集的
子类
相同程序集的
非子类
不同程序集的
子类
不同程序集的
非子类
public
protected internal
protected
internal
private
  • 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
    • 使用枚举