MSDN官方文档中说,“扩展方法使您能够向现有类型‘添加’方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。对于用 C# 和 Visual Basic 编写的客户端代码,调用扩展方法与调用在类型中实际定义的方法之间没有明显的差异。”
按照这种官方描述,扩展方法就是用一个静态类型去“扩展”另一个类型,使得另一个类型看起来像是被我们添加了很多“成员实例方法”。官方文档也说得很明白,“通常,您更多时候是调用扩展方法而不是实现您自己的扩展方法。”“通常,建议您只在不得已的情况下才实现扩展方法,并谨慎地实现。只要有可能,必须扩展现有类型的客户端代码都应该通过创建从现有类型派生的新类型来达到这一目的。”也就是说,扩展方法只是C#从语言上对框架功能LINQ的一种支持,语言设计者并不希望我们在其他场合使用扩展方法。
尽管如此,在特定场合下你还是会觉得有足够理由使用扩展方法。这类场合往往具有这样的特征:BCL框架类库或者第三方类库中提供了某个实例方法,但是在你的代码中使用会存在某种缺陷——1. 该方法本身没有参数有效性约束;2. 该方法是链式调用的一环,但可能返回null导致后续调用失败——而在你的代码中作出相应处理是繁琐的,会破坏可读性;并且这个方法所在类型是密封的。
这时,作为一个脑中对扩展方法概念有所了解的程序员,理应认识到扩展方法的用武之地来了,这次它不是用来扩展类型,而是,真的,“扩展”方法了。
你需要创建一个调用语法和原方法一样的扩展方法,当然,为了可读性,你的方法名称应当极具对原方法名称的某种启发性和诱导性。我们来看两个实例。
【实例1,
VB】在LINQ
to XML中,我们经常写下诸如
Dim s =
xe.Element("E").Attribute("A").Value
这样的语句。但是如果不存在E元素或者A特性,则会返回null,导致后面抛出空引用异常。事实上我们希望遇到此情况能使s得到空字符串。为此我们定义扩展方法来做处理。以Attribute为例,我们扩展Attribute方法为AttributeX方法:
Module M <System.Runtime.CompilerServices.Extension()> _ Public Function AttributeX(ByVal xe As XElement, ByVal attName As String) As XAttribute Return If(xe.Attribute(attName), New XAttribute(attName, String.Empty)) End Function End Module
类似地,我们扩展一个ElementX方法,当不存在指定元素时返回新的空元素。这样,我们即可高枕无忧地调用:
Dim s = xe.ElementX("E").AttributeX("A").Value
【实例2,C#】在AutoCAD .NET开发中,有一个Curve.GetParameterAtDistance方法,其参数范围在0到curve长度之间,否则会抛出异常。我有如下调用:
List<RoadInterval> intersectBreakIntervals = intersectDists.Select(x => new RoadInterval { start = _road.GetParameterAtDistance(x - GetHongxianWidth()), end = _road. GetParameterAtDistance (x + GetHongxianWidth()) }).ToList();
其中参数可能出现小于0或者大于curve长度的情况。由于使用了对象初始化器,不便于进行其他处理。为此我定义了扩展方法用于扩展此方法:
public static class ExtensionMethods { public static double GetParamAtDist(this Curve cv, double dist) { if (dist < 0) { dist = 0; } else if (dist > cv.GetDistanceAtParameter(cv.EndParam)) { dist = cv.GetDistanceAtParameter(cv.EndParam); } return cv.GetParameterAtDistance(dist); } }
这样,我又高枕无忧了。
不知各位是否也悟出了“扩展”二字的新含义呢?