C#扩展方法

扩展方法可以让你在不对原有类型进行修改、继承和重新编译的条件下,添加一些额外功能。扩展方法是一种特定类型的静态方法,但可以通过原始类型的实例进行调用。通过C#实现的扩展方法在使用方法上跟原始类型的真实定义的方法没有不同。

因此上述特性很适合在对一些类库进行二开时使用,如笔者就是通过项目中发现对newtonsoft.json.dll进行扩展才了解到这个知识点的。可见多读项目代码还是多少有用的!

最常用的扩展方法要数对于System.Collections.IEnumerable和System.Collections.Generic.IEumerable<T>类型添加LINQ查询操作。要使用标准的LINQ查询方法,只要引入System.Linq命名空间,然后在任何实现IEnumerable<T>接口的类型实例中都可以使用GroupBy, OrderBy, Average等方法,如List<T>和Array。

下面的例子展示了如何对一个int数组进行OrderBy操作,可以看到LINQ的扩展方法使用起来很简单明了:

class ExtensionMethods2   
{

    static void Main()
    {           
        int[] ints = { 10, 45, 15, 39, 21, 26 };
        var result = ints.OrderBy(g => g);
        foreach (var i in result)
        {
            System.Console.Write(i + " ");
        }           
    }       
}
//Output: 10 15 21 26 39 45

实现

扩展方法需要被定义成static,可却只能通过类的实例语法来调用。方法定义的第一个参数必须是需要添加到的原始类型,使用this操作符来指定。定义后的扩展方法,只要通过using命令将其所在的命名空间导入到程序中,即可使用。

下面的例子展示了如何给System.String类添加一个扩展方法,注意这个方法的定义没有嵌套在任何其他类型中,是一个独立的类定义。

namespace ExtensionMethods
{
     public static class MyExtensions
     {
          public static int WordCount(this String str)
          {
               return str.Split(new char[]{' ', '.', '?'}, StringSplitOptions.RemoveEmptyEntries).Length;
          }
     }
}

在使用WrodCount方法时,只需要导入命名空间:

using ExtensionMethods;

然后即可在代码中使用:

string s = "Hello Extension Methods";
int i = s.WordCount();

关于这个例子的更多信息,参见How to: Implement and Call a Custom Extension Method.

扩展方法的运行时绑定

你只能通过扩展方法对类或接口进行扩展,而不能重写或覆盖它们。如果扩展方法与类或接口方法拥有一样的名称和签名,那么该方法将永远不会被执行。这个其实好理解,如果扩展能够对原有类型的方法进行重写的话,那么程序将毫无安全性可言了,破解就变得轻而易举了。

在编译时,扩展方法的优先级始终低于原有类型方法,这意味着,编译器在进入一个函数调用点时,首先是从实例类型中的原始方法列表中寻找匹配的函数,如果找不到才在所有扩展方法中绑定第一个匹配的方法返回。

实际上,笔者在实际测试发现:

  • 引入具有相同签名的多个扩展命名空间时,在编译时会报二义性错误
  • 扩展方法只能访问原有类型的public属性、方法,如果是自己的源代码的话,不如继承的访问权限更加灵活

关于此部分,有兴趣的读者,可以阅读官网文档Extension Methods中对应的例子。

使用时应注意:

一般来说,对于扩展方法只有在真正需要的时候才使用,在有可能的情况下,使用对原有类型的继承来实现功能的添加。

当对一些有可能进行变更的类型进行扩展时,其改动可能会引起扩展方法的异常,在使用中特别需要注意这一点。

如果你决定要对一个已有类型进行扩展时,记住下面事项:

  • 拥有与原有类型定义方法签名一致的扩展方法永远不会被执行;
  • 扩展方法使用命名空间域,当引入的命名空间中有多个扩展方法时,其所有方法都会被引入;

如果是在编写类库(class library)时,不要使用扩展方法的方式来避免对类库版本号的改变。实际上,当有一些象征性的功能需要添加时,更合适的做法是发布新的版本库。

标签: c#, 扩展方法, Extension Method

添加新评论