VB.net 2010 视频教程 VB.net 2010 视频教程 VB.net 2010 视频教程
当前位置:
主页 > 编程开发 > c#教程 >
  • c#教程之面向对象的C# 编程

  • 2016-04-14 06:58 来源:未知

面向对象的C# 编程

与C++一样,C# 是一种面向对象的编程语言,它通过类、结构和接口来支持对象的封装、继承和多态等特征,还利用委托和事件来支持对象的消息响应。

17.3.1 对象和类型

与C++一样,C# 也有两种主要的对象类型——类和结构。其中类是一种完整的对象类型,结构可以视为是一种轻型的类。与C++不同的是C# 中的结构不支持继承(所以它不具备完整的对象特征),而且结构在C# 中是一种值类型(C# 中的类则是一种引用类型)。
与C++/CLI一样,C# 中的类和结构都不支持多重类继承,但是都支持多重接口实现。

1.类

类类型定义包含数据成员、函数成员和嵌套类型的数据结构,其中数据成员包括常量和字段,函数成员包括方法、属性、事件、索引器、运算符、实例构造函数、析构函数和静态构造函数。类类型支持继承,继承是派生类可用来扩展和专门化基类的一种机制。
1)类声明
C# 语言的类(class)声明完整格式如下(其中,灰色方括号“[]”内为可选内容、省略号“…”表示重复、圆括号内的竖线(或)分隔项“( * | * | * )”表示多中选一):
[[属性]] [类修饰符] [partial] class 标识符 [<类型参数[, …]>] [(: 基类 | : 接口类型
              [, …] | : 基类, 接口类型[, …] ] [where 类型参数 : 类型参数约束[, …] …] {
       [类成员声明 ……]
}[;]
其中:
l  属性(attributes)——用方括号括起的由程序自己定义和使用的额外说明信息,可以附加到各种程序实体上,并可在运行时获取这些信息。例如:
[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute: Attribute {
    public HelpAttribute(string url) {
       this.url = url;
    }
    public string Topic = null;
    private string url;
    public string Url {
       get { return url; }
    }
}
l  类修饰符(class-modifiers)有(粗体为C# 特有的):
new(新的)——用于在嵌套类中掩藏继承的同名成员。
n  public(公用的)——可以在任何地方使用,不受访问限制。
n  protected(保护的)——只能被同一类和派生类中成员使用,派生类的父程序(集)不会影响保护访问。
internal(内部的)——内部类只能被本程序(集)访问。
n  private(私有的)——只能被同一类中成员使用。
abstract(抽象的)——抽象类不允许实例化。
sealed(密封的)——密封类不允许派生。
n  static(静态的)——静态类没有也不允许有实例构造函数。
功能与C++/CLI的可见性说明符类似,只是增加了static修饰符。
l  partial(分部的)——用于定义分部类、结构和接口等对象类型,即将它们的定义分成多个部分,保存到多个代码文件中。这是C# 2.0新引进的功能,用于解决大型对象类型的定义问题,因为C# 不支持类体之外的方法和函数体定义。
l  class(类)——定义类的关键字。
l  标识符(identifier)——类的名称。
l  类型参数(type-parameter)——用于泛型类。
l  基类(base calss)——唯一父类的名称。默认基类为object。
l  接口类型(interface-type)——C# 支持多重接口的实现。
l  where(位于)——引导类型参数约束子句,对类型参数列表中的参数进行联系约束。类型参数约束具体包括:class、struct、类类型名、接口类性名、类型参数名、new ()(构造函数约束)。例如:
class Dictionary<K, V> // 定义字典泛型类
    where K: IComparable<K> // 键必须是可以比较的类型
    // 映射值必须是可打印、有键提供器和可创建的
    where V: IPrintable, IKeyProvider<K>, new() {
    ……
}
l  类成员的种类有:constant-declaration(常量声明)、field-declaration(字段声明)、method-declaration(方法声明)、property-declaration(属性声明)、event-declaration(事件声明)、indexer-declaration(索引器声明)、operator-declaration(运算符声明)、constructor-declaration(构造函数声明)、finalizer-declaration(终结器声明,即析构函数定义)、static-constructor-declaration(静态构造函数声明)、type-declaration(类型声明)。
l  ;(分号)——类体后的分号是可选的,在C# 中一般不用分号,分号是为了与C++兼容而保留的。而在C++必须使用分号,则是为了在类型声明后可同时定义该类型的实例对象(列表)。但是在C# 中,已经不允许这样做了,所以这里的分号已经是多余的了。
2)类成员
类中的数据和方法称为类成员(class members)。类中除了成员外,还可以包含嵌套的类和结构等类型。
(1)访问修饰符
类成员可以有如下访问修饰符(access modifiers):
l  public(公用的)——访问无限制。
l  protected(保护的)——只可被包含类(containing class)或其派生的类型访问。
internal(内部的)——只能被此程序访问。
protected internal(保护内部的)——只能被此程序或其包含类所派生的类型访问。
l  private(私有的)——只能被其包含类访问,为默认的。
C# 类的public、protected、private等成员修饰符,每次只能修饰一个成员,直接位于成员声明的开始处,而且没有冒号分隔符。而不是像C++那样,一个修饰符可以修饰位于其后(直到下一个修饰符为止)的若干成员。
(2)数据成员
类的数据成员(data member)有:
l  字段(field)——类变量。声明格式为:
[ [属性] ] [字段修饰符] 类型 标识符[ = 表达式 或 数组初始化器][, ……];
这里的字段修饰符有:new(新)、public(公用)、protected(保护)、internal(内部)、private(私有)、static(静态)、readonly(只读)、volatile(易变),及它们的有效组合。其中,C# 新引进了如下三种:
n  只读字段(readonly field)——可以在程序运行时由构造函数初始化的常量字段(而普通常量只能在编译时初始化)。
n  新字段(new field)——用于覆盖基类的同名变量。
n  内部字段(internal field)——只能被本程序访问的字段。
l  常量(constant)——用const关键字定义的类常量。声明格式为:
[ [属性] ] [常量修饰符] const标识符[ = 常量表达式] [,……] ;
其中,常量修饰符有:new、public、protected、internal、private及它们的有效组合。
类的数据成员可以是静态数据(使用了static修饰符),也可以是实例数据(未使用static修饰符)。静态数据为类的所有实例对象所共有,而实例数据则每个实例对象都自己的副本。常量默认是(总是)静态的。
字段还可以是只读的(使用了readonly关键字,这是C# 新增的)和易变的(使用了volatile关键字,字段值一般位于CPU中的寄存器中,高效但是有效期短暂)。
(3)函数成员
类的函数成员(function member)有:
l  方法(method)——类的成员函数。声明格式为:
[ [属性] ] [方法修饰符] 返回类型 成员名 [<类型参数, …>] (形参, …) [where 类型参数 : 类型参数约束[, …] ……] {……};
方法修饰符有:new(新)、public、protected、internal、private、static、virtual(虚)、sealed(密封)、override(重写)、abstract(抽象)、extern(外部)、partial(分部),及它们的有效组合。成员名可以是标识符或接口类型.标识符。其中,C# 新增了如下5个方法修饰符:
n  新方法(new method)——隐藏继承的同名方法。
n  密封方法(sealed method)——不能被派生类重写。
n  重写方法(override method)——覆盖基类的虚方法。
n  抽象方法(abstract method)——没有函数体的方法,需要在派生类中实现后才能使用。
n  分部方法(partial method)——在一个分部类中定义,而在另一个分部类中实现的方法。分部方法不能定义访问修饰符,隐含为private。分部方法的返回类型必须是void,而且partial关键字必须紧靠void之前出现才有效。
与C++不同的还有,C# 方法的输入参数,如果是参数的引用,不再使用&符号,而是改用ref关键字;另外,C# 还增加了形参的输出修饰符out,标明该参数用于方法的输出,可以未初始化,但是在方法中必须进行有意义的赋值。例如:
int f(ref int i, out int j) {i++; j = i + 100;}
l  属性(property)——提供对对象或类的特性(characteristic)进行访问的成员。声明格式为:
[ [属性] ] [属性修饰符] 类型 成员名{
       [ [属性] ] [访问修饰符] get {……};
       [ [属性] ] [访问修饰符] set {……};
}
其中,属性修饰符与方法修饰符相同,访问修饰符有protected、internal、private、protected internal和internal protected,与类成员的普通访问修饰符相比,没有public、多了internal protected(内部保护的)。
l  事件(event)——定义该类可生成的通知(notifications)。在发生某些行为时,可让对象通知程序。客户程序可以调用事件处理程序代码来响应该事件。声明格式为:
[ [属性] ] [事件修饰符] event 类型 标识符[ = 表达式 或 数组初始化器][, ……];
或:
[ [属性] ] [事件修饰符] event 类型 成员名 {
       [ [属性] ] add {……}
       [ [属性] ] remove {……}
}
其中,事件修饰符与方法修饰符相同。
l  索引器(indexer)——允许对象像数组一样被索引的一种成员。声明格式为:
[ [属性] ] [索引器修饰符] 类型 成员名{
       [ [属性] ] [访问修饰符] get {……};
       [ [属性] ] [访问修饰符] set {……};
}
其中,索引器修饰符与方法修饰符类似,只是少了static。
l  用户定义的运算符(operator)——运算符重载函数。声明格式为:
n  一元运算符:
[ [属性] ] [运算符修饰符] 类型 operator 一元运算符 (类型 标识符} {……};
可重写的一元运算符有:+、-、!、~、++、--、true、false。其中true和false必须同时重载。
n  二元运算符:
[ [属性] ] [运算符修饰符] 类型 operator 二元运算符 (类型 标识符, 类型 标识符} {……};
可重写的一元运算符有:+、-、*、/、%、&、|、^、<<(右移)、==、!=、>、<、>=、<=。
n  转换运算符:
[ [属性] ] [运算符修饰符] implicit operator 类型 (类型 标识符} {……};
[ [属性] ] [运算符修饰符] explicit operator 类型 (类型 标识符} {……};
其中,运算符修饰符有:public、static、extern,及它们的有效组合。
与C++相比,C# 不能重载new、=、+=、-=、*=、/=、%=、^=、&=、|=、>>(左移)、>>=、<<=、&&、||、,、[]、()(->、->*、new[]、delete、delete[])等运算符。但是却可以重载true、false和隐式与显式强制转换运算符。
l  构造函数(constructor)——包括实例构造函数和静态构造函数。
n  实例构造函数(instance constructor)——实现初始化一个类实例所需的行为,会在创建每个类实例时被系统调用。注意:因为C# 会自动给字段初始化,所以构造函数在C# 中的作用没有在C++的大。格式为:类型名([参数列表]);
注意:即使调用没有任何参数的默认构造函数,也不能省去后面的圆括号“()”,不然是语法错误。例如:A a = new A();
n  静态构造函数(static constructor)——实现初始化一个类所需的行为,会在程序装入时被系统调用。为类编写无参数的具体构造函数是C# 的一个新特征,主要用于类的静态字段或属性的运行时初始化。格式为:
static 类型名([参数列表]);
l  终结器(finalizer)——在C# 的旧版本中叫析构函数(destructor),但由于引用类型的对象是位于具有垃圾内存自动回收功能的CLR堆上,所以C# 中的析构函数的功能与C++的很不一样。为了与C++的析构函数有所区别,在C# 的新版本中,改叫终结器。格式(与C++的析构函数相同)为:~类型名();。

2.结构

C# 中的结构(struct)与C++中的类似,也可以封装数据和函数(属性和行为),是一种轻量级的类。但是它与C# 的(位于CLR堆中的)类(为引用类型)不同,它是一种(位于局部栈中的)值类型。
1)结构与类的区别
C# 中的结构与类的区别有:
l  结构是值类型,而类是引用类型。
l  结构是密封的(sealed),因此不能被继承。
l  结构不能继承类和其他的结构。
l  结构隐式地继承了System.ValueType类型。
l  结构的(无参数)默认构造函数不能被自定义的构造函数取代。
l  结构的自定义的构造函数,必须初始化结构中全部成员的值。
l  结构没有析构函数。
l  不允许初始化结构的字段,但是可以初始化结构的常量成员。
2)结构声明
结构的完整声明格式为:
[ [属性] ] [结构修饰符] [partial] struct 标识符 [<类型参数列表>] [: 结构接口列表] [类型参数约束子句] {
       [结构成员声明 ……]
}[;]
其中,结构的修饰符与枚举修饰符相同:new、public、protected、internal、private,但是与类的有所不同:因为C# 的结构不支持继承,所以没有类的sealed和abstract修饰符;也没有static修饰符;还与C++一样,结构的默认修饰符为public(类的默认修饰符为private)。
结构成员声明与类的基本相同,只是没有finalizer-declaration(终结器声明,即析构函数定义)。
C# 的结构和类一样,其public、protected、private等成员修饰符,每次只能修饰一个成员,也是直接位于成员声明的开始处,而且也没有冒号分隔符。而不是像C++那样,一个修饰符可以修饰位于其后(直到下一个修饰符为止)的若干成员。例如:
struct Point { // 定义结构
    public int x, y;
    public Point(int x, int y) {
       this.x = x;
       this.y = y;
    }
}
// 使用结构
Point a = new Point(10, 10);
Point b = a;
a.x = 100;
System.Console.WriteLine(b.x);
与枚举类型一样,结构可以作为顶层类型(这点与C++一致),也可以作为类的成员(嵌套类型,这一点与C++不同),但是不能作为局部类型来定义(这点与C++一致)。还有一个与C++不同点是,在C# 的结构类型定义体后的分号是可选的,而在C++中是必须的。

3.分部类和静态类

分部类和静态类都是C# 所特有的,而且都是C# 2.0版新增加的。前者允许将一个类、结构或接口的定义,放到多个文件中;后者则是仅包含静态成员的类。
1)分部类
分部类和结构是在类和结构的声明中,使用了partial修饰符的类和结构。位于不同文件中的同一个分部类,必须位于同一个命名空间或包含类型(对嵌入式分部类)之中,而且在所有类修饰符必须完全一致。对泛型类,还要求声明中的类型参数和对类型参数的约束也完全一致。但是类的属性、基类和实现接口的列表却可以不同。另外,分部类也可以用作嵌套类,不过其包含类必须也是分部类。
编译时,编译器会合并位于不同文件中的同一个分部类的XML注释、属性、基类、接口和成员。例如:
// Customer1.cs
[CustomAttribute]
public partial class Customer : BaseClass, IInterface1
{
    private int id;
    private string name;
    private string address;
    private List<Order> orders;
    public Customer() {
       ……
    }
}
// Customer2.cs
[AnotherAttribute]
public partial class Customer : IInterface2
{
    public void SubmitOrder(Order orderSubmitted) {
       orders.Add(orderSubmitted);
    }
    public bool HasOutstandingOrders() {
       return orders.Count > 0;
    }
}
编译时会合并成:
[CustomAttribute]
[AnotherAttribute]
public class Customer : BaseClass, IInterface1, IInterface2
{
    private int id;
    private string name;
    private string address;
    private List<Order> orders;
    public Customer() {
       ……
    }
    public void SubmitOrder(Order orderSubmitted) {
       orders.Add(orderSubmitted);
    }
    public bool HasOutstandingOrders() {
       return orders.Count > 0;
    }
}
2)静态类
静态类是在类的声明中,使用了static修饰符的类。静态类中只能包含静态成员,可以用静态构造函数来初始化这些静态成员。静态类是密封的(不能派生其他类),静态类不能被实例化,也不能包含实例构造函数。
静态类一般用于定义公用的常数和方法(如.NET中的Math类),使用时不需要创建实例,直接用“类名.字段”或“类名.方法”即可。例如:
public static class Math{……}
……
double s = Math.PI * r * r;
int x = Math.Min(x1, x2) * Math.Abs(y);

