五、谈扩展方法的理解
2018-06-17 21:36:31来源:未知 阅读 ()
为什么要用扩展方法
在说什么是扩展方法之前我们先来说说为什么要用扩展方法。
首先我们定义一个 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; else return (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; else return (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; else return (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; else return (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; else return 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; else return 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; else return 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; }
上面所有的都只是扩展方法的附加用处,扩展方法真正的威力是为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代码:
虽然编译后的代码是一样的,但是做为程序员的我们更喜欢哪种方式呢?
总结:
我们在对扩展方法的怎么使用疑惑或者忘记了规则的时候,我们不用去查找资料说:
- 第一个参数是要扩展或者要操作的类型,这称为"被扩展的类型"
- 为了指定扩展方法,要在被扩展的类型名称前面附加this修饰符
- 要将方法作为一个扩展方法来访问,要用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; else return (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; else return (DateTime.Now - person.BirthTime).Days / 365; } public static bool IsNullOrEmpty(this string str) { return string.IsNullOrEmpty(str); } } }
本文以同步至《C#基础知识巩固系列》
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:图片检测
- Mysql下中文乱码的解决方法 2020-02-26
- windows系统中mysql自动备份的方法 2020-02-26
- MySQL中使用WHERE子句的方法 2019-10-25
- MySQL的默认密码的修改方法 2019-10-25
- 关于php开启错误提示的总结 2019-10-09
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash