了解LINQ

了解LINQ

本文主要的是泛谈LINQ是啥?以及常见的用法大纲如下:

  • LINQ的那些根基
  • LINQ的一些基本用法

LINQ的根基#

IEnumerable和IEnumerator#

为啥能够被foreach?

实际上,能够被foreach的对象,一定是实现了带有返回值的IEnumerator的GetEnumerator()方法的接口,而.NET内置的该接口则是IEnumerable,一般指的是IEnumerable泛型接口,让我们来看看IEnumerator接口有啥成员:

Copy   public interface IEnumerator   {       object Current       {           get;       }       bool MoveNext();       void Reset();   }
  • Current:集合当前的对象
  • MoveNext:是否能够移动到下一次
  • Reset

因此,实际上我们进行foreach的时候,等价于:

Copyvar animals = new List<string>() { "Cat", "Dog", "Pig" };foreach (var animla in animals){    Console.WriteLine(animla);}Console.WriteLine("-----------");var enumerator = animals.GetEnumerator();while (enumerator.MoveNext()){    Console.WriteLine(enumerator.Current);}

输出结果:

CopyCatDogPig-----------CatDogPig

而能被LINQ的对象就是一个实现了IEnumerable的可被枚举的集合

LINQ的基本用法#

扩展方法在LINQ的应用:LINQ的流式语法#

LINQ的方法一般都是通过扩展方法了扩展的,就如最常用的几个,Where,Any,例如,我实现了一个跟Where功能类似的简化版:

Copy public static class MyListExtension{    public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> enumable, Func<T, bool> func)    {        foreach (var item in enumable)        {            if (func(item))            {                yield return item;            }        }    }}

其实为啥会提到用扩展方法呢?就是因为LINQ就是为了简单的能够处理复杂集合的数据,那么扩展方法就能够实现较为简单的链式查询,例如:

Copyvar result= animals.MyWhere(t => t is "Cat" or "Dog").Select(t=>t.ToUpper()).ToList();result.ForEach(t =>Console.WriteLine(t));

输出结果:

CopyCATDOG

LINQ的查询表达式:LINQ的查询语法#

假如上述的例子有LINQ的查询表达式来编写,则写法是这样:

Copyvar result = (from t in animals              where t is "Cat" or "Dog"              select t.ToUpper()).ToList();result.ForEach(t => Console.WriteLine(t));

输出结果也是一样的:

CopyCATDOG

LINQ的延迟执行:IQueryable#

首先我们来看看IQueryable的接口定义:

Copy    public interface IQueryable : IEnumerable    {        Type ElementType        {            get;        }         Expression Expression        {            get;        }        IQueryProvider Provider        {            get;        }    }

我们可以看到实际上IQueryable是继承了IEnumerable,因此同样具备其特性,然后主要看其三个属性

  • ElementType:集合的类型
  • Expression:表达式树,这是延迟执行的重点,下面我们会一窥究竟
  • IQueryProvider:IQueryable创建表达式树和执行的部分
Copy    public interface IQueryProvider    {          IQueryable CreateQuery(Expression expression);          IQueryable<TElement> CreateQuery<TElement>(Expression expression);        object? Execute(Expression expression);        TResult Execute<TResult>(Expression expression);    }

我们先来看段代码:

Copyvar result1 = (from t in animals                where (t.Equals( "Cat") || t.Equals("Dog"))                select t.ToUpper()).AsQueryable();Console.WriteLine($"Expression:{ result1.Expression.ToString()}");Console.WriteLine($"ExpressionType:{result1.Expression.GetType()}");foreach (var item in result1){    Console.WriteLine(item);}Console.WriteLine("---------------");var result2 = from t in result1              where  t.Contains("CAT")              select t;Console.WriteLine($"Expression:{ result2.Expression.ToString()}");Console.WriteLine($"ExpressionType:{result2.Expression.GetType()}");foreach (var item in result2){    Console.WriteLine(item);}

输出如下:

CopyExpression:System.Linq.Enumerable+WhereSelectListIterator`2[System.String,System.String]ExpressionType:System.Linq.Expressions.ConstantExpressionCATDOG---------------Expression:System.Linq.Enumerable+WhereSelectListIterator`2[System.String,System.String].Where(t => t.Contains("CAT"))ExpressionType:System.Linq.Expressions.MethodCallExpression2CAT

我们从输出可以证明,实际上在返回result1和result2,就是通过IQueryProvider不断地在拼接表达式树,而最后通过foreach或者ToList等操作的时候,则才是真正调用Execute方法执行当前的IQueryable里的那个表达式树属性Expression,而像LINQ To Sql或者是EFCore等需要IQueryable这种解释型的就是去实现了IQueryProvider里面的方法

参考#

源码#

  • BlogCodeSample/LinqSample at main · ZhengDaoWang/BlogCodeSample
免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部