前言


上一篇《从LINQ开始之LINQ to Objects(上)》主要介绍了LINQ的体系结构、基本语法以及LINQ to Objects中标准查询操作符的使用方法。
本篇则主要讨论LINQ to Objects中的扩展方法以及延迟加载等方面的内容。

扩展方法


扩展方法简介

  扩展方法能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或其他方式修改原始类型。扩展方法是静态方法,它是类的一部分,但实际没有放在类的源代码当中。
下面,我们来看一个简单示例,为上一篇中定义的Employee类添加扩展方法GetSeniority获取员工在本公司的工龄:

public static class EmployeeExtension
{/// <summary>/// 计算员工在本公司的工龄/// </summary>/// <param name="employee"></param>/// <returns></returns>public static long GetSeniority(this Employee employee){TimeSpan ts = DateTime.Now - employee.EntryDate;return (long)ts.TotalDays / 365;}
}

接下来,遍历employees列表,输出所有员工的姓名及工龄:

        //获取所有员工的姓名及在本公司的工龄foreach (var employee in employees){Console.WriteLine("EmployeeName: " + employee.EmployeeName + " Seniority: " + employee.GetSeniority());}//******************************Output*******************************//EmployeeName: Mike Seniority: 1//EmployeeName: Jack Seniority: 10//EmployeeName: Adolph Seniority: 0//EmployeeName: Antony Seniority: 6//EmployeeName: Asa Seniority: 2//EmployeeName: Bernie Seniority: 9//EmployeeName: Carl Seniority: 2//EmployeeName: Duncan Seniority: 7//EmployeeName: Aimee Seniority: 0//EmployeeName: Cassie Seniority: 3//*******************************************************************

由示例可以看出:
1)扩展方法中,可以访问被扩展类型的所有公有方法和属性。
2)第一个参数是要扩展的类型,以this关键字开头。
3)即使扩展方法是静态的,也要使用标准的实例方法语法进行调用。
下面的示例演示了如果扩展方法与类中的某个方法具有相同的签名,则扩展方法不会被调用。在Employee类中定义方法SayHello

    public void SayHello(){Console.WriteLine("Hello , I'm " + EmployeeName);}

在EmployeeExtension类中为Employee类定义扩展方法SayHello

    public static void SayHello(this Employee employee){Console.WriteLine("Hello , I'm " + employee.EmployeeName + " ,this is Extension Method");}

此时,新入职了一位同事Dave,调用SayHello方法向大家问好

        Employee dave = new Employee("011", "Dave", 30, new DateTime(2017, 5, 25), Sex.Male, Department.PD, 200000, new string[] { "climbing" });dave.SayHello();//******************************Output*******************************//Hello , I'm Dave//*******************************************************************

注意:此时调用的是Employee类下面的SayHello方法。

使用扩展方法来扩展接口

  把方法扩展到某个接口中,实现该接口的多个类就可以使用相同的实现代码。
以下示例介绍了扩展方法扩展接口的使用场景,首先,定义了一个接口IHobby,接口中包含Play方法

public interface IHobby
{void Play();
}

分别创建类Reading、Swimming、Shopping实现IHobby接口

public class Reading : IHobby
{public void Play(){Console.WriteLine("I'm Reading.");}
}public class Swimming : IHobby
{public void Play(){Console.WriteLine("I'm Swimming.");}
}public class Shopping : IHobby
{public void Play(){Console.WriteLine("I'm Shopping.");}
}

此时,我们需要在实现IHobby接口的类增加一个的方法ShareFeelings,输出I'm happpy.当然,可以在接口上新增一个方法,然后将实现该接口的类逐个添加ShareFeelings方法,假如实现该接口的类很多,使用扩展方法,就可以大大的减少代码的修改量,测试起来也非常简单。

    public static void ShareFeelings(this IHobby hobby){Console.WriteLine("I'm happy.");}

使用接口变量来调用扩展方法

    IHobby hobby = new Reading();hobby.ShareFeelings();//******************************Output*******************************//I'm happy.//*******************************************************************

LINQ中的扩展方法

  LINQ为IEnumerable<T>接口提供给了各种扩展方法,以便用户在实现了该接口的任意集合上使用LINQ查询。本节主要研究LINQ中Where扩展方法的实现,这个扩展方法位于System.Linq命名空间下的Enumerable类中。

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {if (source == null) throw Error.ArgumentNull("source");if (predicate == null) throw Error.ArgumentNull("predicate");if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate);if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate);if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate);return new WhereEnumerableIterator<TSource>(source, predicate);}

