扩展方法并不能从效率或者技术上对代码产生影响,但是可以让代码更优雅也更好看,这可能是我目前觉得最有用的地方。
我们从一个例子来看:首先假设你有一个类,存放三个double类型数据
public sealed class Data
{
public double d1, d2, d3;
public Data(double D1,double D2,double D3)
{
d1 = D1;
d2 = D2;
d3 = D3;
}
public double Sum()
{
return d1 + d2 + d3;
}
}
这是一个非常有限的类,但假设你想让他拥有更多的方法,如方法返回这三个数的平均数。那么一般有几种办法实现这个额外的功能。
- 如果你有源代码并可以修改这个类,你只需要为这个类增加一个方法
- 如果你不能修改这个类(如这个类在一个第三方类库中),那么只要它不是密封的,你就能把它作为一个基类并派生一个类,在派生类实现这个额外的方法。
然而如果不能访问代码或者该类是密封的,你就没法用上述方法实现,你就不得不在另一个类中实现该类的公有成员实现这样的方法。
public class UglyWay
{
public static double Average(Data data)
{
return data.Sum() / 3;
}
}
然后你就可以在main函数中这么调用:
Data data = new Data(3, 4, 5);
Console.WriteLine($"Average: {UglyWay.Average(data)}");
尽管上面的方法也不错,但是如果能在实例自身上调用该方法,而不是创建一个作用于它的类的实例,将会更优雅。区别如下:
ExtendMyData.Average(data) //静态形式
data.Average(); //实例形式
扩展方法允许你使用第二种形式。先看扩展方法的代码:
public static class ExtendData
{
public static double Average(this Data data)
{
return data.Sum() / 3;
}
public static Data PrintInfo(this Data data)
{
Console.WriteLine($"d1: {data.d1}\td2: {data.d2}\td3: {data.d3}");
return data;
}
//public static double Getd1(this Data data)
//{
// return data.d1;
//}
}
这里我写了2个扩展方法(注意扩展方法的参数形式),一个是返回平均数,一个是打印数据信息,其中打印信息后还返回了参数本身,这么做可以实现连续调用:
Data data = new Data(3, 4, 5);
// Console.WriteLine($"Average: {UglyWay.Average(data)}");
Console.WriteLine(data.PrintInfo().Average());
输出如下:
d1: 3 d2: 4 d3: 5
4
这么做可以实现类似于管道操作,而且理论上可以无限调用。
小结
- 扩展方法必须声明在静态类中
- 本身必须被声明为static,必须在参数中包括关键字this和它所扩展的类的名称,且作为第一个参数类型。
值得说明的是,扩展方法不会修改原始类的封装性,当你把Data类中的成员声明为私有方法时,无论你怎么扩展都无法直接访问到那三个成员,也就无法将其打印出来。
细心的你在使用Linq的时候,用鼠标点击linq的扩展方法就会出现类似的形式。一旦当你添加了扩展方法,你就可以像使用.net内部方法一样使用这些方法(智能提示会把这些方法加入),因为你还可以扩展.net内部的方法,如为string类扩展你自己的方法。