【转】五、谈扩展方法的理解

为什么要用扩展方法

在说什么是扩展方法之前我们先来说说为什么要用扩展方法。

首先我们定义一个 Person 类:

public class Person
{/// <summary>/// 出生日期/// </summary>public DateTime BirthTime { get; set; }/// <summary>/// 死亡日期/// </summary>public DateTime? DeathTime { get; set; }//、、、、、、
}

加入这个类来自第三方的dll引用,且现在我们需要添加一个方法 GetAge 获取年龄。你可能会想到自己定一个子类继承:

public class MyPerson : Person
{public int GetAge(){if (DeathTime.HasValue)return (DeathTime.Value - BirthTime).Days / 365;elsereturn (DateTime.Now - BirthTime).Days / 365;}
}

是的,这样可以实现我们的需求。不过实现新增的方法就去继承真的是最合适的吗(暂且不说)? 如果上面定义的密封类呢? public sealed class Person ,这个时候是不能继承的,我们只能另想办法。

随意写个静态类:

public static class ExtensionClass
{public static int GetAge(Person person){if (person.DeathTime.HasValue)return (person.DeathTime.Value - person.BirthTime).Days / 365;elsereturn (DateTime.Now - person.BirthTime).Days / 365;}

然后调用  age = ExtensionClass.GetAge(p); ,是的看似不错。可是这和我们说的扩展方法有什么关系呢?下面就是见证奇迹的时候了。

其他的任何地方都不变,唯一变化的是在参数前面加里this关键字。对,是的,仅仅如此它就变成了我们今天要讲的扩展方法。

调用如:  var age = p.GetAge(); 相比上面的 age = ExtensionClass.GetAge(p); 更简单明了。

这里我们说的是在需要扩展密封类的方法时,我们可以使用到扩展方法。还有一种情况就是,在需要扩展接口的时候时候我们更加需要。比如,需要扩展IList的排序。我们要么写个扩展方法,要么是继承实现接口(会强制要求实现接口下的所有方法)。我想你心中已经有了答案选择哪种方式。

扩展方法到底是什么

我们看到上面使用的扩展方法,有没有感觉很神奇。仅仅多添加了一个this关键字就直接可以当成扩展方法使用了。那扩展方法到底是什么东东,看了上面代码好像和静态方法有着说不清道不明的关系。下面我们继续分析:

分别定义一个静态方法和一个扩展方法