由上述代码可以看出,Where方法是对IEnumberable接口的扩展,需要传入一个委托参数predicate,该委托要求返回布尔类型。假设我们对List<T>类型的对象调用Where方法,则返回一个WhereListIterator<TSource>对象。WhereListIterator<TSource>类派生自Iterator<TSource>类,下面是Iterator<TSource>类的源码,这里我们只需要注意GetEnumerator方法,该方法对于同一个线程,返回同一个迭代器,不同线程则克隆一个,并将state属性设置为1。

    abstract class Iterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>{int threadId;internal int state;internal TSource current;public Iterator() {threadId = Thread.CurrentThread.ManagedThreadId;}public TSource Current {get { return current; }}public abstract Iterator<TSource> Clone();public virtual void Dispose() {current = default(TSource);state = -1;}public IEnumerator<TSource> GetEnumerator() {if (threadId == Thread.CurrentThread.ManagedThreadId && state == 0) {state = 1;return this;}Iterator<TSource> duplicate = Clone();duplicate.state = 1;return duplicate;}public abstract bool MoveNext();public abstract IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector);public abstract IEnumerable<TSource> Where(Func<TSource, bool> predicate);object IEnumerator.Current {get { return Current; }}IEnumerator IEnumerable.GetEnumerator() {return GetEnumerator();}void IEnumerator.Reset() {throw new NotImplementedException();}}

此时,再回到WhereListIterator<TSource>类,该类重写了MoveNext方法。首先,调用GetEnumerator方法获得一个枚举器,在While循环中,只要MoveNext方法返回true,就用Current属性获得集合当前的元素,并使用委托predicate引用的方法处理该元素,返回剩余元素中满足条件的第一个元素。当遍历结束,调用Dispose方法释放非托管资源,并将state属性设置为-1。

    class WhereListIterator<TSource> : Iterator<TSource>{List<TSource> source;Func<TSource, bool> predicate;List<TSource>.Enumerator enumerator;public WhereListIterator(List<TSource> source, Func<TSource, bool> predicate) {this.source = source;this.predicate = predicate;}public override Iterator<TSource> Clone() {return new WhereListIterator<TSource>(source, predicate);}public override bool MoveNext() {switch (state) {case 1:enumerator = source.GetEnumerator();state = 2;goto case 2;case 2:while (enumerator.MoveNext()) {TSource item = enumerator.Current;if (predicate(item)) {current = item;return true;}}Dispose();break;}return false;}public override IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) {return new WhereSelectListIterator<TSource, TResult>(source, predicate, selector);}public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) {return new WhereListIterator<TSource>(source, CombinePredicates(this.predicate, predicate));}}

源码传送门:http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,dc4c4c53ff606bc0

延迟加载


延迟执行

  在运行期间定义查询表达式时,查询不会运行,只有在迭代时才进行计算。
下面的示例定义了一个LINQ查询,从集合中找出姓名以A开头的所有员工,因为迭代在查询定义时不会进行,而是在执行每个foreach语句时进行。

        var nameStartWithA = from e in employeeswhere e.EmployeeName.StartsWith("A")select e;Console.WriteLine("First iteration : ");foreach (var item in nameStartWithA){Console.WriteLine(item.EmployeeName);}Console.WriteLine();employees.Add(new Employee("011", "Lily", 25, new DateTime(2017, 5, 29), Sex.Female, Department.HR, 100000, new string[] { "shopping" }));employees.Add(new Employee("012", "Leo", 28, new DateTime(2017, 5, 29), Sex.Male, Department.IT, 200000, new string[] { "reading" }));employees.Add(new Employee("013", "Amelia", 29, new DateTime(2017, 5, 29), Sex.Female, Department.PD, 200000, new string[] { "reading", "run" }));employees.Add(new Employee("014", "Ava", 32, new DateTime(2017, 5, 29), Sex.Female, Department.PD, 400000, new string[] { "swimming" }));Console.WriteLine("Second iteration : ");foreach (var item in nameStartWithA){Console.WriteLine(item.EmployeeName);}//******************************Output*******************************//First iteration ://Adolph//Antony//Asa//Aimee//Second iteration ://Adolph//Antony//Asa//Aimee//Amelia//Ava//*******************************************************************

补充:延迟加载的工作原理可从上一章节中对源码的分析得出。

立即执行

  查询在定义表达式时立即执行,而不是在迭代中进行。通过调用ToArray()、ToList()等扩展方法可以实现此项操作。
下面,我们修改上一节中的示例来说明:

        var nameStartWithA = (from e in employeeswhere e.EmployeeName.StartsWith("A")select e).ToList();Console.WriteLine("First iteration : ");foreach (var item in nameStartWithA){Console.WriteLine(item.EmployeeName);}Console.WriteLine();employees.Add(new Employee("011", "Lily", 25, new DateTime(2017, 5, 29), Sex.Female, Department.HR, 100000, new string[] { "shopping" }));employees.Add(new Employee("012", "Leo", 28, new DateTime(2017, 5, 29), Sex.Male, Department.IT, 200000, new string[] { "reading" }));employees.Add(new Employee("013", "Amelia", 29, new DateTime(2017, 5, 29), Sex.Female, Department.PD, 200000, new string[] { "reading", "run" }));employees.Add(new Employee("014", "Ava", 32, new DateTime(2017, 5, 29), Sex.Female, Department.PD, 400000, new string[] { "swimming" }));Console.WriteLine("Second iteration : ");foreach (var item in nameStartWithA){Console.WriteLine(item.EmployeeName);}//******************************Output*******************************//First iteration ://Adolph//Antony//Asa//Aimee//Second iteration ://Adolph//Antony//Asa//Aimee//*******************************************************************

