C#程序设计.唐大仕.04.C#高级语言特性
C#高级语言特性
1. 委托
- delegate
- 大致上:委托 \(\approx\) 函数指针
委托是对函数原型的包装
- 委托的声明
1 | public delegate double MyDelegate(double x); |
- 委托的实例化
1 | // 可以是对象的某个方法, 也可以是类的某个静态方法 |
- 委托的调用
- 委托变量名(参数列表)
1 | d2(8.9); |
一个例子
1 | // 声明一个委托 |
- C# 4 以上版本定义了很多的委托
- 例如
1 | // 没有返回值, 封装的委托从 0 个参数到 16 个参数 |
- 我们可以对上面的例子做如下修改
1 | // ... |
委托的合并
- 委托的合并----多播MultiCastDelegate
- 一个委托实例中可以 “包含” 多个函数
- 调用委托,就是调用其中多个函数
- 多个函数间的先后顺序是没有意义的
- 返回值也就没有太多意义
- 运算符:
+
、-
、+=
、-=
- 动态地增减其中的函数
- 提高了程序的灵活性
- 例子
委托的转换与相等
委托的转换
- 按声明的名称判断
- 以下两个不能互相转换或加减
1
2delegate void D(int a);
delegate void E(int a);委托的相等
- 按内容(即其中“包含的函数”)来判断
- 有点点像两个字符串的 “相等” 与否的判断
一些测试
1 | D cd1 = new D(C.M1); |
总结
- 委托相当于函数指针
- 但它类型更安全,是引用类型
- 且功能更强大,有多播功能
2. 事件
- 大致上:事件 \(\approx\) 回调函数
GUI 中的事件
1 | this.button1.Click += new System.EventHandler(this.button1_Click); |
自定义事件
- 事件的声明
1 | public event 委托名 事件名; |
- 事件的注册与移除
- 事件名
+=
或-=
- 在事件所在类的外面,只能用以上两个运算符
- 事件名
- 事件的发生(激发)
- 事件名(参数列表)
- 相当于回调所注册的函数
实例
定义及使用事件的 6 步曲
- 声明事件参数类:
class xxxEventArgs{}
- 声明委托:
delegate void xxxEventHandler(obj, args)
- 定义事件:
public event 类型 名称
- 发生事件:
事件名(参数)
- 定义一个方法:
void 方法名(obj, args)
- 注册事件:
xxx.事件+= new 委托(方法名)
事件与委托的关系
事件有点像委托类型的实例
- 事件一定有相关的委托类型
- 与委托实例一样,事件也“包含”多个函数
- 事件的运算受更多限制(在类外只能用
+=
或-=
)
事件比委托实例更复杂
- 可以定义事件存取器
1
2
3
4修饰符 event 委托类型名 事件名 {
add{e += value; }
remove{ e -= value; }
}
事件总结
- 事件是一种消息机制
- 事件源调用事件,别的类注册事件
- 事件的类型是一个委托
3. lambda 表达式
- C# 语言新特性
- C#2.0 引入泛型
- C#3.0 引入 Lambda 及 Linq
- C#4.0 更多的动态特性 dynmaic
泛型
- Generic
1 | List<Book> books = new List<Book>(); |
匿名方法
1 | delegate(参数){ 方法体; } |
- 可以当一个匿名方法
1 | new Thread(new ThreadStart(delegate(){ |
- 可以被隐式转换为一个兼容的委托类型
- 省略
ThreadStart
- 省略
1 | new Thread(delegate() { |
lambda 表达式
- 相当于匿名方法的简写
- 省略 delegate,甚至省略参数类型
1 | (参数) => { 语句或表达式; } |
- 例子
1 | new Thread(()=>{ |
1 | button1.Click += (sender,e) => { |
lambda 表达式 vs 匿名方法
- lambda 表达式比匿名函数简单
- 匿名函数多一个功能:
- 不写
(参数)
的匿名函数,可以转成任意多个参数的委托
- 不写
Linq
- LINQ:Language Integrated Query
- 完整例子
1 | List<Program> programs = new List<Program>(); |
总结
- 匿名函数使用
delegate
- Lambda表达式使用
=>
- Linq 使用
from
,where
,select
- 对比代码
4. 运算符重载
- 运算符重载有一些限制
- 如成对(true/false),如类型要求,如有的不能重载
- 运算符的声明
1 | // 一元运算符 |
5. 异常处理
异常
1 | try { |
- 不管是否有异常,都会执行到
finally
- 即使在
try
中return
了,也会执行finally
- 代码
- 即使在
System.Exception
类
1 | // 方法 |
系统内部异常类
1 | System.OutOfMemoryException |
捕获和处理异常
1 | try{ |
catch{}
表示捕获所有种类的异常
1 | try { |
抛出异常
1 | if(xxxxxx) { |
自定义异常类
- 从
Exception
或ApplicationException
继承 - 重抛异常
1 | throw; |
- 异常链接
1 | throw new Excepiton("msg", e); |
- 这里 e 称为内部异常
- InnerException 属性
- 使得外部能进一步知道内部的异常原因
算术溢出与 checked
- 对溢出进行检查
- 对整个程序 csc /checked
- 对部分程序
- 针对表达式: checked(表达式) 及 uncheckd(表达式)
- 针对块语句: checked{...} 及 uncheckd{...}
- 对溢出异常进行捕获
- try{ ... } catch( OverflowException e ) { ... }
6. Attribute
- 用在类上
1 | [ ] |
- 用在方法上的
1 | [ ] |
1 | [ ] |
- Attribute 是与类、结构、方法等元素相关的额外信息,是对元信息的扩展
- 通过Attribute可以使程序、甚至语言本身的功能得到增强
使用系统定义的 Attribute
- 使用 Attribute 的一般方式
- 在程序集、类、域、方法等前面用[]表示
- 可以省略“Attribute”几个字母,只写xxxxx
- 可以带参数
- 位置参数 (相当于构造方法带的参数)
- 命名参数(域名或属性名=值)
- 示例
- 在Main()方法使用[STAThread]
- 在结构上、枚举上使用:StructLayout,Flag
- 在程序集级别应用Attribute
- [assembly: AssemblyCompany("")]
自定义Attribute
- 声明 Attribute 类
- 从 System.Attribute 继承而来
- 名字要用 xxxxAttribute
- 使用 Attribute 类
- 在类及成员上面使用方括号
- 可以省略后缀 Attribute
- 通过反射访问属性
- 实例
7. C# 语言中的其他成分
编译预处理
- 标识符声明
1 | // 定义一个标识符 |
- 条件处理
1 |
- 信息报告
1 |
- 行号标记
1 |
unsafe 及指针
- unsafe
- 用于修饰类、方法等
- fixed 及指针
- fixed(类型 * 指针名 = 表达式) 语句
- sizeof运算符
- sizeof(简单或结构类型名)
- stackalloc
- 在栈上分配的内存,而不是在堆上,因此不会担心内存被垃圾回收器自动回收。
- 示例代码
1 | class FileStream: Stream { |
- UnsafeCopy.cs
- unsafe 关键字需要在编译的时候加上
/unsafe
- unsafe 关键字需要在编译的时候加上
1 | // 编译时需要: /unsafe |
其他关键字
- lock
- 多线程程序中,lock可以将某个对象加锁
- volatile
- 随时可能被程序以外的其他因素所修改
- 域被 volatile 修饰时,会阻止编译器对它的优化
8. 程序的组织
- 名字空间:程序的逻辑组织
- 嵌套类型:类中嵌套类型
- 程序集:程序的物理组织
名字空间
名字空间的概念
- 逻辑划分;避免名字冲突
名字空间的声明
- 可嵌套
1 | namespace xxx.xxxx { } |
- 名字空间的导入
1 | using xxx.xxxx; |
- 使用别名
1 | using 别名 = 名字空间或类名; |
嵌套类型
- 嵌套类型的概念
- 类型中的类型
1 | class A { |
- 嵌套类型的可访问性
- 受各个层次的限制
程序集
- 模块(module)
- 程序集(assembly)
- exe、dll
- VS 生成一个程序集
- 新建项目的时候指定为类库
- 在 VS 上引用程序集
- 在项目上点右键,添加引用
编译示例
1 | csc /target:mod /out:Add.mod Add.cs |
9. 语法总结
类型声明
- 类型声明是 C# 程序的主体,它可以位于名字空间中,也可以是嵌套的类型
- 类型声明包括以下几种:
- 类 class
- 结构 struct
- 接口 interface
- 枚举 enum
- 委托 delegate
类的成员
- 常数(const)
- 它代表了与类相关的常数数据
- 域(field)
- 它是类中的变量
- 方法(method)
- 它实现了可以被类实现的计算和行为
- 属性(property)
- 它定义了命名的属性和与对这个属性进行读写的相关行为
- 事件(event)
- 它定义了由类产生的通知
- 索引(indexer)
- 它允许类的实例通过与数组相同的方法来索引
- 运算符(operator)
- 它定义了可以被应用于类的实例上的表达式运算符
- 实例构造函数(instance constructor)
- 它执行需要对类的实例进行初始化的动作
- 析构函数(destructor)
- 类的实例被清除时实现的动作(结构不能有析构函数)
- 静态构造函数(static constructor)
- 它执行对类本身进行初始化的动作
- 类型(type)
- 它代表位于类中的类型