时间:2021-05-19
扩展方法(Extension Methods)是C#3.0时引入的新特性,相信很多人都听过并且也都用过,最常见的是在LINQ中的使用。
不仅如此,在开发中,我们也可以创建自己扩展方法,使用它来优化类的设计、简化代码。本文将简单地介绍扩展方法的概念、定义、使用场景以及要注意的点。
一、概念
扩展方法是一种特殊类型的静态方法。对于一个C#类型,如类(包括密封类)、值类型、接口等,扩展方法可以在不改变该类型源码的前提下,为它的实例提供新的成员。因此,若要为一个框架或第三方库的某个类型增加辅助功能,通过扩展方法就可以轻而易举地实现,这也是“扩展”的意义所在。
二、如何定义
创建扩展方法很简单,有以下几个步骤:
1、创建一个静态类;
2、在其中创建一个静态方法;
3、为这个静态方法添加至少一个参数,并在第一个参数前加上this关键字,这个关键字会告诉编译器当前方法是一个扩展方法。而这个方法将成为第一个参数所属类型的新成员。
以下一个典型的扩展方法,用于为枚举值提供一个可获取其DescriptionAttribute特性值的方法:
namespace TLA. Infrastructure. Extensionspublic static class EnumExtensions{public static string GetDescription(this Enum en){Type type = en. GetType();MemberInfo[] memInfo = type . GetMember(en. ToString());if (memInfo != null && memInfo.Length > 0){object[] attrs = memInfo[0] .GetCustomAttributes (typeof (DescriptionAttribute), false);if (attrs != null && attrs. Length > 0){return ( (DescriptionAttribute )attrs [0]) . Description;}}return en. ToString();}}}注意:只有在引用扩展方法所在的静态类的命名空间后,才能使用它;否则,直接调用会编译失败。上例中,使用该扩展方法要引用TLA.Infrastructure.Extensions命名空间。
三、何时使用
从扩展方法的概念上,不难看出,它可以用在以下几种场合:
1、要为某个类型扩展功能,但没有其源码,比如某个框架或第三方库中的一个类;例如,想要获取一个列表中所有的奇数项,就可以为IList<T>接口增加一个扩展方法,这里的IList<T>接口本身是.NET框架中的接口。
public static class IListExtentions{public static IEnumerable<T> OddItems<T>(this IEnumerable<T> list){if (list == nu1l){throw new ArgumentNullExcept ion (nameof(list)) ;}for (int i = 0; i < list. Count(); i++){if(i%2==0){yield return list. ElementAt(i);}}}}2、即使可以访问原有类型的源码,也可以使用扩展方法为它添加辅助功能;
public interface ILog{void Log(string message, LogLevel logLevel);}public static class ILogExtensions{/// <summary>///记录调试信息/// </summary>/// <remarks>扩 展方法,方便记录调试信息</ remarks>public static void LogDebug(this ILog logger, string message ){if (true) // 判断日志配置中是否允许输入Debug类型的日志{logger? .Log($" {message}", LogLevel.Debug);}}}3、重用代码,使代码更简洁;由于扩展方法封装了一段完整的逻辑,所以,使用扩展方法就避免了复制粘贴代码的情况。上例中扩展方法的内容也符合这种使用场景。
四、注意事项
以下是定义与使扩展方法时的一些注意事项和最佳实践:
1、扩展方法本质上是为原有类型提供辅助功能,因此,在创建时,要确保它具有实际意义,且遵循单一职责原则;也即,不能过度使用扩展方法并且它能够完成一个具体、完整的功能;
2、扩展方法本身具有通用性,因此,它里面应避免特定的业务数据类型及其相关逻辑;
3、如果为接口增加扩展方法,扩展方法的命名空间可以与接口的一致;否则,应尽量避免与原类型写在同一命名空间下,这样会“污染”原类型。建议的做法是为扩展方法所在的类设定一个单独的命名空间,如:<Company>.<Product>.Extentions。不过,这样做也有缺点:在操作原有类型的实例时,如果不引用扩展方法所在的命名空间,那么,它就不容易被发现,而解决这个问题的办法是,尽量将扩展方法文档化,并告诉项目组的其他开发人员;
4、为接口增加扩展方法后,则所有实现此接口的类都会包含该扩展方法;
5、在扩展方法中,要对第一个参数进行非空检查,如果为空,应抛出ArgumentNullException(参数为空)异常。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
扩展阅读c#基础系列1---深入理解值类型和引用类型c#基础系列2---深入理解String引言在上篇文章深入理解值类型和引用类型的时候,有的小伙伴就推荐说一说
本文实例讲述了C#使用SendMessage实现进程间通信的方法。分享给大家供大家参考。具体分析如下:为了深入理解消息机制,先来做一个测试项目在新建项目的For
最近在看深入理解C#,发现这是一本很不错的书,将很多C#的知识点联系了起来,更像是一本C#历史书,从C#1一步步介绍到C#4。所以准备一边看,一边整理读书笔记。
先来一段代码引入主题。如果你可以直接说出代码的输出结果,说明本文不适合你。(代码引自《深入理解C#》第三版)classProgram{privatedelega
本文以实例形式讲述了C#泛型的用法,有助于读者深入理解C#泛型的原理,具体分析如下:首先需要明白什么时候使用泛型:当针对不同的数据类型,采用相似的逻辑算法,为了