 public static class ExtensionClass{public static int GetAge2(Person person){if (person.DeathTime.HasValue)return (person.DeathTime.Value - person.BirthTime).Days / 365;elsereturn (DateTime.Now - person.BirthTime).Days / 365;}public static int GetAge(this Person person){if (person.DeathTime.HasValue)return (person.DeathTime.Value - person.BirthTime).Days / 365;elsereturn (DateTime.Now - person.BirthTime).Days / 365;}

分别调用:

var p = new Person() { BirthTime = DateTime.Parse("1990-07-19") };
var age = p.GetAge();
age = ExtensionClass.GetAge2(p);

编译后的IL代码:

我们看到反编译成IL之后发现两者并无不同。所以,我理解成(扩展方法本质上就是静态方法,之所以出现扩展方法是C#以另外一种形式表现静态方法而已。只有有何妙用下面会继续讲解)。且 编译后同样带上了静态类名。

扩展方法可以做些什么

  • 把已有的静态方法转成扩展方法:如:
public static bool IsNullOrEmpty(this string str)
{return string.IsNullOrEmpty(str);
}

调用:

string str = null;
var isNull = str.IsNullOrEmpty();

感觉相比期静态方法调用要优雅,更接近我们的自然语言。

  • 可以编写很多的帮助类,如(以string为例):

/// <summary>/// 转DateTime /// </summary>/// <param name="str"></param>/// <returns></returns>public static DateTime? MyToDateTime(this string str){if (string.IsNullOrEmpty(str))return null;elsereturn DateTime.Parse(str);}/// <summary>/// 转double/// </summary>/// <param name="str"></param>/// <returns></returns>public static double MyToDouble(this string str){if (string.IsNullOrEmpty(str))return -1;elsereturn double.Parse(str);}/// <summary>/// 转int/// </summary>/// <param name="str"></param>/// <returns></returns>public static int MyToInt(this string str){if (string.IsNullOrEmpty(str))return -1;elsereturn int.Parse(str);}/// <summary>/// 指示指定的字符串是 null 还是 System.String.Empty 字符串。/// </summary>/// <param name="str"></param>/// <returns></returns>public static bool IsNullOrEmpty(this string str){return string.IsNullOrEmpty(str);}/// <summary>/// 如果字符串为null,则返回空字符串。(否则返回原字符串)/// </summary>/// <param name="str"></param>/// <returns></returns>public static string GetValueOrEmpty(this string str){if (str.IsNullOrEmpty())return string.Empty;return str;}

View Code

上面所有的都只是扩展方法的附加用处,扩展方法真正的威力是为Linq服务的(主要体现于IEnumerable和IQueryable),实现链式编程。下面我们自己来实现所谓的链式编程:

初始化 Person 集合。

List<Person> persons = new List<Person>()
{new Person(){ BirthTime=DateTime.Parse("1990-01-19")},new Person(){ BirthTime=DateTime.Parse("1993-04-17")},new Person(){ BirthTime=DateTime.Parse("1992-07-19"), DeathTime=DateTime.Parse("2010-08-18")},new Person(){ BirthTime=DateTime.Parse("1990-03-14")},new Person(){ BirthTime=DateTime.Parse("1991-08-15")},new Person(){ BirthTime=DateTime.Parse("1993-07-29")},new Person(){ BirthTime=DateTime.Parse("1991-06-19")}
};

需求:1.查询活人。2.按出生日期排序

public static class ExtensionClass{/// <summary>/// 按条件查询/// </summary>/// <typeparam name="T"></typeparam>/// <param name="list"></param>/// <param name="func"></param>/// <returns></returns>public static IList<T> MyWhere<T>(this IList<T> list, Func<T, bool> func){List<T> newList = new List<T>();foreach (var item in list){if (func(item))newList.Add(item);}return newList;}/// <summary>/// 升序排序/// </summary>/// <typeparam name="T"></typeparam>/// <param name="list"></param>/// <param name="func"></param>/// <returns></returns>public static IList<T> MyOrderBy<T>(this IList<T> list, Func<T, DateTime> func){if (list.Count() <= 1)return list;for (int i = 0; i < list.Count(); i++){for (int j = i + 1; j < list.Count(); j++){var item1 = list[j - 1];var item2 = list[j];if ((func(item1) - func(item2)).Ticks > 0){list[j - 1] = item2;list[j] = item1;}}}return list;}/// <summary>/// 降序排序/// </summary>/// <typeparam name="T"></typeparam>/// <param name="list"></param>/// <param name="func"></param>/// <returns></returns>public static IList<T> MyOrderByDescending<T>(this IList<T> list, Func<T, DateTime> func){if (list.Count() <= 1)return list;for (int i = 0; i < list.Count(); i++){for (int j = 1; j < list.Count() - i; j++){var item1 = list[j - 1];var item2 = list[j];if ((func(item1) - func(item2)).Ticks < 0){list[j - 1] = item2;list[j] = item1;}}}return list;}}

调用:(这里仅仅为了演示,所以不要讨论实现是否合理、算法是否高效。)

var newPersons = persons.MyWhere(t => t.DeathTime == null).MyOrderByDescending(t => t.BirthTime);
foreach (var item in newPersons)
{Console.WriteLine(item.BirthTime);
}

就是如此简单的实现了所谓的函数式编程。结果图如下:

这样一句代码搞定所有逻辑,像自然语言般的流畅。其实.net为IEnumerable实现了这样的扩展,如:

执行结构和上面一模一样。

其实扩展方法也可以当成静态方法来使用:

 var p1 = ExtensionClass.MyWhere(persons, t => t.DeathTime == null);var p2 = ExtensionClass.MyOrderByDescending(p1, t => t.BirthTime);var p3 = ExtensionClass.MyOrderBy(p2, t => t.BirthTime);

(不信?继续看,有图有真相)

C#代码:

反编译C#的代码:(你是不是看到了,编译后直接就是使用的扩展方法的形式。)

反编译的IL代码:

虽然编译后的代码是一样的,但是做为程序员的我们更喜欢哪种方式呢?

总结:

我们在对扩展方法的怎么使用疑惑或者忘记了规则的时候,我们不用去查找资料说:

  1. 第一个参数是要扩展或者要操作的类型,这称为"被扩展的类型"
  2. 为了指定扩展方法,要在被扩展的类型名称前面附加this修饰符
  3. 要将方法作为一个扩展方法来访问,要用using指令导入扩展类型的命名空间,或者使扩展类型和调用代码在同一个命名空间中.

我们只需记住,当你不知道怎么编写或使用扩展方法时,你先把它当成静态方法编写或使用。如果可行,一般都可以转成扩展方法的形式。

全部代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity.Utilities;
using System.Diagnostics.CodeAnalysis;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using System.IO;namespace test
{class Program{static void Main(string[] args){/*             * 1.工具类* 2.链式编程*/string str = null;var isNull = str.IsNullOrEmpty();var p = new Person() { BirthTime = DateTime.Parse("1990-07-19") };var age = p.GetAge();age = ExtensionClass.GetAge2(p);List<Person> persons = new List<Person>() {new Person(){ BirthTime=DateTime.Parse("1990-01-19")},new Person(){ BirthTime=DateTime.Parse("1993-04-17")},new Person(){ BirthTime=DateTime.Parse("1992-07-19"), DeathTime=DateTime.Parse("2010-08-18")},new Person(){ BirthTime=DateTime.Parse("1990-03-14")},new Person(){ BirthTime=DateTime.Parse("1991-08-15")},new Person(){ BirthTime=DateTime.Parse("1993-07-29")},new Person(){ BirthTime=DateTime.Parse("1991-06-19")}};var newPersons = persons.MyWhere(t => t.DeathTime == null).MyOrderByDescending(t => t.BirthTime);var p1 = ExtensionClass.MyWhere(persons, t => t.DeathTime == null);var p2 = ExtensionClass.MyOrderByDescending(p1, t => t.BirthTime);var p3 = ExtensionClass.MyOrderBy(p2, t => t.BirthTime);foreach (var item in newPersons){Console.WriteLine(item.BirthTime);}Console.ReadKey();}}public sealed class Person{/// <summary>/// 出生日期/// </summary>public DateTime BirthTime { get; set; }/// <summary>/// 死亡日期/// </summary>public DateTime? DeathTime { get; set; }}//public class MyPerson : Person//{//    public int GetAge()//    {//        if (DeathTime.HasValue)//            return (DeathTime.Value - BirthTime).Days / 365;//        else//            return (DateTime.Now - BirthTime).Days / 365;//    }//}public static class ExtensionClass{/// <summary>/// 按条件查询/// </summary>/// <typeparam name="T"></typeparam>/// <param name="list"></param>/// <param name="func"></param>/// <returns></returns>public static IList<T> MyWhere<T>(this IList<T> list, Func<T, bool> func){List<T> newList = new List<T>();foreach (var item in list){if (func(item))newList.Add(item);}return newList;}/// <summary>/// 升序排序/// </summary>/// <typeparam name="T"></typeparam>/// <param name="list"></param>/// <param name="func"></param>/// <returns></returns>public static IList<T> MyOrderBy<T>(this IList<T> list, Func<T, DateTime> func){if (list.Count() <= 1)return list;for (int i = 0; i < list.Count(); i++){for (int j = i + 1; j < list.Count(); j++){var item1 = list[j - 1];var item2 = list[j];if ((func(item1) - func(item2)).Ticks > 0){list[j - 1] = item2;list[j] = item1;}}}return list;}/// <summary>/// 降序排序/// </summary>/// <typeparam name="T"></typeparam>/// <param name="list"></param>/// <param name="func"></param>/// <returns></returns>public static IList<T> MyOrderByDescending<T>(this IList<T> list, Func<T, DateTime> func){if (list.Count() <= 1)return list;for (int i = 0; i < list.Count(); i++){for (int j = 1; j < list.Count() - i; j++){var item1 = list[j - 1];var item2 = list[j];if ((func(item1) - func(item2)).Ticks < 0){list[j - 1] = item2;list[j] = item1;}}}return list;}public static int GetAge2(Person person){if (person.DeathTime.HasValue)return (person.DeathTime.Value - person.BirthTime).Days / 365;elsereturn (DateTime.Now - person.BirthTime).Days / 365;}public static int GetAge(this Person person){if (person.DeathTime.HasValue)return (person.DeathTime.Value - person.BirthTime).Days / 365;elsereturn (DateTime.Now - person.BirthTime).Days / 365;}public static bool IsNullOrEmpty(this string str){return string.IsNullOrEmpty(str);}}
}

View Code

本文以同步至《C#基础知识巩固系列》

转载于:https://www.cnblogs.com/RYouHoo-923/p/8267517.html

【转】五、谈扩展方法的理解相关推荐

  1. c#扩展方法的理解(二:接口)

    namespace ExtensionInterfaceMethod {class Program{static void Main(string[] args){//使用接口变量来调用扩展方法IBa ...

  2. [C# 基础知识系列]专题十五:全面解析扩展方法

    引言:  C# 3中所有特性的提出都是更好地为Linq服务的, 充分理解这些基础特性后.对于更深层次地去理解Linq的架构方面会更加简单,从而就可以自己去实现一个简单的ORM框架的,对于Linq的学习 ...

  3. jQuery each、节点操作、动画演示、尺寸操作、扩展方法

    一.each 1.方式一:$.each(数组或者自定义对象,function(i,j){console.log(i,j)}) $.each(li,function(i,j){console.log(i ...

  4. JQuery添加扩展方法(理解$.extend(),与$.fn.extend()方法区别)

    为什么80%的码农都做不了架构师?>>>    理解$.extend(),与$.fn.extend()方法区别 1.$.extend()方法 $.extend()方法在JQuery中 ...

  5. c# 扩展方法奇思妙用高级篇五:ToString(string format) 扩展

    在.Net中,System.Object.ToString()是用得最多的方法之一,ToString()方法在Object类中被定义为virtual,Object类给了它一个默认实现: 1     p ...

  6. MongoDB:利用官方驱动改装为EF代码风格的MongoDB.Repository框架 五 --- 为ListMongoDBRef增加扩展方法...

    本次改动主要内容:为List<MongoDBRef>增加扩展方法 在MongoDB.Repository的使用过程中,发现在一个类中只定义一个List<MongoDBRef>是 ...

  7. 浅谈Spring IOC的理解

    浅谈Spring IOC的理解 学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊 ...

  8. Linq 下的扩展方法太少了,您期待的 MoreLinq 来啦

    一:背景 1. 讲故事 前几天看同事在用 linq 给内存中的两个 model 做左连接,用过的朋友都知道,你一定少不了一个叫做 DefaultIfEmpty 函数,这玩意吧,本来很流畅的 from. ...

  9. 『软件工程13』浅谈面向对象方法,统一建模语言UML

    浅谈面向对象方法UML 一.UML的含义 二.UML的主要内容 1.UML的概念模型 2.UML概念模型图例 三.UML的基本构造块 1.UML中的事物 (1)UML中的四种事物 (2)UML中各种事 ...

最新文章

  1. Caffe学习系列(18): 绘制网络模型
  2. 软件测试缺陷发生方法,软件测试缺陷分析方法简介
  3. Oracle 最后通牒:要 Java 8 更新先交钱!
  4. 深度 | 从各种注意力机制窥探深度学习在NLP中的神威
  5. [渝粤教育] 中国地质大学 事故应急救援 复习题 (2)
  6. Oracle优化的几个简单步骤
  7. java 枚举类使用反射
  8. MSP430X1XX系列ADC12和DMA详解(附带程序)(上)--ADC12详解与源码
  9. 媒体查询支持ie浏览器各版本的方法
  10. cross_val_score中scoring参数
  11. log4j在线视频教程【讲的很详细,不了解的情况下,听一遍就都了解】
  12. Devil May Cry 1 台词及翻译
  13. 以太猫合约之基础合约分析(一)
  14. 为什么matlab激活完后还要激活(Matlab2012b license失效解决办法)
  15. _C.cpython-36m-x86_64-linux-gnu.so: undefined symbol: _ZN3c105ErrorC1ENS_14SourceLocationERKSs
  16. 「太阁实验篇」SLA 联动静态路由实验
  17. 使用RXTXcomm 报错 #Problematic frame: # C [rxtxSerial.dll+0x4465]
  18. linux posix 消息队列,实现posix消息队列示例分享
  19. 暑期结束了我的实习,可我的大学也过半了 | 暑期实习总结
  20. C语言-十进制/二进制数的互相转化

热门文章

  1. 【BZOJ3242】【UOJ#126】【NOI2013】快餐店
  2. Hadoop集群高可用及zookeeper+kafka组件搭建
  3. android 使用xml定义自己的View
  4. Python的sort()
  5. windows 2012 apache php mysql_Windows Server 2012 R2搭建 Apache+PHP+MYSQL环境
  6. 数据中心如何建设,数据中心机房维护方法详解!
  7. 全面收紧!继新加坡后,又一地拟暂停数据中心建设
  8. 学校机房项目交换机的配置:
  9. 海外IDC数据中心为什么要做REITs
  10. linux线程并不真正并行,Linux系统编程学习札记(十二)线程1