4.object与System.Object

C# 中的关键字object是.NET的System.Object类的别名,它是所有C# 类型和.NET类型的根基类(ultimate base class)。在声明时,没有给出基类的类,C# 编译器都自动假定其派生于Object类。
System.Object类是.NET框架中所有类的最终基类,是类型层次结构的根。它支持.NET框架类层次结构中的所有类,并为派生类提供低级别服务。下面是Object类的定义:
[SerializableAttribute]
[ComVisibleAttribute(true)]
[ClassInterfaceAttribute(ClassInterfaceType.AutoDual)]
public class Object
表17-14是Object类的成员列表。
表17-14  Object类的成员
分类 名称 说明
公共构造函数 Object() 初始化Object类的新实例。
公共方法 public static bool Equals (Object objA [, Object objB]) 已重载。确定两个Object实例是否相等。
public virtual int GetHashCode () 用作特定类型的哈希函数,适合在哈希算法和数据结构(如哈希表)中使用。
public Type GetType () 获取当前实例的类型。
public static bool ReferenceEquals (Object objA, Object objB) 确定指定的Object实例是否是相同的实例。
public virtual String ToString () 返回表示当前Object的String对象。
受保护的方法 Finalizer(终结):~Object () 允许Object在“垃圾回收”回收Object之前尝试释放资源并执行其他清理操作。
protected Object MemberwiseClone () 创建当前Object的浅表副本。
 

