C# 学习笔记(13)—— 扩展方法

2023-10-20 18:51:19 浏览数 (1)

什么是扩展方法

扩展方法,首先是一种方法,它可以用来扩展已定义类型中的方法成员

在扩展方法诞生之前,如果想位一个已有类型自定义含有特殊逻辑的新方法时,你必须重新定义一个类型来继承已有类型,以这种方法来添加方法,如果基类有抽象方法,则还要重新实现这些抽象方法

这样,为了扩展一个方法,我们需要承受更多因继承而产的开销。如果继承来扩展现有类型总有些大材小用的感觉。并且值类型或密封累等也不能被继承,不能由此而获得扩展

于是,C# 3.0 提出了扩展方法,用它来为现有的类型添加方法,从未解决了使用继承进行扩展所带来的弊端

扩展方法的使用

定义扩展方法

代码语言:javascript复制
public static class IEnumerableExtensions
{
  public static bool IsEmpty<T>(this IEnumerable<T> ts)
  {
    return ts == null || ts.Count() == 0;
  }
}

从以上代码中,IsEmpty 方法就是一个扩展方法,它的功能就是判断集合是否为空

并不是所有方法都可以作为扩展方法,如何分辨代码中定义的是扩展方法还是普通方法呢?我们需要考察它是否符合下列扩展方法的定义规则

  • 扩展方法必须在一个非嵌套、非泛型的静态类中定义
  • 至少要有一个参数
  • 第一个参数必须加速this关键字作为前缀(第一个参数类型也称为扩展类型,即指方法对这个类型进行扩展)
  • 第一个参数不能使用任何其他的修饰符(如不能使用 ref、out 等修饰符)
  • 第一个参数的类型不能是指针类型

这些规则都是硬性规定,无论方法违反了哪一条,编译器都会报错,或认为它不是一个扩展方法

调用扩展方法

成功定义了一个扩展方法后,接下来就是该去调用它

代码语言:javascript复制
class Program
{
  static void Main(string[] args)
  {
    List<string> list = new List<string>();
    Console.WriteLine(list.IsEmpty()); // true
  }
}

从以上代码中,list.IsEmpty()调用了我们定义的扩展方法

编译器如何发现扩展方法

编译器会检查所有导入的命名空间和当前命名空间中的扩展方法,并将变量类型匹配到扩展类型,这里存在一个隐式转换的扩展方法

从编译器发现扩展方法的过程来看,方法调用的优先级顺序为:

1、类型的实例方法

2、当前命名空间下的扩展方法

3、导入命名空间的扩展方法

空引用也可调用扩展方法

在 C# 中,在空引用(即 null)上调用实例方法会引发 NullReferenceException 异常,但在空引用上却可以调用扩展方法

代码示例

代码语言:javascript复制
using System;
using System.Linq.Expressions;

namespace Demo
{

    class Program
    {
        static void Main(string[] args)
        {
            string str = null;
            Console.WriteLine(str.IsNull());
        }
    }

    static class NullExtension
    {
        public static bool IsNull(this object obj)
        {
            return obj == null;
        }
    }
}

以上代码在空引用上去调用扩展方法确实没有出现 NullReferenceException 异常

这段代码中扩展的定义是不规范的。代码扩展了 object 类型,所有继承于 object 的类型都将具有该扩展方法,这就对其他子类型产生了“污染”。之所以叫它污染,是因为我们定义的扩展方法本来只想扩展某个子类,却意外地造成了原本不想造成的后果。更好的实现方式如下:

代码语言:javascript复制
public static bool IsNull(this string str)
{
  return str == null;
}

所以当我们为一个类型定义扩展方法时,尽量扩展具体的类型,而不要扩展其基类

在空引用上调用扩展方法没有报错,是因为对于编译器而言,这个过程只是把空引用 “str” 当成参数传入静态方法而已

0 人点赞