目录
  • 前言
  • LINQ的根基
    • IEnumerable和IEnumerator
  • LINQ的基本用法
    • 扩展方法在LINQ的应用:LINQ的流式语法
    • LINQ的查询表达式:LINQ的查询语法
    • LINQ的延迟执行:IQueryable
  • 附:将内存中对象转换为 XML
    • 参考
      • 源码
        • 总结

          前言

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

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

          LINQ的根基

          IEnumerable和IEnumerator

          为啥能够被foreach?

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

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

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

          var 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);
          }
          
          

          输出结果:

          Cat

          Dog

          Pig

          ———–

          Cat

          Dog

          Pig

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

          LINQ的基本用法

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

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

           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就是为了简单的能够处理复杂集合的数据,那么扩展方法就能够实现较为简单的链式查询,例如:

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

          输出结果:

          CAT

          DOG

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

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

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

          输出结果也是一样的:

          CAT

          DOG

          LINQ的延迟执行:IQueryable

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

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

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

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

          我们先来看段代码:

          var 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);
          }
          

          输出如下:

          Expression:System.Linq.Enumerable+WhereSelectListIterator`2[System.String,System.String]

          ExpressionType:System.Linq.Expressions.ConstantExpression

          CAT

          DOG

          —————

          Expression:System.Linq.Enumerable+WhereSelectListIterator`2[System.String,System.String].Where(t => t.Contains(“CAT”))

          ExpressionType:System.Linq.Expressions.MethodCallExpression2

          CAT

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

          附:将内存中对象转换为 XML

          LINQ 查询可以轻松地在内存中数据结构、SQL 数据库、ADO.NET 数据集和 XML 流或文档之间转换数据。 以下示例将内存中数据结构中的对象转换为 XML 元素。

          class XMLTransform
          {
              static void Main()
              {
                  // Create the data source by using a collection initializer.
                  // The Student class was defined previously in this topic.
                  List<Student> students = new List<Student>()
                  {
                      new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores = new List<int>{97, 92, 81, 60}},
                      new Student {First="Claire", Last="O'Donnell", ID=112, Scores = new List<int>{75, 84, 91, 39}},
                      new Student {First="Sven", Last="Mortensen", ID=113, Scores = new List<int>{88, 94, 65, 91}},
                  };
          
                  // Create the query.
                  var studentsToXML = new XElement("Root",
                      from student in students
                      let scores = string.Join(",", student.Scores)
                      select new XElement("student",
                                 new XElement("First", student.First),
                                 new XElement("Last", student.Last),
                                 new XElement("Scores", scores)
                              ) // end "student"
                          ); // end "Root"
          
                  // Execute the query.
                  Console.WriteLine(studentsToXML);
          
                  // Keep the console open in debug mode.
                  Console.WriteLine("Press any key to exit.");
                  Console.ReadKey();
              }
          }

          此代码生成以下 XML 输出:

          <Root>  
            <student>  
              <First>Svetlana</First>  
              <Last>Omelchenko</Last>  
              <Scores>97,92,81,60</Scores>  
            </student>  
            <student>  
              <First>Claire</First>  
              <Last>O'Donnell</Last>  
              <Scores>75,84,91,39</Scores>  
            </student>  
            <student>  
              <First>Sven</First>  
              <Last>Mortensen</Last>  
              <Scores>88,94,65,91</Scores>  
            </student>  
          </Root>

          参考

          《C#7.0核心技术指南》

          源码

          BlogCodeSample/LinqSample at main · ZhengDaoWang/BlogCodeSample

          总结

          声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。