17.3.2 接口与继承

继承(inheritance)是面向对象的基本特征,所有的C# 类和结构都是直接或间接地从类Object派生的。C# 中没有明确指出基类的类,都以Object作为其默认基类。C# 中的结构不支持继承,但是C# 支持类的单一继承(single inheritance),多重继承的问题(与Java一样)需通过接口来解决。

1.接口

接口(interface,界面)的概念来源于组件编程的思想。接口定义统一的公用方法的原型,但是不包含方法的具体实现代码,方法体(代码)由实现接口的类来定义(编写)。
所以接口定义的是一种契约(contract),实现该接口的类则遵守(adhere)此契约。接口相当于组件的界面,供使用组件的用户查询和调用,而实现接口的类则是组件的编码模块本身。
在C# 中,一个接口可以继承自多个接口,一个类也可以实现多个接口。实现某个接口的类,必须实现该接口中的所有方法。
C# 中的接口声明格式为:(与类声明很相似)
[[属性]] [接口修饰符] [partial] interface 标识符 <类型参数, …> [: 基接口, …] [where 类型参数 : 类型参数约束[, …] ……] {
       [接口成员声明 ……]
}[;]
其中:
l  接口修饰符有:new、public、protected、internal、private。
l  接口成员声明包括:接口方法声明、接口属性声明、接口事件声明和接口索引声明。
这些声明中都没有方法体,例如:
public delegate void StringListEventHandler(
       IStringList sender, ListEventArgs e);
