zoukankan      html  css  js  c++  java
  • Linq学习

    1. 前言    

    2. Lambda表达式概念

    3. Lambda表达式树的概念和示例

    4. Lambda表达式的简单应用

    5. 总结

    前言

     

    在LINQ刚发布的时候,一直也没有时间去研究下LINQ,特别是在当时各种LINQ to ***纷纷出现,看得眼花缭乱。

    在LINQ中增加的lambda表达式一直觉得挺神秘的,看到各位高手把lambda表达式运用得如火纯清,很是羡慕. 最近抽

    空学习了解了下LINQ, 对学习的过程进行记录。文章基础,高手请飘过 :-)

    计划学习的主要内容是lambda表达式,LINQ to Objects, LINQ to XML.  三个部分。

    学习之前,推荐一款帮助学习LINQ的优秀工具LINQPad,下载地址:LINQPad下载

    【另注:学习过程未免出现差错,欢迎指正】

    Lambda表达式的概念

    什么是lambda表达式?Lambda 表达式是一种匿名函数,它可以包含表达式和语句,并且可用于创建委托或

    表达式目录树类型。我们使用lambda表达式可以帮助我们编写精简和紧凑的代码,许多操作中允许自定义排序和过

    滤的函数,在.NET2.0的时候通常使用委托函数来实现,在.NET3.5可以使用lambda表达式。

    现在举例说明lambda表达式:  Func<int,int> addOne= item=> item+1 ,其中操作符 “=>”读作“Goes to”,

    可以理解为操作符左边的是函数的参数,操作符右边是函数体内容。上面我们定义的lambda表达式等同于函数如下:

     

    1 int addOne(int item)
    2 {
    3 return item+1;
    4 }

    那么什么样的表达式才是合法的lambda表达式呢?

    1. lambda表达式可以是多个参数。 如:  (item1,item2)=>item1+item2;

    2. lambda表达式可以是0个参数。 如: ()=>"csharp";

    3. lambda表达式可以显示指定参数类型。 (int item1,string item2)=>item1+item2;

    4. lambda表达式函数体可以使用多条语句. (item1)=>{string ret="hello"+item1;return ret;};

     

    使用lambda表达式的时候,不得不提到泛型委托。在上面我们定义的表达式如:(item1,item2)=>item1+item2;

    只是定义的表达式,我们如何调用呢?我们可以定义自己的函数委托来引用lambda表达式,如下

    1 public delegate int addOneDelegate(int item1,int item2);
    2  void Main()
    3 {
    4 addOneDelegate fun=(item1,item2)=>item1+item2;
    5 var result=fun(123,456);
    6 result.Dump("结果");
    7 }
    8
    9
    10 .结果
    11  579

    在这里我们可以使用.NET类库中已经提供的泛型委托Func<T>和Action<T>来引用lambda表达式.代码如下

     

    1 void Main()
    2 {
    3 Func<int,int> fun=(item1,item2)=>item1+item2;
    4 var result=fun(123,456);
    5 result.Dump("结果");
    6 }
    7
    8 .结果
    9  579

    关于Func<T>是泛型委托,最后的一个类型是指返回结果的类型,前面都是输入参数类型,上面的例子中,我们

    的输入类型是INT,返回类型也是INT。同样如果我们定义Func<int,string,bool>,是指输入参数有两个,一个是int

    类型,一个是string类型,函数返回是bool类型。使用泛型委托可以帮助我们方便引用lambda表达式。Func<T>提供

    了多个重载,如下

     

    1 public delegate T Func<T>();
    2  public delegate T Func<A0, T>(A0 arg0);
    3  public delegate T Func<A0, A1, T>(A0 arg0, A1 arg1);
    4  public delegate T Func<A0, A1, A2, T>(A0 arg0, A1 arg1, A2 arg2);
    5  public delegate T Func<A0, A1, A2, A3, T>(A0 arg0, A1 arg1, A2 arg2, A3 arg3);
      

     

    在这里,需要提到一些关于lambda表达式的特性和规则。

    1. lambda表达式的引用变量必须是显式类型。编译器对lambda表达式的类型推断是通过返回的引用变量的类型指定。

    如下面的语句是非法的。

     

    1 void Main()
    2 {
    3 var c=n=>n+1;//Error,Cannot assign lambda expression to an implicitly-typed local variable
    4   Func<string> cc=>n+1;//Ok
    5  }

     

    2. 在lambda表达式中可以直接访问本地变量和全局变量。

     

     

    1 public static string grobalVar="grobal string";
    2  void Main()
    3 {
    4 string localVar="local string";
    5 Func<string,string> fun= n=> n+" can access "+grobalVar+" and "+localVar;
    6 fun("lambda").Dump();
    7 }
    8
    9
    10 结果:
    11 lambda can access grobal string and local string
    12  

    3. lambda表达式的参数可以是ref或out方式传入,在通过ref或out方式传入的时候必须指定参数的具体类型。

     

     

    1 public delegate int RefParameterFunction(ref int n);
    2  void Main()
    3 {
    4 int x=10;
    5 RefParameterFunction fun= (ref int n)=> n++;
    6 fun(ref x);
    7 x.Dump();
    8 }
    9
    10
    11 结果
    12  11

    4. lambda表达式的参数可以支持不定参数数传入。

     

     

    1 public delegate int AddFunction(params int[] ints);
    2  void Main()
    3 {
    4 int[] x={1,2,3,4,5,6,7,8,9};
    5 AddFunction fun= (items)=>
    6 {
    7 int count=0;
    8             foreach(int item in items)
    9 {count=count+item;}
    10             return count;
    11 };
    12 fun(x).Dump("求和:");
    13 }
    14
    15
    16 求和:
    17  45

    Lambda表达式树的概念和示例

     

    Lambda另一个强大的特性就是表达式树,lambda表达式都可以通过表达式树来描述,就不用在代码

    中直接编写表达式。这样的优势就是表达式可以在运行的时候编译运行,而且可以对lambda表达式进行动态修改。

    要使用lambda表达式树,首先提到一个表达式的泛型类Expression<T>,(域名空间System.Linq.Expressions),

    这个类是保存表达式的结构信息。我们把Expession看作一棵树结构,每个结点都是由两部分组成,左树和右树,一直这样

    递归下去。这里需要说明一下,刚开始使用表达式树的时候容易和表达式产生混淆,比如:

     

    1 void Main()
    2 {
    3
    4 Expression<Func<int,int>> tree = x=>x+1;
    5 Func<int,int> exp= x=> x+1;
    6
    7 tree(1);//'tree' is a 'variable' but is used like a 'method'
    8   exp(1);//输出2
    9  }
    10
    11  

    注意:tree只是lambada表达式的树形结构信息,并不是函数可以直接调用。

     

    现在我们对lambda表达式的树结构输出来查看下,举例:我们编写一个验证三角形是否直角三角形,通过沟谷定律,

    我们很容易编写lambda表达式为:

     

    1 (x,y,z)=> (x*x+y*y)==z*z

    现在我们使用LINQPad的Dump()函数进行输出显示:

     

    1 void Main()
    2 {
    3 Expression<Func<int,int,int,bool>> tree = (x,y,z)=>(x*x+y*y)==z*z;
    4 tree.Dump();
    5 }

    输出结果如下:

     

    通过输出的图形,我们可以清楚的看出整个lambda表达式是由LEFT和RIGHT两部分组成的,Left部分和right部分之间

    的关系通过 NodeType属性指定,所有的NodeType类型通过枚举(System.Linq.Expressions.ExpressionType )定义,

    而结点的Type可以看作返回类型,比如我们定义的  tree的Type是Func<int,int,int,bool>,而Type是Lambda。


    那么如何把表达式树转换为可以直接使用的函数呢?Expression类提供了函数Compile(),就可以把我们定义的lambda

    表达式树编译为实际的函数,代码如下:

     

    1 void Main()
    2 {
    3 Expression<Func<int,int,int,bool>> tree = (x,y,z)=>(x*x+y*y)==z*z;
    4 Func<int,int,int,bool> fun= tree.Compile();
    5 fun(3,4,5).Dump();
    6 }
    7   
    9 结果
    10 True

     

    我们了解到了lambda表达式树的基本概念,现在我们自行构造一个lambda表达式树。还是以上面的验证是否是直角

    三角形为例,我们通过System.Linq.Expressions提供了表达式类来构造这个表达式,不参考LINQPad输出的结构。现在

    我们分析表达式的树结构,(x,y,z) => (x*x + y*y)== z*z 按照操作符把表达式分为left tree和right tree。比如首先

    我们把整个表达式分为左树:x*x + y*y,  右树:z*z, 关系:Equal,以此画出阿里如下:

    我们已经把表达式树分析出来,现在我们开始使用.NET提供的表达式类来构造这棵表达式树,在这棵树比较简单,

    我们比较用到的类包括二元表达式类(BinaryExpression)和参数表达式类(ParameterExpression)。现在我们

    从树的叶结点开始构造。

     

    首先我们需要制定表达式中参数和参数的类型。

     

    1 ParameterExpression expX= Expression.Parameter(typeof(int),"x");
    2 ParameterExpression expY= Expression.Parameter(typeof(int),"y");
    3 ParameterExpression expZ= Expression.Parameter(typeof(int), "z");
      

     

    接着我们使用二元表达式将参数表达式关联起来,X和X,Y和Y,Z和Z,二元关系都是乘.

     

    1 BinaryExpression mulX = Expression.Multiply(expA, expA);
    2 BinaryExpression mulY= Expression.Multiply(expY, expY);
    3 BinaryExpression mulZ= Expression.Multiply(expZ, expZ);
      

    然后我们将X*X+Y*Y通过 加二元表达式关联起来.

     

    1 BinaryExpression addXY = Expression.Add(mulX,mulY);

     

    最后我们将X*X+Y*Y 和Z*Z通过 等于二元表达式关联起来.

     

    BinaryExpression final= Expression.Equal(mulZ, addXY);

    现在我们构造完成后,可以通过编译来执行,下面是完整的代码:

     

    1 void Main()
    2 {
    3 ParameterExpression expX= Expression.Parameter(typeof(int),"x");
    4 ParameterExpression expY= Expression.Parameter(typeof(int),"y");
    5 ParameterExpression expZ= Expression.Parameter(typeof(int), "z");
    6 BinaryExpression mulX= Expression.Multiply(expX, expX);
    7 BinaryExpression mulY= Expression.Multiply(expY, expY);
    8 BinaryExpression mulZ= Expression.Multiply(expZ, expZ);
    9 BinaryExpression addXY = Expression.Add(mulX,mulY);
    10 BinaryExpression final= Expression.Equal(mulZ, addXY);
    11 Expression<Func<int, int, int, bool>> square =
                    Expression.Lambda
    <Func<int, int, int, bool>>(final, expX, expY, expZ);
    12 Func<int, int, int, bool> xx= square.Compile();
    13 xx(3,4,5).Dump();
    14 }
    15
    16
    17 结果:
    18 True
    19  

    Lambda表达式的简单应用

     

    1. 对数组的自定义排序。

     

    1 void Main()
    2 {
    3 string[] items={"csharp","cpp","python","perl","java"};
    4 List<string> list=items.ToList();
    5 list.Sort((x,y)=>y.Length-x.Length);
    6
    7 list.Dump();
    8 }
    9
    10 结果:
    11 python
    12 csharp
    13 perl
    14 java
    15 cpp    

    2. 对数组数据进行搜索

     

    1 void Main()
    2 {
    3 string[] items={"csharp","cpp","python","perl","java"};
    4 List<string> list=items.ToList();
    5 var result= list.FindAll(x=> x.Length==4);
    6
    7 result.Dump();
    8 }
    9
    10 结果:
    11 perl
    12 java

     

    3. 对数组数据进行直接更新

     

    1 void Main()
    2 {
    3 string[] items={"csharp","cpp","python","perl","java"};
    4 var result= items.Select(n=> n+" : "+n.Length);
    5 result.Dump();
    6 }
    7
    8 结果:
    9 csharp : 6
    10 cpp : 3
    11 python : 6
    12 perl : 4
    13 java : 4
        

     

     

     

    总结

    这篇学习记录对Lambda表达式和Lambda表达式树的最基础进行描述,对lambda没有更多深入的研究。比如在表达式树

    的的动态修改,更多复杂的lambda表达式,lamdba表达式树对更复杂函数的构造。如果有兴趣,可以继续研究。

    另:这是我发布的第一篇随笔,写一篇随笔原来也是挺艰难,水平有限,希望大家指正,谢谢。

    1.么是LINQ to Objects?

      

    Linq to objects是使用LINQ查询内存中的数据集合,数据集合都需要实现了 IEnumerable  or IEnumerable<(Of <(T>)>) 接口。比如我们需要对

    一个数组进行排序,就可以使用SQL风格的LINQ to Objects进行这个数组进行排序。

    在使用LINQ to objects之前,需要对了解几个概念。IEnumerable<T>泛型接口,允许对实现这个接口的数据集合进行枚举集合内的元素,当然也包

    括.NET2.0之前的非泛型接口。 序列(Sequences),我们对实现了IEnumerable<T>接口的集合称为序列。标准查询符,是LINQ提供的的查询的符号。

    下面是操作符列表:

    from, in 定义LINQ查询表达式结构,从指定数据集合中提取出数据或新的数据集合
    where 对从数据集合中的数据进行约束限制
    select 从数据集合中选取出数据
    join, on, equals, into 通过特定的键对数据集进行联结
    orderby,ascending,descending 对提取出来的子集按升序或降序进行排序
    group, by 对提取出来的子集按特定的值进行分组

    大多数的操作符都是通过IEnumerable<T>的扩展函数的方式以及System.Linq.IEnumerable的静态函数提供查询相关功能,但是通过扩展函数的方

    式可以方便对序列(Sequences)进行复杂的查询操作,而不需要每次在调用System.Linq.IEnumerable的静态函数的时候将数据集合作为第一个参数传入,

    代码示例如下。

     

    1 void Main()
    2 {
    3 string[] items={"csharp","cpp","python","perl","java"};
    4 var lens1= System.Linq.Enumerable.Select(items,n=>n.Length);//静态函数
    5   var lens2= items.Select(items,n=>n.Length);//扩展函数
    6   lens1.Dump("长度列表1");
    7 lens2.Dump("长度列表2");
    8 }

    在.NET2.0之前有很多遗留的集合类,比如ArrayList,Stack,Hashtable等非泛型的集合类, 由于没有实现IEnumerable<T>接口,因此我们不能直接

    使用LINQ对他们进行查询,而需要通过函数Cast() 或者 OfType()进行转换为序列。示例如下:

     

    1 void Main()
    2 {
    3 ArrayList list=new ArrayList(){"csharp","java","vb"};
    4 //编译错误: Could not find an implementation of the query pattern for source type
    5 //'System.Collections.ArrayList'. 'Select' not found. Consider explicitly
    6 //specifying the type of the range variable 'item'.
    7   var error=from item in list
    8 select item;
    9 //OK
    10   var ok=from item in list.Cast<string>()
    11 select item;
    12 //OK too
    13   IEnumerable<string> ok2=list.OfType<string>().Select(n=>n);
    14 }

    2. LINQ的延迟查询

    在上一个示例代码中,IEnumerable<string> ok2=list.OfType<string>().Select(n=>n); ok2保存是什么呢?我们常常以为IEnumerable<T>就是

    存了查询出来的序列结果,其实不然,select函数并没有把查询出来的结果返回,而只有在IEnumerable<T>被遍历列举的时候才会真正返回查询结果集合。

    下面我们通过代码示例来验证:

     

    1 void Main()
    2 {
    3 string[] items={"csharp","vb","java","cpp","python"};
    4 IEnumerable<string> result=items.Where(n=>n.Length>4);
    5 //显示查询结果
    6   Console.WriteLine("--------enumerated-----------");
    7 foreach(string item in result)
    8 {
    9 Console.WriteLine(item);
    10 }
    11 items[0]="not exist"; //修改数组的内容
    12 //再次显示查询结果
    13   Console.WriteLine("--------enumerated again-----------");
    14 foreach(string item in result)
    15 {
    16 Console.WriteLine(item);
    17 }
    18 }
    19


    结果:
    --------enumerated-----------
    csharp
    python
    --------enumerated again-----------
    not exist
    python
     

    我们注意示例代码,result两次遍历发生了变化,因为我们直接修改了数组的内容。我们可以得出结论:查询被延迟了,当我们对IEnumerable<T>进行

    遍历列举的时候,序列中的元素才会被yield返回。

    现在我们来讨论下延迟查询的作用。延迟查询有什么有点和什么缺点呢?

    优点:1. 执行过程中减少资源的占用,提高性能。数据只在使用的时候才读取出来,而不用每次查询都要读取出来。

            2. 实际保存的是查询条件和约束,源数据发生变化不会影响查询结果的准确性。

     

    1 void Main()
    2 {
    3 int[] list={1,2,3,4,5,6,7};
    4 var result=list.Where(n=>n>4);
    5 result.Dump("结果是:5,6,7");
    6 list[6]=0;
    7 result.Dump("结果是:5,6");
    8 }

           在例子中,我们查询条件是大于4的所有数字,但是中途数组中的数据发生变化,原来的数字 7 修改为了 0,因此实际我们的查询结果应该也会变化。

    因此延迟查询可以保证我们一直能够得到想要的数据集合。

     

    缺点:数据可能不一致造成异常。由于我们的查询结果是在实际遍历的时候才会读取出来,因此查询条件的异常错误也就只会在实际遍历时候才会产生,前

            面我们总结的延时查询的优点2,在某些时候也会成为我们的错误。因此在使用的时候要很仔细。

            下面我们还是用代码示例来说明这个问题。

    1 void Main()
    2 {
    3 int[] list={1,2,3,4,5,6,7};
    4 var result=list.Select(n=>100/n);
    5 result.Dump(); //正常
    6 //..其他代码
    7   list[6]=0;
    8 //..其他代码
    9   result.Dump();//除以0异常
    10  }
        

    那么我们如何让查询不是延迟的呢?方法很简单,在IEnumerable<T>的扩展函数中,比如ToArray, ToList, ToDictionary, or ToLookup等几个非延迟

    方法可以将查询结果返回。

     

    1 void Main()
    2 {
    3 int[] list={1,2,3,4,5,6,7};
    4 var result=list.Select(n=>100/n).ToList();
    5 result.Dump();//result保存查询结果
    6  }

    在查询操作符之中,有部分操作符并不是延迟的,下面列举出非延迟和延迟的操作符.

    (图片来源:《Pro LINQ: Language Integrated Query in C# 2008》):

    3.扩展函数

    前面我们说到过,IEnumerable<T>和IEnumerable 是通过扩展函数提供查询操作功能,这里我们对扩展函数做个简单说明,如果你已经了解了扩展函数,

    可以直接跳过.扩展函数允许你直接在已经存在的类型上添加函数,而不用修改原有类型,或者通过继承的方式.扩展函数是一种特殊的静态函数,但是是需要通过类

    型的实例来调用,下面是扩展函数的格式:

     

    1 public static IEnumerable<T> Where<T>(
    2 this IEnumerable<T> source,
    3 Func<T, int, bool> predicate);

    上面是IEnumerable<T>的标准操作符where,下面我们举例实现自己的扩展操作符,扩展string类型增加函数统计指定的字符的数量GetCharCount(),

    这里我们需要注意几点:

    1.推荐定义自己的域名空间,统一管理扩展函数。

    2.扩展函数和所在的类都要是静态的。

    3.允许重载现有的函数。

     

    1 namespace MyExtension //自定义域名空间
    2 {
    3 public static class StringExtension  //静态类
    4 {   //统计字符在字符串串中出现的次数
    5 public static int GetCharCount(this string source, char c) //静态函数
    6 {
    7 return (from item in source where item = c select item).Count();
    8 }
    9 }
    10 }
      

    使用扩展函数的示例如下:

    1 using MyExtension //引用我们扩展域名空间
    2  namespace ConsoleApplication1
    3 {
    4 static class Program
    5 {
    6 static void Main(string[] args)
    7 {
    8 string demoString = "this is test string";
    9 int count= demoString.GetCharCount('i'); //直接调用扩展函数
    10   Console.WriteLine(x);
    11 }
    12 }
    13 }  

    4.常见查询操作符使用介绍

     

     

     

    在经过了上面对linq to objects的了解,在这一节中,我们对常见的操作符进行说明,在这里会根据操作符的是否属于延迟操作符进行分类。

     

    附注:后面的示例中用到的序列都为 items,定义如下:

     

    string[] items={"charp","cpp","python","perl"};

    1.  Where

    作用: 过滤序列,将结果放入新序列中

    是否延迟:   Yes

    参数重载1:   Func<T,bool> 委托  

    参数重载2:   Func<T,int,bool>委托  (int 标识索引index)

     

    var result=items.Where(p=>p.Length==4);
    //描述: 返回字符串长度为4的序列
     
    var result=item.Where((p,i)=>i==1); //i为索引
    //描述: 返回索引为1的字符串

     

     

    2. Select

    作用:对序列元素进行操作,返回新的结果序列(返回序列的类型和原序列类型可以不同)

    是否延迟:Yes

    参数重载1:Func<T, int, S> selector

    参数重载2:Func<T, S> selector

     

    var result=items.Select(p=>p.Length);
    //描述: 返回序列中所有元素的长度
     
    var result=items.Select((p,i)=>p+":"+i)
    //描述: 返回序列中所有元素和序号的组合序列

     

    3. SelectMany

    作用:创建新的一对多关系的序列,将原序列中每个元素进行操作转换为新的序列。

    是否延迟:Yes

    参数重载1:Func<T, IEnumerable<S>> selector

    参数重载2:Func<T, int, IEnumerable<S>> selector

     

    var result=items.SelectMany(p=>p.ToArray());
    //描述:将序列中string元素转换为字符数组,即结果是“cpp”被转换为IEnerable<Char> 值为{ ‘c’,‘p’,‘p’}
     
    var result=items.SelectMany((p,i)=>i==1?p.ToArray():new char[]{})
    //描述: 将序列中序号为1的元素转换为字符数组,其他的元素转换为空字符数组

     

    4. Take

    作用:从原序列中获取指定数量的元素集合,返回新的序列。

    是否延迟:Yes

    参数:int count

     

    var result=items.Take(1);
    //描述: 返回序列的第一个元素 结果为"Csharp"

     

    5. TakeWhile

    作用:从原序列中yield满足条件的元素,直到遇到不满足条件的元素,剩余的其他元素将被忽略。

    是否延迟:Yes

    参数重载1:Func<T, bool> predicate

    参数重载2:Func<T, int, bool> predicate

     

    string[] s={"1","22","333","4444","555","66","7"};
    var result= s.TakeWhile(p=>p.Length<4);
     //描述: 返回满足长度小于4的序列,注意:当遍历到"4444"的时候不满足条件yield结束,后面的元素虽然满足条件但是被忽略
    //结果:
    1
    22
    333

     

    6. Skip

    作用:从原序列中跳过指定数量的元素,返回剩余元素组成的序列。

    是否延迟:Yes

    参数:int count

     

    var result= items.Skip(2);
     //描述: 跳过两个元素返回剩余的元素组成的序列
    //结果: {"python","perl"};

     

    7. SkipWhile

    作用:从原序列中跳过满足条件的元素,返回剩余元素组成的序列。

    是否延迟:Yes

    参数:Func<T, int, bool> predicate

     

    var result= items.SkipWhile(p=>p.Contains(‘h’));
     //描述: 跳过元素中有包含字符h的元素,返回其他元素
    //结果: {"cpp","perl"};

     

    7. OrderBy 和 OrderByDescending

    作用:从原序列中跳过满足条件的元素,返回剩余元素组成的序列。

    是否延迟:Yes

    参数:Func<T, int, bool> predicate

     

     

    var result= items.SkipWhile(p=>p.Contains(‘h’));
     //描述: 跳过元素中有包含字符h的元素,返回其他元素
    //结果: {"cpp","perl"};

      8. ThenByThenByDescending

      作用:在已排序后的序列进行第二种方式排序。

    是否延迟:Yes

    重载参数1:Func<T, K> Selector

    重载参数2:Func<T, K> Selector,IComparer<K> comp

    1 var result=items.OrderBy(s=>s.Length).ThenBy(s=>s[0]);
     //描述:先按照长度排序,然后按照字符串首字母排序

    9.Join

    作用:通过指定的键连接多个序列组合成新序列,和SQL的内连接相似。

    是否延迟:Yes

    参数:IEnumerable<U> inner,Func<T, K> outerKey,Func<U, K> innerKey,Func<T, U, V> result

     

    1 void Main()
    2 {
    3 string[] items1={"aa","bbb","ccc","dddd"};
    4 string[] items2={"eee","ff","gggg","h","iii"};
    5 var r=items1.Join(items2,i=>i.Length,j=>j.Length,(i,j)=>new {result=string.Format("{0}:{1}",i,j)});
    6 r.Dump();
    7 }
    8  //将两个序列按照长度连接组成新序列

    结果:
    result
    aa:ff
    bbb:eee
    bbb:iii
    ccc:eee
    ccc:iii
    dddd:gggg
     

    注意:

    1.返回的序列是匿名类型,可以自定义结果序列的列,但是同时需要指定名称(result就是名称)。

    2.由于是匿名类型,因此只能使用var关键字

    10.GroupBy

    作用:对序列按照指定方式进行分组,生产新的分组序列

    是否延迟:Yes

    参数:Func<T, K> keySelector

    返回值:IEnumerable<IGrouping<K, T>>

    1 void Main()
    2 {
    3 string[] items={"1","22","33","444","555","6666"};
    4 var r1=from item in items
    5 group item by item.Length into g
    6 select new {g.Key};
    7 r1.Dump();
    8
    9 //等同于下面
    10   var r2= items.GroupBy(item=>item.Length).Select(g=>g.Key);
    11 r2.Dump();
    12 }

    结果:
    1
    2
    3
    4

    11.Union

    作用:组合两个序列为一个序列,去除重复的元素

    是否延迟:Yes

    参数:IEnumerable<T> second

     

     

    1 void Main()
    2 {
    3 string[] items={"1","22","6666"};
    4 string[] items2={"1","22","33",,"777"};
    5 items.Union(items2).Dump();
    6 }
    结果:
    1
    22
    33
    6666
    777
     

    12.ToArray , ToList

    作用:将序列转换为数组 或 列表

    是否延迟:No

    参数:none

     

     

    1 void Main()
    2 {
    3 string[] items={"1","22","33","444","555","6666"};
    4 var r= items.Where(n=>n.Length>2).ToArray();
    5 var r2= items.Where(n=>n.Length>2).ToList();
    6 }
    7  //r和r2保存的是实际的数组和列表,而不是查询条件对象

    13.ToDictionary

    作用:转换序列为泛型字典Dictionary of type <K, T>

    是否延迟:No

    参数:Func<T, K> keySelector,Func<T, E> elementSelector,IEqualityComparer<K> comparer

     

     

    1 void Main()
    2 {
    3 string[] items={"1","22","33","444","555","6666"};
    4 items.ToDictionary(k=>k.Length).Dump();//错误:重复键
    5   items.ToDictionary(k=>k,k=>k.Length).Dump();
    6 }
    结果:
    key value
     1 1  
    22 2  
      33 2 
        444 3 
       555 3      
    6666
    4
      

    5.后记

    在上面的例子中只列出了几个常见,某些比较简单的操作符比如:Min,Max,Count,ElementAt等从字面意思都可以看出来就没有做描述。

    由于理解不够深入,存在的问题请指正。

    转自
    http://www.cnblogs.com/75115926/archive/2011/02/09/1950056.html

  • 相关阅读:
    完全备份、差异备份以及增量备份的区别(转)
    Backup Exec Inventory 与Catalog的含义(转载)
    从客户端中检测到有潜在危险的Request.Form值的解决办法
    IQueryable与IEnumberable的区别(转)
    SQL递归查询(with cte as) 物料分解
    Http权威指南笔记(二) Http状态码大全
    Http权威指南笔记(一) URI URL URN 关系
    echarts在.Net中使用实例(二) 使用ajax动态加载数据
    echarts在.Net中使用实例(一) 简单的Demo
    sql显示12个月数据
  • 原文地址:https://www.cnblogs.com/vebest/p/2148656.html
Copyright © 2011-2022 走看看