linq 总结

来源:互联网 发布:四级英语网络培训机构 编辑:程序博客网 时间:2024/06/01 18:50

linq是插入c#和vb.net中的一种sql查询语言.sql查询语言是一个以用途目的为导向,而不是指令式的语言,因此比较人性化.

结合linq,c#也能写出高效易于理解的数据处理代码.因此,每一个学习c#的朋友,都应该学好linQ.

下面是我学习的一些总结,希望对大家也有点帮助.

函数形式和查询语句形式

linq 语言包含两种形式,一种是linq的特有查询语言,一种是函数形式的.函数形式是补充的功能,而特有形式是一般常用的功能. 所有的特有形式,都有对应的函数形式.linq的特有查询形式,实际上是一种语法糖.

立即执行和延迟执行

linq 有两种执行模式,一种是立即执行,一种是延迟执行.延迟执行本质上相当于匿名函数,你写了一个匿名函数的函数体,不代表你会立刻调用,只有当你调用的时候,才会执行.

流式和非流式

linq中,对数据处理采用了两种模式,一种是一个接一个元素进行单独处理,这称为流式,一个是整体处理,这叫非流式.linq为了优化性能,采用了要多少数据就实际读取多少数据的策略,应该还是比较好理解的.

linq标准查询函数分类列表

聚合操作

Aggregate( Func<聚合值, 下一个元素, 返回聚合值> ) : 根据特定函数执行聚合操作(聚合就是累计吧)

Average序列的平均值.

Count序列元素个数或者满足指定条件的元素个数.

LongCount返回元素个数(结果类型为Int64,也就是支持范围比较大)

Sum 累计.

排序操作

OrderBy 将序列升序以非流式方式工作.该操作不查询数据.

OrderByDescending 将序列降序.以非流式方式工作.该操作不查询数据.

Reverse 反转序列.以非流式方式工作,该操作不查询数据.

ThenBy 对排序结果再次进行升序.以非流式方式工作,该操作不查询数据.

ThenByDescending 对排序结果再次进行降序.以非流式方式工作,该操作不查询数据.

集合操作

Concat 串联两个序列.该操作不查询数据.

Except 左集合减右集合的结果.右集合以非流式方式工作.该操作不查询数据.

Intersect 交集.右集合以非流式方式工作.该操作不查询数据.

Union 并集.该操作不查询数据.

筛选操作

Distinct 返回非重复元素组成的新序列.该操作不查询数据.

First返回第一个元素或符合条件的第一个元素.

FirstOrDefault 返回第一个元素或符合条件的第一个元素.如没有返回默认.

Max返回序列中最大的元素.

Min 返回序列中最小的元素.

Last返回最后一个元素或最后一个符合条件的元素.

LastOrDefault 返回最后一个元素或最后一个符合条件的元素.如没有返回默认.

Single返回唯一元素,或符合条件的唯一元素,如有多个报异常.

SingleOrDefault 返回唯一元素,或符合条件的唯一元素或默认值,如有多个报异常.

Where 条件筛选.该操作不查询数据.

判断操作

All(Func<下一个元素>, 返回是否符合条件> ) : 测试序列是否都满足条件

Any 是否存在元素或是否存在符合条件的元素.

Contains是否包含指定元素.

SequenceEqual比较两序列是否相等.

联接投影分组操作

Select 序列每个元素根据转换函数产生投影.该操作不查询数据.

SelectMany 序列每个元素根据转换函数产生序列投影(一对多),最后串联所有投影序列.该操作不查询数据.

GroupBy 按指定键分组序列.以非流失方式工作.该操作不查询数据.

GroupJoin 近似左联接.右集合以非流式方式工作.该操作不查询数据.

Join 内联接.右集合以非流式方式工作.该操作不查询数据.

ToLookup 返回一对多的字典.

范围选择操作

ElementAt返回指定位置的元素.

ElementAtOrDefault 返回指定位置的元素.当该位置没有返回默认.

Skip 跳过指定数目的元素,返回剩余元素组成的序列.该操作不查询数据.

SkipWhile 跳过元素,直到条件不符合,返回剩余元素组成的序列.该操作不查询数据.

Take 返回指定个数的元素组成的序列.该操作不查询数据.

TakeWhile 返回元素序列,直到条件不符合.该操作不查询数据.