public interface IStringList {
    void Add(string s);
    int Count { get; }
    event StringListEventHandler Changed;
    string this[int index] { get; set; }
}
接口声明中其余项的含义与类的相同,这里就不再赘述了。

2.继承的种类

C# 中的继承有两种类型:
l  实现继承(类继承)——派生类型继承了基类型的所有成员,包括方法、属性、事件和索引的实现代码,派生类既可以直接调用基类的各种函数成员的实现代码,也可以自己编写代码来覆盖或重写它们,还可以为派生类添加新的数据和函数成员。
l  接口继承(非实现继承)——派生类型只继承了基类型中的函数签名(signature of a function),包括函数名、类型参数的个数、每个形参(formal parameter)的类型和种类(kind,值、引用、输出),从左到右的顺序,但是不包括函数的返回类型、参数名称、类型参数的名称、以及可对最右边的参数指定的参数修饰符。而没有继承基类型中的实现代码。接口继承主要用于指定派生类应该具有什么特性和功能。接口继承相当于接口的实现。
C++只支持实现继承,VB则只支持接口继承,C++/CLI、Java和C# 都既支持实现继承,也支持接口继承,所以非常适用于编写基于组件的分布式软件。
C# 中的结构,支持(多重)接口继承,但是不支持实现继承。即,你可以在结构中实现多个接口中定义的函数成员,但是你却不能从类或结构来派生新的结构,也不能从结构派生新的类。不过,用户定义的结构,总是自动派生于System.ValueType抽象类,还可以派生于(实现)多个接口。
C# 中的类,则支持单一实现继承和多重接口继承。

