欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第十八章 扩展方法

程序员文章站 2022-04-29 08:26:10
...

  18.1 概述

  什么是扩展方法,它可以用来扩展已经定义类型的方法成员。

  为了解决每次扩展需要继承的问题,所以C#3.0新添加了扩展方法,为现有的类型添加方法。

  【扩展方法特性】

  1:扩展方法必须在一个非嵌套,非泛型的静态类中定义

  2:至少有一个参数

  3:第一个参数必须加上this关键字(第一个参数类型也就是扩展类型,即指方法对这个类型进行扩展)

  4:第一个参数不能使用任何其他的修饰符

  5:第一个参数类型不能是指针类型

  扩展方法的使用,下面的代码演示了如何使用扩展方法:

  eg:

  // #region >

  // /*----------------------------------------------------------------

  // // Copyright (C) 2021 极客部落

  // // 版权所有。

  // //

  // // 文件名:Program.cs

  // // 文件功能描述:

  // //

  // //

  // // 创建者:GeekTribe

  // // 时间:14:05

  // //----------------------------------------------------------------*/

  // #endregion

  using System;

  using System.Collections.Generic;

  ?

  namespace MSN

  {? public static class ListExten

  {

  //定义扩展方法

  public static int JSum(this IEnumerable source)

  {

  if (source==null)

  {

  throw (new ArgumentNullException("数组为空"));

  }

  ?

  int jsum=0;

  bool flag=false;

  foreach (int current in source)

  {

  if (!flag)

  {

  jsum +=current;

  flag=true;

  }

  else

  {

  flag=false;

  }

  }

  return jsum;

  }

  }

  class MainClass

  {

  public static void TestMethod(int input)

  {

  List source=new List { 1, 2, 3, 4, 5, 6, 3 };

  int jsum=ListExten.JSum(source);

  Console.WriteLine("数组的奇数之和为" + jsum); source.JSum();

  }

  ?

  public static void Main(string[] args) {

  MainClass.TestMethod(5);

  }

  }

  }

  18.2 扩展方法查找规则

  编译器如何发现扩展方法呢?

  对于C#3.0编辑器而言,当它看到某个类型的变量在调用方法时候,首先去该对象的实例方法中进行查找,如果没有找到与调用方法同名并且参数一致的实例方法,编译器就会去查找是否存在合适的扩展方法。

  编译器会检查所有导入的命名空间和当前的命名空间的扩展方法,并将变量匹配到扩展类型,这里存在一个隐式的类型转换的扩展方法。前面代码之和,List到我们扩展的类型IEnumerable就存在一个隐式转换,因为List实现了IEnumerable接口。在C#中,从子类到父类的转换可以通过隐式转换来完成。

  在VS中,扩展方法前面都有一个向下的箭头标识。

  这个是我们人为识别扩展方法的办法,编译器会根据

  SystemtimepilerServices.ExtensionAttribute属性来识别扩展方法。如果我们定义的是扩展方法的话,该属性就会被自动绑定到方法之上。

  从编译器发现扩展方法的过程来看,方法调用的优先级顺序为:类型的实例方法->当前命名空间下的扩展方法->导入命名空间的扩展方法。

  下面我们通过代码来演示编译器发现方法的过程:

  // #region >

  // /*----------------------------------------------------------------

  // // Copyright (C) 2021 极客部落

  // // 版权所有。

  // //

  // // 文件名:Program.cs

  // // 文件功能描述:

  // //

  // //

  // // 创建者:GeekTribe

  // // 时间:14:05

  // //----------------------------------------------------------------*/

  // #endregion

  using System;

  ?

  namespace CustomNameSpace

  {

  using MSN;

  ?

  public static class CustomExtensionClass

  {

  public static void Print(this Person per)

  {

  Console.WriteLine("调用的是CustomNameSpace命名空间下扩展方法的输出,姓名为{0}", per.Name);

  }

  ?

  public static void Print(this Person per, string s)

  {

  Console.WriteLine("调用的是当前命名买游戏账号平台空间下的扩展方法输出,姓名为{0},附加字符串{1}", per.Name, s);

  }

  }

  }

  ?

  namespace MSN

  {

  using CustomNameSpace;

  ?

  public class Person

  {

  public string Name { get; set; }

  }

  ?

  public static class ExtensionClass

  {

  public static void Print(this Person per)

  {

  Console.WriteLine("调用的是当前命名空间下的扩展方法输出,姓名为{0}", per.Name);

  }

  }

  ?

  class MainClass

  {

  public static void Main(string[] args) {

  ?

  Person p=new Person { Name="zhangsan" };

  p.Print();

  p.Print("Hello");

  }

  }

  }

  注意:

  同一个命名空间下的两个类含有扩展类型相同的方法,编译器不知道该如何调用哪个方法,就会出现编译错误。

  18.3 空类型调用扩展方法

  在C#中,在空引用(null)上调用实例方法会引发NullReferenceException异常的,但在空引用上却可以调用扩展方法,如下代码所示:

  // #region >

  // /*----------------------------------------------------------------

  // // Copyright (C) 2021 极客部落

  // // 版权所有。

  // //

  // // 文件名:Program.cs

  // // 文件功能描述:

  // //

  // //

  // // 创建者:GeekTribe

  // // 时间:14:05

  // //----------------------------------------------------------------*/

  // #endregion

  using System;

  ?

  namespace MSN

  {

  public static class NullExten

  {

  public static bool IsNull(this object obj)

  {

  return obj==null;

  }

  }

  ?

  class MainClass

  {

  public static void Main(string[] args) {

  Console.WriteLine("空类型调用扩展方法演示:");

  string s=null;

  Console.WriteLine("字符串S为空字符串:{0}", s.IsNull());

  }

  }

  }

  ?

  上述的代码存在缺陷,因为扩展了object,所有继承于object的类型都将具有该扩展方法,这就对其他的类型产生了污染,修改的时候将其改为string即可。之所以不会出现异常,因为编译器只是把空引用s作为参数传入了静态类NullExten,从而调用了静态方法IsNull。