8.6 扩展方法
14.3.2节StringBuilder成员会提到StringBuilder类提供的字符串处理方法比String类少。
这是一件很奇怪的事,StringBuilder类是可变的,所以他应该是进行字符串处理的首选方式。
现在假定你想自己定义一些缺失的方法以方便操作StringBuilder。
例如你也许想定义以下IndexOf方法。
public static class StringBuilderExtensions { public static Int32 IndexOf(StringBuilder sb,Char value) { for(Int32 index=0;index<sb.Length;index++) if(sb[index]==value)return index; return -1; } }
定义好这个方法后,可以在代码中使用它,如下所示。
StringBuilder sb=new StringBuilder("Hello. My name is Jeff");//初始字符串 //将句点改为感叹号,然后获取!字符的索引(5) Int32 index=StringBuilderExtensions.IndxOf(sb.Replace('.','!'),'!');
但更好的做法是让StringBuilder类自己定义了IndexOf方法,这样上述代码就可以重写为:
Int32 index=sb.Replace('.','!').IndexOf('!');
这样代码就非常清晰合理了,要将IndexOf方法转变成扩展方法,只需在第一个参数前添加this关键字:
public static class StringBuilderExtensions { public static Int32 IndexOf(this StringBuilder sb,Char value) { for(Int32 index=0;index<sb.Length;index++) if(sb[index]==value)return index; return -1; } }
现在,当编译器看到以下代码
Int32 index=sb.IndexOf('X');
就首先检查StringBuilder类或者他的任何基类是否提供了获取单个Char参数,名为indexOf的一个实例方法。
如果是就生成IL代码来调用他。如果没有找到匹配的实例方法,
就继续检查是否有任何静态类定义了名为IndexOf的静态方法。
方法的第一个参数的类型和当前用于调用方法的那个表达式的类型匹配,而且该类型必须用this关键字标识。
在本例中,表达式是sb,类型时StringBuilder(用this关键字标识),以及一个Char。
编译器找到了这个IndexOf方法,所以生成相应的IL代码来调用这个静态方法。
VS通过智能感知来给程序员列出能用的扩展方法
8.6.1 规则和原则
- C#只支持扩展方法,不支持扩展属性、事件、操作符。
- 扩展方法必须在非泛型的静态类中声明。 类型没有限制。至少有一个参数,而且只有第一个参数能用this关键字标记。
- 编译器在静态类中查找扩展方法时,要求静态类本身必须具有文件作用域,不能嵌套在另一个类中。
- 编译器要求导入扩展方法,如Wintellect命名空间定义了一个,为了访问其中扩展方法,必须在他的源代码文件顶部写一条using指令。
- 使用需谨慎,不是所有人都熟悉它。不要讲System.Object作为扩展方法的第一个参数,否则这个方法在所有表达式类型上都调用。
- 扩展方法可能存在版本控制问题。
8.6.2 用扩展方法扩展各种类型
由于扩展方法实际是对一个静态方法的调用,所以CLR不会生成代码对调用方法的表达式进行null值检查。
例如StringBuilder的一个实例为null,调用扩展方法IndexOf时不会抛出空引用异常,
而是会在IndexOf方法内部的for循环时抛出空引用异常。
但还要注意,可以为接口类型定义扩展方法,如下所示。
public static void ShowItems<T>(this IEnumerable collection) { foreach(var item in collection) Console.WriteLine(item); }
任何表达式。只要他最终的类型实现了IEnumerable<T>接口,就能调用上述扩展方法。
委托部分省略