3.方法的继承

C# 中方法的继承关系非常多样,除了传统C++的虚函数(C# 增加了override修饰符)和纯虚函数(即C# 的abstract抽象函数)外,还增加了隐藏方法(new)、密封方法(sealed)和基类方法的调用指令(base.方法名)等诸多新方式。
1)虚方法override
虚函数是面向对象编程中实现运行时多态性的标准途径,而编译时的多态性则是由具有同样函数名,但是函数签名不同的多个重载(overload)函数来实现的。
与C++不同的是,在C# 中重写基类的虚方法时,必须使用C# 特有的重写修饰符override(覆盖)。例如:
class MyBaseClass {
    public virtual int VirtualMethod() {……}
}
class MyDrivedClass : MyBaseClass {
    public override int VirtualMethod() {……}
}
2)隐藏方法new
在C++中,不需要添加任何修饰符,就可以任意重载或覆盖基类的虚函数和非虚函数,但是在C# 中,这样做会得到一个编译警告。目的是,为了避免程序员的笔误,也为了适应代码的维护与升级。解决办法是,上面介绍过的使用override修饰符来明确重载基类的虚方法,以及使用new修饰符来显式隐藏(覆盖)基类中具有同样签名的(非虚)方法。例如:
class HisBaseClass {
    public int GroovyMethod() {……}
}
class MyDrivedClass : HisBaseClass {
    public new int GroovyMethod () {……}
}
3)基类方法调用base.方法名
在C++中,没有直接调用基类函数的方法。但是在C# 中,这可以用“base.方法名”的方式来完成,这一点与Java中的super的功能类似。例如:
base. GroovyMethod(); // 在MyDrivedClass中调用HisBaseClass类的同名方法
4)抽象方法abstract
在C++中,利用“=0”的纯虚函数,表示该函数没有函数体定义,需要在派生类中自己编写。含有纯虚函数的类,叫做抽象类,不能被实例化。
在C# 中,则直接用关键字abstract(抽象)来定义抽象方法和抽象类。例如:
abstract class A {
    public abstract int f ( );
}
5)密封方法sealed
C# 增加的sealed(密封的)关键字,用于修饰方法和类,表示继承来的虚方法不能被其派生类再重写和类不能被继承。分别被称为密封方法和密封类,其中密封方法必须同override一起使用。例如:
using System;
class A {
    public virtual void F() {
       Console.WriteLine("A.F");
    }
    public virtual void G() {
       Console.WriteLine("A.G");
    }
}
class B: A {
    public sealed override void F() {
       Console.WriteLine("B.F");
    }
    public override void G() {
       Console.WriteLine("B.G");
    }
}
class C: B {
    public override void F() { // 错误,不能覆盖密封方法
       Console.WriteLine("C.F");
    }
    public override void G() {
       Console.WriteLine("C.G");
    }
}
又例如:
sealed class A {}
class B: A {} // 错误,不能从密封类派生