生成操作

DefaultIfEmpty 如果是空序列,就返回一个默认或指定元素的新序列. 该操作不查询数据.

Empty返回空序列.

Range 生成一个整数升序序列.该操作不查询数据.

Repeat 生成一个指定值的序列.该操作不查询数据.

转换操作

AsEnumerable 返回标准查询接口(非子类的特定接口). 该操作不查询数据.

AsQueryable 返回IQueryable接口.该操作不查询数据.

Cast 返回标准查询接口,将元素类型强制转换,常用于非泛型序列.该操作不查询数据.

OfType 返回标准查询接口,将元素类型进行转换,如不能转换就放弃该元素,常用于非泛型序列.该操作不查询数据.

ToArray返回数组.

ToDictionary返回字典.

ToList 返回列表.


今天,我们将着眼于五个用于序列的聚合运算。很多时候当我们在对序列进行操作时,我们想要做基于这些序列执行某种汇总然后,计算结果。

 

Enumerable 静态类的LINQ扩展方法可以做到这一点 。就像之前大多数的LINQ扩展方法一样,这些是基于IEnumerable <TSource>序列的操作。

SUM() - 计算整个序列的总和

它有两种形式:

  • SUM( )
    • 计算整个序列的总值。
    • 源类型必须是以下类型之一: int,long,double,decimalsingle 或这些类型的可空变种(int?, long?, double?…) 。
  • Sum(Func<TSource, Xprojection)
    • 计算序列投影值的和。
    • 从MSDN上,我们得知X必须是以下类型之一:int,long,double,decimalsingle 或这些类型的可空变种(int?, long?, double?…) 。

 

在这里请注意几件事情。

 

首先,尽管在C#中支持许多类型,SUM()方法-非投影式-只支持int,long,double,decimalsingle 。  

 

1: // 正确

2: double[] data = { 3.14, 2.72, 1.99, 2.32 };

3: var result = data.Sum();

4: 

5: //不支持

6: short[] shortData = { 1, 2, 5, 7 };

7: 

8: // 出现编辑错误

9: var shortResult = shortData.Sum();

还要注意的是,你可以操作上面这些类型允许Null 的可空值变种。在之前我们讨论过,可为空的类型可以是一个棘手的事情,但用SUM()时我们不用担心,因为所有的空值在求和时都排除了 :

 

var data = new List<int?> { 1, 3, 9, 13, null, 7, 12, null };
var result = data.Sum();

   
第二,投影形式是一个比较有趣和有用的方法:
 

为了说明这一点,让我们假设一个简单的POCO Employee:

 

 

public sealed class Employee

{

public string Name { get; set; }

public double Salary { get; set; }

public short Dependents { get; set; }

}


 


var employees = new List<Employee>

{

new Employee { Name = "Bob", Salary = 35000.00, Dependents = 0 },

new Employee { Name = "Sherry", Salary = 75250.00, Dependents = 1 },

new Employee { Name = "Kathy", Salary = 32000.50, Dependents = 0 },

new Employee { Name = "Joe", Salary = 17500.00, Dependents = 2 },

};

 

然后我们就可以使用投影方式获得Salary 的总值:

 
var totalSalary = employees.Sum(e => e.Salary);
 
虽然投影形式表面上似乎被限制在了上述的类型里(int,long,single,double,decimal),但是如果我们使用lambda表达式或匿名表达,投影的形式将允许较短的类型:
 
employees.Sum(e => e.Dependents);

employees.Sum(delegate(Employee e) { return e.Dependents; });
 
这是因为lambda表达式和匿名委托的结果可以自动扩大小数值类型(如 short)到 int。
 

Average() - 返回序列的平均值

Average()方法,就像SUM()一样,只不过它是用总和除以实际涉及到的项目数。涉及到的是什么意思?请记住,SUM( )不包括空值 。Average()是将所有非null值求平均。例如:

 

 

var intList = new int?[] { 10, 20, 30, null };



// 返回 20

Console.WriteLine(intList.Average());

 

MIN() - 返回最小的序列值

