建议101:使用扩展方法,向现有类型“添加”方法
考虑如何让一个sealed类型具备新的行为。以往我们会创建一个包装器类,然后为其添加方法,而这看上去一点儿也不优雅。我们也许会考虑修改设计,直接修改sealed类型,然后为其发布一个新的版本,但这依赖于你拥有全部的源码。更多的时候,我们会采取针对第三方公司提供的API进行编程的方式。对于我们来说,FCL是一组第三方公司(微软)提供给我们的最好的API。
包装类的编码形式如下:
class Program
{
static void Main(string[] args)
{
Student student = new Student();
Console.WriteLine(StudentConverter.GetSexString(student));
}
} public static class StudentConverter
{
public static string GetSexString(Student student)
{
return student.GetSex() == true ? "男" : "女";
}
}
public class Student
{
public bool GetSex()
{
return false;
}
}
可以看到,Student类型只提供了一个GetSex方法,它返回了一个bool值的结果。我们需要的是要将一个bool值转换为一个字符串,StudentConverter就是为了满足需求而创建的一个包装器类。调用者的代码看起来就应该是这样的:
Console.WriteLine(StudentConverter.GetSexString(student));
但是我们知道,可以有更优美的形式让调用者像调用Student类型的实例方法一样来调用GetSexString了。这种更好的方式就是扩展方法:
class Program
{
static void Main(string[] args)
{
Student student = new Student();
Console.WriteLine(student.GetSexString());
}
} public static class StudentExtension
{
public static string GetSexString(this Student student)
{
return student.GetSex() == true ? "男D" : "女?";
}
}
扩展方法除了让调用着可以像调用类型自身的方法一样去调用扩展方法外,它还有一些其他的主要优点:
- 可以扩展密封类型;
- 可以扩展第三方程序集中的类型;
- 扩展方法可以避免不必要的深度继承体系。
扩展方法还有一些必须遵循的要求:
- 扩展方法必须在静态类中,而且该类不能是一个嵌套类;
- 扩展方法必须是静态的;
- 扩展方法的第一个参数必须是要扩展的类型,而且必须加上this关键字;
- 不支持扩展属性、事件。
值得注意的一点是,扩展方法还能够扩展接口。这让接口看上去也是可以扩展的。扩展方法的这个特性被广泛应用于提供LINQ查询功能的Enumerable类和Queryable类中。以Enumerable为例,针对IEnumerable<T>接口提供了非常丰富的一种方法,如Select:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
//具体代码省略
}
它相当于让继承自IEnumerable<T>接口的任何子类都拥有了Select方法,而这些Select方法在用者看来,就好像是IEnumerable<T>接口所声明的一样。
转自:《编写高质量代码改善C#程序的157个建议》陆敏技