4.修饰符

在C# 的类型和成员的声明中,使用了大量的修饰符,其中有许多是C# 特有的。下面将它们分类汇总,并进行一些简单的比较和分析。其中用粗体表示的修饰符是C++中没有的。
1)类型可见性修饰符与成员访问修饰符——参见表17-15。
表17-15  C# 的类型可见性与成员访问修饰符
修饰符 应用对象 说明
public 所有的类型和成员 任何代码都可以访问。
protected 类型和内嵌类型的成员 只有其派生的类型能访问。
internal 类型和内嵌类型的成员 只能在包含它的程序集中访问。
private 所有的类型和成员 只能在它所属的类中访问。
protected internal 类型和内嵌类型的成员 只能在包含它的程序集和其派生的类型中访问。
 
 
2)其他修饰符——参见表17-16。
表17-16  C# 的其他修饰符
修饰符 应用对象 说明
new 函数成员 隐藏继承的同签名的函数成员。
static 所有成员 不在类型实例上执行。
virtual 仅类的函数成员 可由派生类重写。
abstract 类和函数成员 只定义了签名没有实现代码。
override 函数成员 重写继承的虚拟或抽象函数成员。
sealed 类和函数成员 类不能被继承和方法不能被重写。
extern 仅静态的DllImport方法 成员在外部用另一种语言实现。
 

17.3.3 委托与事件

为了避免C++直接使用函数指针所带来的副作用,C# 用委托来封装事件处理函数的指针,还用委托来定义对象触发的事件。

1.委托

C# 和.NET的委托(delegate)与C/C++中的回调函数类似,但是委托比函数指针更安全。因为回调函数只是一个指向存储函数体的内存指针,无法知晓函数的形参和返回类型,也不能保证指针所指的内容一定是所需要的函数体。
当需要将函数作为参数来传递时,才需要函数指针和委托。主要用于启动线程、编写通用类库和事件处理。
C# 中委托的声明格式为:
[[属性]] [委托修饰符] delegate 返回类型 标识符 [<类型参数, …>] (形参列表) [where 类型参数 : 类型参数约束[, …] ……];
其中,委托修饰符有:new、public、protected、internal、private,及它们的有效组合。与常量修饰符的相同。例如:
delegate int D1(int i, double d);
class A {
    public static int M1(int a, double b) {…}
}
class B {
    delegate int D2(int c, double d);
    public static int M1(int f, double g) {…}
    public static void M2(int k, double l) {…}
    public static int M3(int g) {…}
    public static void M4(int g) {…}
    delegate object D3(string s);
    public static object M5(string s) {…}
    public static int[] M6(object o) {…}
}
委托的使用方法参见下例:
delegate void D(int x);
class Test {
    public static void M1(int i) {…}
    public void M2(int i) {…}
}
class Demo {
    static void Main() {
       D cd1 = new D(Test.M1); // 静态方法
       Test t = new Test();
       D cd2 = new D(t.M2); // 实例方法
       D cd3 = new D(cd2); // 另一个委托
    }
}