从输出结果中可以看出,两次迭代输出的结果相同,但是集合中值改变了。
示例代码下载:https://github.com/Answer-Geng/LINQ

转载于:https://www.cnblogs.com/zhangyingai/p/7074508.html

从LINQ开始之LINQ to Objects(下)相关推荐

  1. Pro LINQ 之三:LINQ to DataSet

    写在前面 将LINQ to DataSet单独放一篇,是因为随后的LINQ to SQL默认只支持MS SQL Server.只有LINQ to DataSet能在没有相应Data Provider帮 ...

  2. LINQ编程之LINQ to SQL

    最近比较忙,博客更新的比较慢,请大家谅解! 今天来简单介绍一下LINQ to SQL的知识.(用实例) 什么是LINQ to SQL? LINQ to SQL是基于关系数据的.NET语言集成查询,用于 ...

  3. Linq初级班 Linq To XML体验(基础篇)

    LINQ To XML体验(基础) 这两天开始学习LINQ to XML的知识,我会继续把自己的感想和示例发布给初学者们学习的,一样欢迎高手们多多指点,请勿使用过激语言,针锋相对,我是个初学者,自知还 ...

  4. LINQ圣经——《LINQ实战》

    媒体评论 本书作者是 LINQ 社区中的重要成员,他们在书中展示了这一技术的精髓--快去享受这一盛宴吧. --Matt Warren ,微软主架构师, LINQ 之父 LINQ 方面的杰作!深入.全面 ...

  5. LINQ系列:LINQ to SQL Exists/In/Any/All/Contains

    1. Any 返回没有Product的Category var expr = from c in context.Categorieswhere !c.Products.Any()select c; ...

  6. LINQ体验(6)——LINQ to SQL语句之Join和Order By

    LINQ体验(6)--LINQ to SQL语句之Join和Order By Join操作 适用场景:在我们表关系中有一对一关系,一对多关系,多对多关系等.对各个表之间的关系,就用这些实现对多个表的操 ...

  7. C# LINQ系列:LINQ to DataSet的DataTable操作 及 DataTable与Linq相互转换

    LINQ to DataSet需要使用System.Core.dll.System.Data.dll和System.Data.DataSetExtensions.dll,在项目中添加引用System. ...

  8. 光脚丫学LINQ(014):LINQ to SQL简介

    视频演示:http://u.115.com/file/f29f7838f6 LINQ to SQL 是 .NET Framework 3.5 版的一个组件,提供了用于将关系数据作为对象管理的运行时基础 ...

  9. 【LINQ语句】LINQ语句

    前言 LINQ(Language Integrated Query)语言集成查询是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式 ...

最新文章

  1. 启信宝CEO陈青山:AI+大数据驱动下的金融新生态
  2. 万字干货 | Python后台开发的高并发场景优化解决方案
  3. bpmn js 生成json_你不知道的 tsconfig.json
  4. 在各路由器进行OSPF的基本配置
  5. poj-1064Cable master(二分)
  6. 进程线程协程-基本概念及特点
  7. linux5 syscall 流程_Linux的上的程序是如何运行的,api机制是怎么样?
  8. 用户界面设计参考 (ZT)
  9. C++并发与多线程(一)线程传参
  10. linux 网络编程connection refused,《UNIX网络编程》第一个例子出现“connect error: Connection refused”错误提示信息?...
  11. 生信过程中的各种文件格式
  12. 敌兵布阵 HDU - 1166(树状数组-模板线段树写法)
  13. log 1用计算机怎么打开,log是什么?log怎么打开?
  14. 前段时间的世界互联网大会
  15. AI芯片:寒武纪DianNao,英伟达NVDLA和谷歌TPU1的芯片运算架构对比分析
  16. php微信自动回复开发,PHP微信开发之文本自动回复
  17. ubuntu安装CD-HIT
  18. HanLP极致简繁转换详细讲解 1
  19. JS给指定元素添加父元素
  20. GO_strings.Reader

热门文章

  1. DevExpress学习之DevExpress.XtraGrid.Columns.GridColumn
  2. CSS Sprites图片拼合生成器实现思路
  3. 去了新公司,物理通过
  4. Deep Learning回顾之LeNet、AlexNet、GoogLeNet、VGG、ResNet
  5. 运用深度学习教机器人理解自然语言
  6. Facebook发布人工智能产品DeepText:能以人类智商
  7. 海尔智慧家庭食联网:为每1个家庭配备专属健康顾问
  8. hdu 1016 Prime Ring Problem(DFS)
  9. 【译】为什么这样宏定义#define INT_MIN (-2147483647 - 1)?
  10. 如何激励用户为你的app评分?