事件是 C# 语言内置的语法, 可以定义和处理事件, 为使用组件编程提供了良好的基础.
1.16.1 事件驱动
Windows 操作系统把用户的动作都看作消息, C# 中称作事件, 例如用鼠标左键单击按钮, 发出鼠标单击按钮事件. Windows 操作系统负责统一管理所有的事件, 把事件发送到各个运行程序. 各个程序用事件处理函数响应事件, 这种方法也叫事件驱动.
C# 语言使用组件编制 Windows 应用程序. 组件本质上是类. 在组件类中, 预先定义了该组件能够响应的事件, 以及对应的事件函数 (不是程序员编制的事件处理函数), 该事件发生, 将自动调用相应的预定义事件函数. 例如, 按钮类中定义了单击事件 Click 和单击事件函数. 一个组件可能定义了多个事件, 应用程序中不必也没必要响应所有的事件, 而只需响应其中很少事件, 程序员编制相应的事件处理函数, 用来完成需要响应的事件所应完成的功能. 现在的问题是, 第一, 如何把程序员编制的事件处理函数和组件类中预先定义的事件函数联系起来. 第二, 如何使不需响应的事件无动作. 这是本节要解决的问题.
1.16.2 事件的声明
在 C# 中, 事件首先代表事件本身, 例如按钮类的单击事件, 同时, 事件还是代表类型引用变量, 可以代表程序员编制的事件处理函数, 把事件和事件处理函数联系在一起. 下面的例子定义了一个 Button 组件, 这个例子不完整, 只是说明问题. 实际在 C# 语言类库中已预定义了 Button 组件, 这里的代码只是想说明 Button 组件中是如何定义事件的. 例子如下:
- public delegate void EventHandler(object sender,EventArgs e);// 代表类型声明
- //EventHandler 变量可以代表没有返回值, 参数为 (object sender,EventArgs e) 的函数
- public class Button:Control// 定义一个按钮类 Button 组件
- {
- ...// 按钮类 Button 其它成员定义
- public event EventHandler Click;// 声明一个事件 Click, 是代表类型引用变量
- protected void OnClick(EventArgs e)//Click 事件发生, 自动触发 OnClick 方法
- {
- if(Click!=null)// 如果 Click 已代表了事件处理函数, 执行这个函数
- Click(this,e);
- }
- public void Reset()
- {
- Click=null;
- }
- }
在这个例子中, Click 事件发生, 应有代码 (未列出) 保证自动触发 OnClick 方法. Click 是类 Button 的一个事件, 同时也是代表类型 EventHandler 的引用变量, 如令 Click 代表程序员编制的事件处理函数, Click 事件发生时, 将执行程序员编制的事件处理函数, 完成程序员希望完成的工作. 如果 Click 为 null,OnClick 函数将不做任何事情. 下节将介绍用何种语句使 Click 代表程序员编制的事件处理函数.
1.16.3 事件的预订和撤消
在随后的例子中, 我们声明了一个使用 Button 类的登录对话框类, 对话框类含有两个按钮: OK 和 Cancel 按钮.
- public class LoginDialog: Form// 登录对话框类声明
- {
- Button OkButton;
- Button CancelButton;
- public LoginDialog()// 构造函数
- {
- OkButton=new Button();// 建立按钮对象 OkButton
- // 下条语句令 Click 代表 OkButtonClick 方法, 注意 += 的使用
- OkButton.Click+=new EventHandler(OkButtonClick);
- CancelButton=new Button();// 建立按钮对象 CancelButton
- CancelButton.Click += new EventHandler(CancelButtonClick);
- }
- void OkButtonClick(object sender, EventArgs e)// 程序员编制的事件处理函数
- {
- ...// 处理 OkButton.Click 事件的方法, 完成程序员希望完成的工作
- }
- void CancelButtonClick(object sender, EventArgs e)
- {
- ...// 处理 CancelButton.Click 事件的方法, 完成程序员希望完成的工作
- }
- }
在例子中建立了 Button 类的两个实例, 单击按钮事件 Click 通过如下语句和事件处理方法联系在一起: OkButton.Click+=new EventHandler(OkButtonClick), 该语句的意义是使 OkButton.Click 代表事件处理方法 OkButtonClick, 这样只要 Click 事件被触发, 事件处理方法 OkButtonClick 就会被自动调用. 撤消事件和事件处理方法 OkButtonClick 的联系采用如下语句实现: OkButton.Click-=new EventHandler(OkButtonClick), 这时, OkButton.Click 就不再代表事件处理方法, Click 事件被触发, 方法 OkButtonClick 就不会被调用了. 务必理解这两条语句的用法. 使用 Visual Studio.NET 集成环境可以自动建立这种联系, 在自动生成的代码中包括这两条语句.
1.3 索引指示器
在 C# 语言中, 数组也是类, 比如我们声明一个整型数组: int[] arr=new int[5], 实际上生成了一个数组类对象, arr 是这个对象的引用 (地址), 访问这个数组元素的方法是: arr[索引], 在数组类中, 使用索引访问元素是如何实现的呢? 是否可以定义自己的类, 用索引访问类中的数据成员? 索引指示器 (indexer) 为我们提供了通过索引方式方便地访问类的数据成员的方法.
首先看下面的例子, 用于打印出小组人员的名单:
- using System
- class Team
- {
- string[] s_name = new string[2];// 定义字符串数组, 记录小组人员姓名
- public string this[int nIndex]// 索引指示器声明, this 为类 Team 类的对象
- {
- get// 用对象名 [索引] 得到记录小组人员姓名时, 调用 get 函数
- {
- return s_name[nIndex];
- }
- set// 用对象名 [索引] 修改记录小组人员姓名时, 调用 set 函数
- {
- s_name[nIndex] =value;//value 为被修改值
- }
- }
- }
- class Test
- {
- public static void Main()
- {
- Team t1 = new Team();
- t1[0]="张三";
- t1[1]="李斯";
- Console.WriteLine("{0},{1}",t1[0], t1[1]);
- }
- }
显示结果如下: 张三, 李斯
1.4 命名空间
一个应用程序可能包含许多不同的部分, 除了自己编制的程序之外, 还要使用操作系统或开发环境提供的函数库, 类库或组件库, 软件开发商处购买的函数库, 类库或组件库, 开发团队中其它人编制的程序等. 为了组织这些程序代码, 使应用程序可以方便地使用这些程序代码, C# 语言提出了命名空间的概念. 命名空间是函数, 类或组件的容器, 把它们按类别放入不同的命名空间中, 命名空间提供了一个逻辑上的层次结构体系, 使应用程序能方便的找到所需代码. 这和 C 语言中的 include 语句的功能有些相似, 但实现方法完全不同.
1.18.1 命名空间的声明
用关键字 namespace 声明一个命名空间, 命名空间的声明要么是源文件 using 语句后的第一条语句, 要么作为成员出现在其它命名空间的声明之中, 也就是说, 在一个命名空间内部还可以定义命名空间成员. 全局命名空间应是源文件 using 语句后的第一条语句. 在同一命名空间中, 不允许出现同名命名空间成员或同名的类. 在声明时不允许使用任何访问修饰符, 命名空间隐式地使用 public 修饰符. 例子如下:
- using System;
- namespace N1//N1 为全局命名空间的名称, 应是 using 语句后的第一条语句
- {
- namespace N2// 命名空间 N1 的成员 N2
- {
- class A// 在 N2 命名空间定义的类不应重名
- {
- void f1(){
- };
- }
- class B
- {
- void f2(){
- };
- }
- }
- }
也可以采用非嵌套的语法来实现以上命名空间:
- namespace N1.N2// 类 A,B 在命名空间 N1.N2 中
- {
- class A
- {
- void f1(){
- };
- }
- class B
- {
- void f2(){
- };
- }
- }
也可以采用如下格式:
- namespace N1.N2// 类 A 在命名空间 N1.N2 中
- {
- class A
- {
- void f1(){
- };
- }
- }
- namespace N1.N2// 类 B 在命名空间 N1.N2 中
- {
- class B
- {
- void f2(){
- };
- }
- }
1.18.2 命名空间使用
如在程序中, 需引用其它命名空间的类或函数等, 可以使用语句 using, 例如需使用上节定义的方法 f1() 和 f2(), 可以采用如下代码:
- using N1.N2;
- class WelcomeApp
- {
- A a=new A();
- a.f1();
- }
using N1.N2 实际上是告诉应用程序到哪里可以找到类 A. 请读者重新看一下 1.2.1 节中的例子.
1.5 非安全代码
在 C 和 C++ 的程序员看来, 指针是最强有力的工具之一, 同时又带来许多问题. 因为指针指向的数据类型可能并不相同, 比如你可以把 int 类型的指针指向一个 float 类型的变量, 而这时程序并不会出错. 如果你删除了一个不应该被删除的指针, 比如 Windows 中指向主程序的指针, 程序就有可能崩溃. 因此滥用指针给程序带来不安全因素. 正因为如此, 在 C# 语言中取消了指针这个概念. 虽然不使用指针可以完成绝大部分任务, 但有时在程序中还不可避免的使用指针, 例如调用 Windows 操作系统的 API 函数, 其参数可能是指针, 所以在 C# 中还允许使用指针, 但必须声明这段程序是非安全 (unsafe) 的. 可以指定一个方法是非安全的, 例如: unsafe void F1(int * p){...}. 可以指定一条语句是非安全的, 例如: unsafe int* p2=p1; 还可以指定一段代码是非安全的, 例如: unsafe{ int* p2=p1;int* p3=p4;}. 在编译时要采用如下格式: csc 要编译的 C# 源程序 /unsafe.
来源: http://www.bubuko.com/infodetail-3073747.html