2.匿名方法

除了利用方法签名来定义委托之外,在C# 中还可以通过匿名方法(anonymous method)来使用委托。所谓匿名方法,是用作委托参数的一个代码块(代替原来用作定义委托实例时的参数的处理函数[体])。其定义格式为:
delegate [([形参列表])] {……}
例如:
using System;
delegate int D();
class Test {
    static D F() {
       int x = 0;
       D result = delegate { return ++x; };
       return result;
    }
    static void Main() {
       D d = F();
       Console.WriteLine(d());
       Console.WriteLine(d());
       Console.WriteLine(d());
    }
}
输出结果为:
1
2
3

3.λ表达式

从C# 3.0起,可以使用Lambda(λ)表达式来将实现代码赋予委托。委托参数可用λ表达式代替,(有参数的)匿名方法也可以改用λ表达式。
λ表达式的格式(单参数&多处理语句与多参数&单处理语句的格式可类推):
1)  单参数&单处理语句:(类型 参数) => 处理语句; 或:参数 => 处理语句;
例如:
delegate bool Filter(int i);
void F()
{
int max =5;
Filter f = (int n) => n < max; // 或Filter<int> f = n => n < max;
DoWork(f);
}
 
2)  多参数&多处理语句:{类型 参数, 类型 参数, ……) => {处理语句; 处理语句; ……; return 返回值;}
例如:
Func<int, int> lmbd = {int x, int y} => {x++; y--; return x*y;}

Func<int, int> lmbd = {x, y} => {x++; y--; return x*y;}
 

4.事件

事件(event)被广泛用于对象间的通信,例如Windows的应用程序就是基于消息机制的。C# 事件除了表示发生了某个有意义的事件外,还表示已经定义了一个可以处理通知的对象(委托)。
事件的定义分为两步:先定义一个委托类型(包含在事件触发时将要调用的方法),再通过C# 的关键字event用相关委托声明该事件。
事件的两种声明格式在前面的类成员说明中已经给出。
即平凡事件格式:
[[属性]] [事件修饰符] event 委托类型 标识符[ = 表达式 或 数组初始化器][, ……];
或非平凡事件格式:
[[属性]] [事件修饰符] event 委托类型 事件名 {
       [[属性]] add {……} // 对应于add_事件名 += ……
       [[属性]] remove {……} // 对应于remove_事件名 -= ……
}
其中,事件修饰符与方法修饰符的相同。
例如:
class X
{
private D __Ev; // Field to hold the delegate
public event D Ev {
add {
/* Add the delegate in a thread-safe way */
}
remove {
/* Remove the delegate in a thread-safe way */
}
}
}
又例如:
class Control: Component
{
// Unique keys for events
static readonly object mouseDownEventKey = new object();
static readonly object mouseUpEventKey = new object();
// Return event handler associated with key
protected Delegate GetEventHandler(object key) {...}
// Add event handler associated with key
protected void AddEventHandler(object key, Delegate handler) {...}
// Remove event handler associated with key
protected void RemoveEventHandler(object key, Delegate handler) {...}
// MouseDown event
public event MouseEventHandler MouseDown {
add { AddEventHandler(mouseDownEventKey, value); }
remove { RemoveEventHandler(mouseDownEventKey, value); }
}
// MouseUp event
public event MouseEventHandler MouseUp {
add { AddEventHandler(mouseUpEventKey, value); }
remove { RemoveEventHandler(mouseUpEventKey, value); }
}
// Invoke the MouseUp event
protected void OnMouseUp(MouseEventArgs args) {
MouseEventHandler handler;
handler = (MouseEventHandler)GetEventHandler(mouseUpEventKey);
if (handler != null)
handler(this, args);
}
}
例如(鼠标单击事件Click):
// 定义
public delegate void EventHandler(object sender,
       System.EventArgs e);
