C#学习笔记之三:C#接口与事件委托
2010-01-11 10:39 张智清 阅读(6434) 评论(0) 编辑 收藏 举报自从使用了Windows Live Writer如此方便之工具,终于不用进行繁复劳动整理文档更新日志了,先前就是怕麻烦,好多笔记就搁在电脑里了,今天在翻出一篇。
★ 接口就是语义相关的一个或多个抽象成员组成的命名集合。只有在接口被一个类或结构实现了,否则它们是不起任何作用的。
接口定义(声明):
★ 在C#中,接口使用interface关键字来定义。接口从不指定一个基类(甚至是System.Object),接口包含的成员不带访问修饰符。
★ 所有接口成员都隐式规定为公共的(public)。在接口中,所有方法都是抽象的。
★ 接口可由方法、属性、事件、索引器或这4种成员类型的任何组合构成。接口不能包含字段。
★ 类的属性和索引器可以为接口上定义的属性或索引器定义额外的访问器。例如,接口可以声明一个带有get访问器的属性(即只读属性),而实现该接口的类可以声明同时带有get和set访问器的同一属性。但是,如果属性或索引器使用显式实现,则访问器必须匹配。
接口的实现:
★ 接口的实现有两种:常规的不带接口名称的实现,也就是隐式实现;而为了解决命名冲突问题而在实现是带上接口名称的,则是显示实现。
★ 当类直接派生自System.Object时,简单地列出该类支持的接口即可,否则最直接的基类必须是列在冒号后面第一个成员。
★ 对于结构而言,由于结构总是派生自System.ValueType,所以仅需要在结构定义后面简单列出每个接口就行。
★ 一个类或结构实现接口的原则是:要么全要要么全不要。即所有在接口中定义的成员都必须在要实现它的类或结构中实现。
★ 任何基接口或父接口定义的方法都自动实现定义。
隐式实现接口:
★ 若要实现接口成员,类中的对应成员必须是公共的(public访问修饰符)、非静态的,并且与接口成员具有相同的名称和签名。
★ 隐式实现的接口成员无论是通过类实例或结构实例都可以访问,而且也可以通过接口实例来访问。
显示实现接口:
★ 使用显式接口实现是为了保证一个指定接口方法被限定在接口级别。且不能在定义显式实现成员时加访问修饰符,它总是(不带任何访问修饰符)隐式私有的。
★ 显示接口成员不能通过类实例或结构实例来调用,只允许通过接口实例(或通过强制转换提取接口)来访问。
显示实现益处1:隐藏代码的实现2:在使用接口访问的系统中,调用者只能通过接口调用而不是底层的类来访问。
接口的用途:
接口作为参数
★ 接口作为有效的.NET类型,当然可以构造将接口作为参数的方法。即可以直接将一个实现了该接口的类的实例对象传入。
接口作为返回值
★ 接口也可以被用来作为方法的返回值。这个方法可以检查对象是否支持该接口,如果是,则返回一个接口引用。
接口的其他认识:
★ 如何才能动态判断一个类型支持哪些接口呢?
A.显示强制转换
B.获取接口引用:
as关键字:如果该对象可被视为一个指定的接口,则as关键字得到指向该对象接口的引用,否则,将返回一个值为null的空引用。
is关键字:如果要考察的对象与指定接口不符,将返回false值。反而言之,如果该类型与指定接口相符,则可以安全调用接口成员。
★ 接口与抽象基类的对比:相同之处为,都是要全实现细节的;不同之处为,抽象基类不只能够定义一组抽象方法,还可以指定公用的、私有的和受保护的状态数据以及许多可被子类访问的实体方法。而接口纯粹是一组协议,它从不定义状态数据,从不提供方法实现。
★ 接口可以继承接口。抽象类可以实现(implements)接口,抽象类是否可继承实体类,但前提是实体类必须有明确的构造函数
构建可枚举类型(IEnumerable和IEnumerator)
★ GetEnumerator()方法返回一个对另一个接口System.Collections.IEnumerator的引用。这个接口提供了基础设施,调用方可以用来移动IEnumerable兼容容器包含的内部对象
★ GetEnumerator()被定义为公开的,对象用户可以与IEnumerator类型交互。
★ 在.NET1.1下,想让自定义集合支持类似foreach的枚举循环,必须实现IEnumerable接口(也可能是IEnumerator接口)。而.NET 2.0开始提供了通过迭代器(iterator)构建支持foreach循环的类型。
★ 迭代器指定了foreach处理容器内部项时该如何返回。迭代器方法必须命名为GetEnumerator(),返回值也必须为IEnumerator类型。但自定义类不需要再实现原来那些枚举接口了。
★ 关键字yield用来向调用方foreach构造指定返回值。当到达yield返回语句后,当前位置被存储下来,从上次迭代器被调用的位置重新开始执行。
构建可克隆的对象(ICloneable)
★ 要使自定义类型支持向调用方返回自身同样副本的能力,需要实现标准ICloneable接口。该类型定义了一个简单的方法Clone()。
★ 如有一个仅包含值类型的类或结构,使用MemberwiseClone()实现Clone()方法。如果有一个保存其他引用类型的自定义类型,需要建立一个新的类型以传入每个引用类型成员变量的说明。
构建可比较的对象(IComparable)
System.IComparable接口指定了一种允许一个对象可基于某些特定键值进行排序的行为。
public interface IComparable
{
int CompareTo(object o);
}
★ 构建自定义类型的时候,可以实现IComparable以使该类型数组可以被排序。
★ 充实CompareTo()的细节时需要决定排序操作的基准。
★ CompareTo()方法的逻辑是,根据某个特定数据字段比较传入的类型与当前实例,其返回值被用来判断这个类型小于、大于或是等于它所比较的对象。(返回值小于0,则这个实例在指定对象之前;返回值等于0,这个实例等于指定对象;返回值大于0,这个实例在指定对象之后。)
★ 指定多个排序顺序(IComparer):比较两个对象的通用方法,定义在System.Collections命名空间里。
interface IComparer
{
int Compare(object o1, object o2);
}
与IComparable接口不同,IComparer接口不是在要排序的类型中,而是在许多辅助类中实现的,其中每个排序各有一个依据。
System.Collections命名空间的接口
System.Array是最基础的容器构造,这个类提供了大量服务,例如逆转、排序、清除、枚举等。但它有许多限制,最显著的就是在添加或删除项时不能自动改变本身的大小。所以我们可以使用更强大的System.Collections命名空间里定义的类型。
★ 接口除了用于建立多态性以外,还可用来作为回调机制。这个技巧使对象可以使用一组普通的成员进行双向对话。
★ 事件接口并不直接由对接受事件感兴趣的对象实现,而是由一个名为接收器对象(sink object)的辅助对象实现的。
★ 在.NET Framework里,回调仍是可能的,它们的功能是由使用更为安全和面向对象的委托(delegate)来完成的。
.NET委托类型Delegate
函数指针是指向函数的指针变量。因此它首先应是指针变量,只不过该指针变量指向函数。函数指针有两个用途:调用函数和做函数的参数。
委托是“函数指针”在C#中的更好实现。因为委托是面向对象的,且是类型安全的。★ 一个委托类型表示了方法的签名(方法的参数类型及顺序),一个委托实例可以表示一个具体的方法,即某个类的实例方法或静态方法。
★ 委托类型包括3个重要信息:它所调用的方法的名称;该方法的参数(可选);该方法的返回值(可选)。
★ .NET委托既可以指向静态方法,也可以指向实例方法。(这与C或C++的函数指针不同。)
★ 委托的声明与方法的声明有些类似,这是因为委托本身就是为了进行方法的引用。只不过要清楚:委托是一种类型,而方法是类的成员。
★ 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
★ 使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时(这里用“调用”这个词,是因为此变量代表一个方法),可以依次调用所有绑定的方法。
★ 委托被创建并提供了上述信息后,它可以在运行时调用其指向的方法。每个委托都被自动赋予同步或异步访问方法的能力。
★ 在C#中委托定义使用delegate关键字,其委托的名称可以自由选择。
访问修饰符 delegate 返回类型 委托名称 (参数列表….);
★ 委托的实例化必须保证:方法的签名及返回值类型必须与委托类型所声明的一致。
委托声明: public delegate int SomeDelegate (string arg1, bool arg2);
方法: private static int SomeMethod (string arg1, bool arg2){return 0;}
实例化: SomeDelegate sd=new SomeDelegate(SomeMethod);
★ C#委托定义经编译器编译会生成一个派生自System.MulticastDelegate的密封类,它含有3个编译器生成的方法(Invoke()、BeginInvoke()、EndInvoke()),这3个方法的参数与返回值基于委托的声明。(System.MulticastDelegate继承自System.Delegate类,System.Delegate类则继承自System.Object)
★ 无法直接显示地声明一个继承自System.MulticastDelegate类的委托。委托必须使用delegate关键字声明,编译器会自动为我们生成继承代码。
★ 委托既然是一个类,那么它可以被定义在任何地方,即可以定义在类的内部,也可以定义在类的外部。
对于何时使用委托,何时使用接口(即策略模式),MSDN中有明确的描述:
在以下情况下,请使用委托:
当使用事件设计模式时。
当封装静态方法可取时。
当调用方不需要访问实现该方法的对象中的其他属性、方法或接口时。
需要方便的组合。
当类可能需要该方法的多个实现时。
在以下情况下,请使用接口:
当存在一组可能被调用的相关方法时。
当类只需要方法的单个实现时。
当使用接口的类想要将该接口强制转换为其他接口或类类型时。
当正在实现的方法链接到类的类型或标识时:例如比较方法。