MIN()扩展方法用于研究序列,并返回从它的最小值  :

  • Min()
    • 查找序列中最小的值。
    • 抛出异常,如果没有实现IComparableIComparable<T>
    • 抛出异常,如果序列是空的,源类型是值类型。
    • 如果序列是空的,X是引用类型或者Nullable的值类型,则返回 null 。
  • Min(Func<TSource, Xprojection)
    • 返回泛型序列中的最小值
    • 如果 TSource 类型实现 IComparable<T>,则此方法使用该实现比较值。 否则,如果 TSource 类型实现 IComparable,则使用该实现比较值。
    • 抛出异常,如果序列是空的,X是值类型。
    • 如果 TSource 为引用类型,且源序列为空或仅包含为 null 的值,则此函数返回 null。

       

MIN()支持几乎任何类型,只要该类型实现IComparableIComparable <T>。因此,它是不限制的数值类型,可以用于任何比较的对象(包括像值类型的DateTime,TimeSpan):

 

 

var shortList = new short[] { 1, 3, 7, 9, -9, 33 };

// 返回 -9

var smallest = shortList.Min();


// 根据家庭成员数量找到最小值 

var minDependents = employees.Min(e => e.Dependents);

 
此外,MIN()不使用泛型约束限制那些支持IComparable 接口的类型参数。相反,它抛出一个运行异常来回应如果序列非空,没有在它的对象实现IComparable的接口。
因此如果使用我们的之前定义的Employee类,下面的第一次调用将返回Null(序列为空),第二次调用会抛出(非空,但不包含IComparable的对象序列) 。
 

var result1 = Enumerable.Empty<Employee>().Min();

var result2 = employees.Min();
 
最后,请注意,对于值类型,如果序列是空,将抛出异常, 所以下面两个都会抛出异常:
 

var result3 = Enumerable.Empty<int>().Min();

var result4 = Enumerable.Empty<Employee>().Min(e => e.Dependents);
 
 

MAX() - 返回最大的序列值

MAX()MIN()的行为完全一样,只不过它返回最大值,而不是最小值。因此,我们可以使用这些序列中的最大值,或从一个序列的预测最大值:

 
///返回33   
VAR biggestShort = shortList.Max();

//返回75250.0
VAR highestSalary = employees.Max(E => e.Salary);

其他方面,请参考Min()。

Aggregate() - 序列的自定义累加器

 

有三种形式的Aggregate():

  • Aggregate(Func<TSource, TSource, TSource> function)
    • 适用于一个函数,它接受一个累加器值和下一个值,并返回结果。
    • 值和序列类型是相同的。
    • 种子值是序列中的第一个值。
  • Aggregate(TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> function)
    • 应用序列中的一个函数,累加器值和下一个项目,并返回一个结果。
    • 值和序列类型可以不同或相同。
    • 必须提供一个种子值来初始化,将指定的种子值用作累加器初始值。
  • Aggregate(TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> function, Func<TAccumulate, TResult> resultProjection)
    • 同上述,将指定的种子值用作累加器的初始值,并使用指定的函数选择结果值。

这可能看起来相当复杂。只要记住 “此方法的工作原理是对 source 中的每个元素调用一次 func。 每次调用 func 时,都将传递序列中的元素和聚合值(作为 func 的第一个参数)。 并用 func 的结果替换以前的聚合值。”

 

例如,如果我们想要做一个序列中的所有数字的乘法:

 

var numbers = new int[] { 1, 3, 9, 2 };

// 使用当前总值乘以下一个数值得到新的总值
var product = numbers.Aggregate((total, next) => total * next);

最后的值是: 1 X 3 X 9 X 2 = 54。

 

 

下面看看怎么用更复杂的聚合计算, 可能我们想得到这样一个结果 -- 用每个雇员的工资除以家庭总人口数(包括他自己),再将这些数相加:

 

var weirdCalculation = employees.Aggregate(0.0, 

(result, next) => result + next.Salary / (next.Dependents + 1));
 
参照上面的Empolyee 定义,得到的结果是 110458.8333, 为方便理解请看下面的Excel 表格:
 
ScreenShot009

所以你看,我们可以做相当复杂的聚合计算,关键是要记住,你所提供的函数留给下一个“元素”,并把它应用到正在运行的“总值”。

 

摘要

四个简单的和一个可能有点复杂的,这一组功能相当强大!这些方法可以很容易地对序列进行聚合,使你不需要进行循环和自己计算。他们很运行快也很容易使用,他们很容易阅读,他们也被全面测试过了。敬请享受!