public class Button {
    public event EventHandler Click;
    public void Reset() {
       Click = null;
    }
}
// 使用
using System;
public class Form1 {
    public Form1() {
       // 为Button1的Click事件添加事件处理程序Button1_Click
       Button1.Click += new EventHandler(Button1_Click);
    }
    Button Button1 = new Button();
    void Button1_Click(object sender, EventArgs e) {
       Console.WriteLine("Button1 was clicked!");
    }
    public void Disconnect() {
       Button1.Click -= new EventHandler(Button1_Click);
    }
}

复习思考题

1.         给出OOP的英文原文、中文译文和三个基本特征。
2.         C、C++、Java、C# 分别面向什么编程?它们之间有什么关系?
3.         C和C++语言分别是谁、在什么时间、为什么目的发明的?
4.         C和C++语言有哪些国际标准?
5.         如何定义C++的命名空间?什么是无名的命名空间?
6.         如何使用C++的命名空间?三种使用方法各有什么特点和适用范围?
7.         Java语言最初是为什么应用设计的?它有哪些设计目标和关键特性?
8.         Java语言有哪些主要优势和缺点?它适用于什么编程?
9.         Java语言是如何实现跨平台和安全性的?
10.     C# 和.NET的总设计师是谁?他以前还开发过哪些著名软件?
11.     C# 语言的设计目标有哪些?
12.     C# 的主要特点是什么?
13.     C# 编程有哪些优势?
14.     C# 2.0、3.0和4.0的主要改进分别是什么?
15.     如何创建Visual C# 的控制台应用程序?
16.     C# 有头文件吗?C# 程序的入口函数是什么?放在什么地方?
17.     与C++相比,C# 增加了哪两种新注释格式?它们有什么用处?
18.     逐字符‘@’有什么用处?
19.     可空类型符‘?’有什么用处?
20.     C# 的类型分成哪两大类?列举一些各类的典型具体类型。
21.     C# 中有联合类型union吗?
22.     给出装箱和拆箱的英文单词和用处。
23.     C# 的字符、整数和实数类型与C++的什么不同?
24.     C# 的decimal类型用多少字节存储?主要用于什么?其常量后缀是什么字母?
25.     C# 增加哪些运算符?主要用于什么方面?
26.     C# 中的模运算%与C++的有什么不同?
27.     C# 中的枚举与C++的有哪些不同?
28.     C# 中的数组定义和创建与C++的有哪些不同?
29.     在C# 中怎样获取数组的大小和维数?
30.     C# 中有全局的变量和常量吗?
31.     C# 中的switch语句与C++的有什么不同?
32.     与C++相比,C# 增加了什么循环?
33.     C# 中的命名空间的定义与使用与C++的有什么不同?
34.     与C++相比,C# 语言增加了哪些新关键字?它们各有什么用处?
35.     与C++相比,C# 语言删除了哪些关键字?为什么?
36.     C# 中类的声明(定义)格式与C++的有哪些不同?
37.     与C++相比,C# 语言增加了哪些新的类修饰符?它们各有什么用处?
38.     与C++相比,C# 语言增加了哪些新的类成员的访问修饰符?它们各有什么用处?使用方法有哪些不同?
39.     partial类和方法是什么版本引进的?有什么用处?
40.     C# 类的数据成员和函数有哪些?增加了哪些修饰符?它们各有什么用处?
41.     C# 中的属性(property)是什么?有什么用处?如何声明(定义)?
42.     与Java相比,C# 保留了C++的部分指针和运算符重载功能?为什么?与C++的有什么区别?
43.     为什么C# 的类没有析构函数而只有终结器?
44.     C# 的结构和类有哪些区别?
45.     为什么Java和C# 都要去掉类的多重继承?类似的功能如何实现?
46.     为什么Java和C# 都要引入接口类型?它与C++中的什么类型类似?
47.     什么是实现继承和接口继承?VB、C++、Java和C# 分别支持什么继承?
48.     什么是静态类?有什么用处?如何定义和使用?
49.     C# 和.NET中的委托与C/C++中的什么类似?如何什么声明和使用委托?
50.     事件与委托有什么关系?如何定义和使用事件处理程序?

练习题

1.         (Hello例)实现17.2.1中的第一个C# 程序HelloWorld。
2.         (选做题,例子)实现本章中的其他例子。
(大作业选题,OOP)探讨OOP语言,对比
相关教程
关于我们--广告服务--免责声明--本站帮助-友情链接--版权声明--联系我们       黑ICP备07002182号