扩展方法可以理解为现有的类型(现有类型可以为自定义的类型和.Net
类库中的类型)扩展(添加)应该附加到该类型中的方法。
在没有扩展方法之前,如果我们想为一个已有类型自定义自己逻辑的方法时,我们必须自定义一个新的类型来继承已有类型的方式来添加方法
扩展方法必须具备下面的规则:
- 它必须在一个非嵌套、非泛型的静态类中
- 它至少要有一个参数
- 第一个参数必须加上this关键字作为前缀(第一个参数类型也称为扩展类型,即指方法对这个类型进行扩展)
- 第一个参数不能用其他任何修饰符(如不能使用ref out等修饰符)
- 第一个参数的类型不能是指针类型
演示代码:
CustomExtensionClass.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CustomNamesapce
{
using EventDemo;
public static class CustomExtensionClass
{
/// <summary>
/// 扩展方法定义
/// </summary>
/// <param name="per"></param>
public static void Print(this Person per)
{
Console.WriteLine("调用的是不同命名空间下扩展方法输出,姓名为: {0}", per.Name);
}
/// <summary>
/// 扩展方法定义
/// </summary>
/// <param name="per"></param>
public static void Print(this Person per, string s)
{
Console.WriteLine("调用的是不同命名空间下扩展方法输出,姓名为: {0}, 附加字符串为{1}", per.Name, s);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EventDemo
{
// 要使用不同命名空间的扩展方法首先要添加该命名空间的引用
using CustomNamesapce;
class Program
{
static void Main(string[] args)
{
Person p = new Person { Name = "Learning hard" };
// 当类型中包含了实例方法时,VS中的智能提示就只会列出实例方法,而不会列出扩展方法
// 当把实例方法注释掉之后,VS的智能提示中才会列出扩展方法,此时编译器在Person类型中找不到实例方法
// 所以首先从当前命名空间下查找是否有该名字的扩展方法,如果找到不会去其他命名空间中查找了
// 如果在当前命名空间中没有找到,则会到导入的命名空间中再进行查找
p.Print();
p.Print("Hello");
Console.Read();
}
}
// 自定义类型
public class Person
{
public string Name { get; set; }
// 当类型中的实例方法
////public void Print()
////{
//// Console.WriteLine("调用实例方法输出,姓名为: {0}", Name);
////}
}
// 当前命名空间下的扩展方法定义
public static class Extensionclass
{
/// <summary>
/// 扩展方法定义
/// </summary>
/// <param name="per"></param>
public static void Print(this Person per)
{
Console.WriteLine("调用的是同一命名空间下的扩展方法输出,姓名为: {0}", per.Name);
}
}
}
应用Demo:
大家都知道在C#中,在空引用上调用实例方法是会引发NullReferenceException异常的,但是可以在空引用上调用扩展方法,下面看一段演示代码:
NullExten.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EventDemo
{
namespace ExtensionDefine
{
/// <summary>
/// 扩展方法定义
/// </summary>
public static class NullExten
{
// 此时扩展的类型为object,这里我是故意用object类型的
// 如果是为了演示,当我们为一个类型定义扩展方法时,应尽量扩展具体类型,如果扩展其基类的话
// 则所有继承于基类的类型都将具有该扩展方法,这样对其他类型来说就进行了“污染
// 子所以形成了污染,是因为我们定义的扩展方法的意图本来只想扩展某个子类。
// 其实下面这个方法我的意图只是想扩展string类型的,所以更好的定义方法如下:
//public static bool isNull(this string str)
//{
// return str == null;
//}
// 不规范定义扩展方法的方式
public static bool IsNull(this object obj)
{
return obj == null;
}
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace EventDemo
{
// 必须引入扩展方法定义的命名空间
using ExtensionDefine;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("空引用上调用扩展方法演示:");
string s = null;
// 在该程序中要使用扩展方法必须通过using来引用
// 在空引用上调用扩展方法不会发生NullReferenceException异常
// 之所以不会出现异常,是因为在空引用上调用扩展方法,对于编译器而言只是把空引用s当成参数传入静态方法中而已
// 对于编译器来说,s.IsNull()的调用等效于下面的代码
//Console.WriteLine("字符串S为空字符串:{0}", NullExten.IsNull(s));
Console.WriteLine("字符串S为空字符串:{0}", s.IsNull());
Console.ReadKey();
}
}
}