新特性(2):
5、扩展方法
如果需要给一个现有类增加一个方法,我们会怎么做?一般会用两种方式:一、修改原来类的代码,增加这个方法。二、创建一个派生类,在派生类中定义这个方法。修改原有代码肯定是我们不愿意做的,而且 我们也不一定有修改 原 代码的权限。而创建派生类并不是真正向原类型中添加方法, 要使用这个方法 ,我们不得不创建的新实例。有没有一种方式,在不用修改原代码的情况下,直接给这个类添加方法呢?C#3.0中为我们提供了一种方式,这就是扩展方法。
利用扩展方法我们不但能向以前定义的类中添加新方法,而且可以为Framework中定义的原始类型和接口任意添加方法,是不是很新奇啊?没错,这看起来是很有意思,也很方便。其实微软在3.0中已经为许多基础接口增加了扩展方法 ,比如Enumerable(可枚举的)接口中的OrderBy(排序方法)。在LINQ中也很常见,比如标准查询运算符。在Visual Studio的智能感知中, 扩展方法 前面会有个蓝色向下箭头 。
下面介绍一下扩展方法的实现,先看一个例子
定义:
namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
调用:
using ExtensionMethods; // 引用命名空间
string s = "Hello Extension Methods";
int i = s.WordCount();
上面在命名空间 ExtensionMethods中,定义了一个静态类 MyExtensions,实现了静态方法 WordCount,用来获取字符串中包含的单词数量。实现过程是将传进来的字符串参数用空格、点或问号分割为数组,并返数组回元素个数。方法需要一个String参数,但参数类型前面怎么有个"this"啊?这就表明这个方法是this后面 类型(String)的扩展方法。
从上面的例子可以看出,扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用,调用扩展方法与调用在类型中实际定义的方法之间没有明显的差异。 由于扩展方法必须定义在外部的非泛型静态类中,所以在其他命名空间中调用时,需要引用这个静态类的命名空间,这个命名空间可以看做是扩展方法的作用域。这点其实也很好,我们可以在不同的命名空间定义同名的扩展方法,执行不同的实现,根据需要分别调用。
扩展方法在实现上其实与实例方法是不同的, 扩展方法是在 编译器生成中间语言(IL)时转换为静态方法调用 的 ,而且无法访问它们所扩展的类型中的私有变量,所以不会真正违反封装原则。
加载顺序: 当编译器遇到方法调用时,它首先在该类型的实例方法中寻找匹配的方法。如果未找到任何匹配方法,编译器将搜索为该类型定义的任何扩展方法,并且绑定到它找到的第一个扩展方法。
使用时还要注意:
1、扩展方法可以用来扩展类或接口,但不能被重写。
2、编译时,扩展方法的优先级总是比类型本身中定义的实例方法低。换句话说,如果某个类型具有一个名为Process(int i) 的方法,而您有一个具有相同签名的扩展方法,则编译器总是绑定到该实例方法。因此, 与接口或类方法具有相同名称和签名的扩展方法永远不会被调用。
3、在同一命名空间中不能重复定义( 签名 )相同的 扩展方法。
6、分部方法定义
在Framework2.0中增加了分布类的定义,3.0中对于方法也增加了分布定义,同样使用partial关键字。
分部方法声明由两个部分组成:定义和实现。它们可以位于分部类的不同部分中,也可以位于同一部分中。如果不存在实现声明,则编译器将优化定义声明和对方法的所有调用,在编译时移除未提供该实现的方法以及对方法的所有调用 ,并且不会产生运行时开销 。如果调用了未实现的方法,也不会导致编译时错误或运行时错误。分部方法使类的某个部分的实施者能够定义方法(类似于事件),类的另一部分的实施者可以决定是否实现该方法。
例子:
public partial class Employee
{
// 定义方法
partial void MakeAsManager();
// 调用方法
public void Test()
{
MakeAsManager();
}
}
public partial class Employee
{
partial void MakeAsManager()
{
Console.WriteLine("Employee.MakeAsManager()");
}
}
这个很简单 ,但在使用时还是要注意几点:
1.分部方法声明必须以上下文关键字partial 开头,并且方法必须返回void。
2.分部方法可以有ref 参数,但不能有out 参数。
3.分部方法为隐式private 方法,因此不能为virtual 方法。
4.分部方法不能为extern 方法,因为主体的存在确定了方法是在定义还是在实现。
5.分部方法可以有static 和unsafe 修饰符。
6.分部方法可以为泛型的。约束将放在定义分部方法声明上,但也可以选择重复放在实现声明上。参数和类型参数名称在实现声明和定义声明中不必相同。
7.不能将委托转换为分部方法。