C#:Lambda/Linq
文章目录
Func<int, int, int> add = (x, y) => x + y; int result = add(3, 5); // 结果为 8
LINQ (Language Integrated Query) 是一种用于查询各种数据源的技术,它允许以类似于 SQL 的方式来查询对象、集合和其他数据源。它提供了一组方法和语法,让你可以在数据源中执行各种查询操作,
如过滤、排序、投影和分组等。LINQ 与 Lambda 表达式一起使用,使得对数据的操作更加简洁和易读。例如:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();
在这个例子中,Where
是 LINQ 方法,n => n % 2 == 0
就是一个 Lambda 表达式,用于筛选出 numbers
列表中的偶数。
Lambda 表达式允许你定义简洁的匿名函数,而 LINQ 则是一种强大的查询语言,与 Lambda 表达式结合使用,使得在 C# 中对数据的查询和操作变得更加方便和直观。
匿名类
匿名类是在编程中用于临时封装数据的一种特殊类。它允许你在不事先定义类的情况下创建一个对象,并且通常用于临时性或简单的数据封装需求。
在 C# 中,匿名类是通过 new
关键字和对象初始化器语法来创建的,NETFramework3.0出现的。它的定义方式如下:
var person = new { Name = "John", Age = 30 };
在例子中,person
是一个匿名类的实例。该类有两个属性:Name
和 Age
。编译器会自动推断属性的类型基于初始化值的类型。
匿名类常用于 LINQ 查询中,用于选择特定的属性或形成临时的投影。例如:
var people = new[] { new { Name = "Alice", Age = 25 }, new { Name = "Bob", Age = 30 }, new { Name = "Charlie", Age = 28 } }; var names = people.Select(p => p.Name).ToList();
在例子中,people
是一个包含匿名类对象的数组。Select
方法用于选择每个对象的 Name
属性并将其组成一个新的列表。
匿名类通常用于简单的临时需求,因为它们没有类名,也无法被重用。它们对于快速封装和使用临时数据非常有用,但如果需要更复杂的逻辑或重复使用,则可能需要定义具名类。
匿名类+object
object去接匿名类,无法访问属性值,因为C#是强类型语言,object是在编译时确定类型,因为Object没有这个属性
object model = new { Id = 1, Name = "张三", Age = 30, ClassId = 2 }; //无法访问属性值 //model.Id = 134; //Console.WriteLine(model.Id);
匿名类+dynamic
dynamic(动态类型)可以避开编译器检查,.NETFramework 4.0出现的
dynamic去接匿名类,可以访问属性值,因为dynamic是运行时才检查的,但是访问不存在的属性也不报错,运行时才报异常
dynamic dModel = new { Id = 1, Name = "张三", Age = 30, ClassId = 2 }; //可以访问属性值 dModel.Id = 134; Console.WriteLine(dModel.Id); //但是访问不存在的属性也不报错,运行时才报异常 dModel.abccc = 1234;
匿名类+var
var去接匿名类,可以读取属性,不能给属性重新赋值,只能在初始化的时候给定一个值
var是编译器的语法糖,由编译器自动推算类型
var声明的变量必须初始化,必须能推算出类型,var aa = null;或者var aa;都是不正确的
var缺陷:阅读麻烦,建议能明确类型的还是明确类型,优点:简化代码
var vmodel = new { Id = 1, Name = "张三", Age = 30, ClassId = 2 }; //不能给属性重新赋值 //vmodel.Id = 134; //可以读取属性 Console.WriteLine(vmodel.Id);
Lambda表达式
Lambda是什么
(1)形如:()=> { } 就是lambda表达式
(2)lambda表达式就是一个匿名方法,在底层会生成在一个"<>"类中,生成带有名称的方法
Lambda的演变过程
(1).Netframework1.0/1.1,原始方法
/// <summary> /// 声明委托 /// </summary> /// <param name="x"></param> /// <param name="y"></param> public delegate void NoReturnWithPara(int x, string y); /// <summary> /// 声明方法 /// </summary> /// <param name="x"></param> /// <param name="y"></param> private void PrintParam(int x, string y) { Console.WriteLine(x); Console.WriteLine(y); }
NoReturnWithPara method = new NoReturnWithPara(PrintParam); |
(2).NetFramework2.0,匿名方法
增加了一个delegate关键字,可以访问到除了参数以外的局部变量
int i = 0; NoReturnWithPara method = new NoReturnWithPara(delegate (int x, string y) { Console.WriteLine(x); Console.WriteLine(y); Console.WriteLine(i); });
(4).NetFramework3.0后期,简化参数类型
去掉了匿名方法中的参数类型,这个是编译器提供的语法糖,编译器可以根据委托类型定义的参数类型推导出参数类型
int i = 0; NoReturnWithPara method = new NoReturnWithPara((x, y) => { Console.WriteLine(x); Console.WriteLine(y); Console.WriteLine(i); });
(5)如果方法体中只有一行代码,可以省略方法体大括号
NoReturnWithPara method = (x, y) => Console.WriteLine(x);
(6)如果方法只有一个参数,省略参数小括号
Action<string> method = x => Console.WriteLine(x);
(7)如果方法体中只有一行代码,且有返回值,可以省略return
Func<int, string> method = i => i.ToString();
Lambda的优势
1. 简洁性和清晰度:Lambda 表达式使得编写短小精悍的匿名函数变得更容易。它可以在一行代码内表达简单的功能,减少了传统函数定义所需的模板代码,让代码更为精简。
2. 便捷性:Lambda 表达式可以直接作为参数传递给方法,这种便捷性特别适用于需要传递函数作为参数的场景,如 LINQ 查询、委托或事件处理等。
3. 闭包和延迟执行:Lambda 表达式可以捕获外部变量,形成闭包,使得它们可以在其定义范围之外使用这些变量。此外,Lambda 表达式通常用于实现延迟执行的功能,只有在需要时才会被执行,有助于提高性能和资源利用率。
4. 函数式编程:Lambda 表达式推动了函数式编程风格在 C# 中的发展。它们可以与 LINQ 配合使用,以声明性的方式处理集合和序列,使得代码更具表现力和功能性。
5. 提高可读性和可维护性:当使用合适的命名和结构时,Lambda 表达式可以提高代码的可读性。在一些情况下,将复杂的操作转化为 Lambda 表达式可以让代码更易于理解和维护。
6. 并行性和异步编程:Lambda 表达式与并行编程和异步编程结合使用时,可以轻松地实现并行操作和异步任务,提高程序的性能和响应性。
Lambda 表达式是一种强大的工具,它在 C# 中提供了更简洁、更灵活和更具表现力的方式来表达功能。它们的使用使得代码更为精简、可读,并且能够更好地适应现代软件开发中的需求。
扩展方法(Extension Method)
扩展方法的使用十分广泛,包括LINQ的查询扩展方法也是基于扩展方法。
扩展方法提供了在类型定义之外任意位置扩展类型的功能能力。
扩展方法可用于类、结构、枚举等。
定义扩展方法,需要满足:
- 扩展方法的只能定义在static类中,且自身必须是static方法
- 使用this关键词修饰参数,并且this参数只能放在第一个,它标识出是该方法是哪个类型的扩展方法
扩展方法定义如下:
public static ArticleExtensions { public static int WordCount(this Article article) //定义Article类的扩展方法WordCount,用于数出单词数量 { return article.Content.Split(new char[] { ' ', '.', '?', '!' }, StringSplitOptions.RemoveEmptyEntries).Length; } }
此定义不在Article内,而是在独立的静态类ArticleExtensions中。这样定义后,Article类的对象便拥有了WordCount这个扩展方法可用。
事实上,单个静态类中,可以定义多个不同类型的扩展方法。
使用扩展方法,可以像使用类型的方法一样:对象.扩展方法(参数列表)
上例中,可以这样用:
Article article = new Article("A Story.txt"); var wordCount = article.WordCount(); //调用扩展方法,调用者本身作为参数列表里的被扩展的对象(即第一个参数)传入方法,因此调用时括号内会少了第一个参数。
需要注意,需要引用扩展方法所在的命名空间后,才可以使用该扩展方法。
扩展方法也能当作一般的静态方法使用:
Article article = new Article("A Story.txt"); var wordCount = ArticleExtensions.WordCount(article); //当作一般静态方法使用
扩展方法也支持泛型。例如我们可以实现一个泛型扩展方法ToJson<T>,T类型作为被扩展的类型。之后便可以使用任意对象.ToJson()将对象转换成Json字串(这个例子中有个细节:ToJson的泛型参数也被省略了,因为调用者就是第一个参数,可以推断出泛型类型)。
另外,扩展方法如果跟既有方法同名,由于静态方法只能是隐藏,无法作为重写,所以:
- 扩展方法与类型自身方法冲突时:将会优先使用自身方法
- 祖先类型拥有相同签名的扩展方法:由于会进行方法隐藏,将使用对象所显式声明类型的扩展方法,而不是实际类型的方法。这适用于各种情况:实际类型拥有自身定义的静态的或非静态的同签名方法;实际类型拥有同签名的扩展方法。这些情况下都不会使用实际类型的方法。
- 多处定义多个同签名的扩展方法(只要不在一个类中定义,编译器便不会报错):如果它们在不同的命名空间中,使用时可以只using需要使用的扩展方法所在的命名空间,可以正常调用。如果它们在同一个命名空间下,或者使用时同时using了这两个命名空间,则在调用时会引起二义性错误。此时请直接用普通静态方法的语法来调用它们。
扩展方法(Extension Methods)是C#中一种强大的特性,允许开发者在不修改原始类代码的情况下,向现有的类添加新的方法。
表达式树(Expression Tree)
表达式树,主要提供两种能力:
- 可以在运行时动态生成可编译和执行的表达式
- 可以在运行时分析处理代码中编写的表达式
生成、编译和执行表达式树
对于下列简单的表达式
int sum = 1 + 2
它具体由以下部分组成:
- 左值部分
- 变量类型声明 int
- 变量名 sum
- 赋值符号 =
- 右值部分
- 左操作数 1
- 加法符号 +
- 右操作数 2
学过算法的同学应该都知道,我们书写的符合自然语义的表达式为中缀表达式,它无法直接被计算机处理。一般在计算表达式时,需要将中缀表达式转成后缀表达式,这就会用到二叉树。表达式可以表示成一棵二叉树,树的非叶子节点为表达式的运算符,叶子节点为操作数;节点的左侧子节点为左操作数,右侧子节点为右操作数。
上述表达式,如果用C#的表达式树来描述:
var varibleSum = Expression.Variable(typeof(int), "sum"); //声明int类型的变量sum var one = Expression.Constant(1); //声明左操作数常量表达式 1 var two = Expression.Constant(2); //声明右操作数常量表达式 2 var add = Expression.Add(one, two); //声明将两个常量表达式加起来的表达式 1+2 var assignment = Expression.Assign(varibleSum, add); //声明赋值表达式
表达式树与上述二叉树的结构一致。因此构建的方式是先构建叶子节点,然后再通过运算符连接叶子节点构成一个个森林,依次进行下去直到合并成一棵树。
需要注意的是,表达式树是不可变的。一旦生成后,你就无法修改它了。如果需要修改,请考虑复制一棵表达式树,并在复制的过程中替换你要修改的部分。
表达式树是可以被执行的。只需要
- 用Expression的Lamdba方法来生成一个Lambda表达式的表达式树,以便封装成一个可调用的方法。
- 编译它
- 执行
Expression的Lamdba方法返回一个Expression\<TDelegate>类型的表达式,它可以被编译为一个方法。因此你也可以将它分配给一个委托变量。
var expFunc = Expression.Lambda<Func<int>>(assignment); //生成Lambda,expFunc的类型是Expression<Func<int>> var func = expFunc.Compile(); //编译,并赋值给委托。func的类型是Func<int> var assignResult = func(); //执行委托,assignResult为int sum = 1 + 2赋值表达式的结果,为3
Expression<TDelegate>和代码分析
Expression\<TDelegate>它派生自LambdaExpression,用于描述一个Lambda的表达式。其中TDelegate虽然未指定泛型约束条件(主要原因是C#7.1之前并不支持Delegate作为泛型约束类型),但是一般为强类型委托类型(Func或者Action)。
该类型的对象可以由下列方式创建:
- 使用Expression.Lambda静态方法构建
- 直接使用表达式Lambda进行赋值,但是有限制:
- 只能是“表达式Lambda”,不能是“语句Lambda”
- 这种赋值要求表达式不能包含赋值运算符=
当直接赋值为表达式Lambda时,编译器会自动将你的Lambda表达式内容转换成表达式树。开发者因此可以读取表达式树的每个节点,从而进行代码分析。也基于此,表达式树并不仅仅是类库层面的更新,同样也是语法级别的升级。
表达式1 + 2,如果直接用表达式Lambda赋值为Expression<TDelegate>,则可以写作:
Expression<Func<int>> funcExp = () => 1 + 2; var rsl = funcExp.Compile()(); //rsl为 3
编译器自动生成了() => 1 + 2
的表达式树,上面的代码等同于:
Expression<Func<int>> funcExp = Expression.Lambda<Func<int>> ( Expression.Add( Expression.Constant(1, typeof(int)), Expression.Constant(2, typeof(int)) ) ); var rsl = funcExp.Compile()(); //rsl为 3
生成表达式树的一个情景化的例子
想象一下你有一个需求,需要根据给定的条件动态地构建一个查询,比如筛选一组数据并返回符合特定条件的结果。在这个例子中,我们将使用 Expression<TDelegate>
来构建一个简单的表达式树来表示这个查询条件。
假设有一个 Person
类:
public class Person { public string Name { get; set; } public int Age { get; set; } }
现在,我们希望动态地创建一个查询条件,找出年龄大于某个特定值的人。我们可以使用表达式树来实现这个查询条件的动态构建。
using System; using System.Linq.Expressions; public class Program { public static void Main() { // 定义参数 ParameterExpression parameter = Expression.Parameter(typeof(Person), "p"); // 定义条件:p.Age > 30 Expression property = Expression.Property(parameter, "Age"); Expression constant = Expression.Constant(30, typeof(int)); BinaryExpression comparison = Expression.GreaterThan(property, constant); // 构建 Lambda 表达式 Expression<Func<Person, bool>> lambda = Expression.Lambda<Func<Person, bool>>(comparison, parameter); // 编译表达式树并使用 Func<Person, bool> func = lambda.Compile(); // 测试查询条件 var people = new[] { new Person { Name = "Alice", Age = 25 }, new Person { Name = "Bob", Age = 35 }, new Person { Name = "Charlie", Age = 40 } }; var result = people.Where(func); // 输出结果 foreach (var person in result) { Console.WriteLine($"{person.Name}, Age: {person.Age}"); } } }
这个例子中,我们使用 Expression
类的方法来构建了一个简单的表达式树,表示了一个年龄大于 30 的查询条件。然后我们将这个表达式树编译为一个委托,并在一组人员数据上应用这个查询条件。最后,我们打印出满足条件的人员信息。
这种动态构建查询条件的方式非常有用,特别是在需要根据不同的条件来过滤数据时。通过使用表达式树,我们可以以编程的方式构建灵活的查询,而不需要在每个条件变化时都手动编写不同的查询逻辑。
Linq
Linq是什么
LINQ(Language Integrated Query)是C#和.NET框架中的一种特性,它提供了一种统一的编程模型,用于查询各种数据源,如集合、数据库、XML等。
它允许开发者使用类SQL的查询语法(查询表达式)或方法调用的方式来对数据进行查询、过滤、排序、投影等操作。
(1)Linq(Language Integrated Query)即语言集成查询。
(2)Linq是一组语言特性和API,使得你可以使用统一的方式编写各种查询。用于保存和检索来自不同数据源的数据,从而消除了编程语言和数据库之间的不匹配,以及为不同类型的数据源提供单个查询接口。
(3)Linq总是使用对象,因此你可以使用相同的查询语法来查询和转换XML、对象集合、SQL数据库、ADO.NET数据集以及任何其他可用的LINQ提供程序格式的数据。
(4)Linq主要包含以下部分
- Linq to Objects 主要负责对象的查询。
- Linq to XML 主要负责XML的查询。
- Linq to ADO.NET 主要负责数据库的查询。
- Linq to SQL
- Linq to DataSet
- Linq to Entities
- Linq to Everything
Linq的原理
LINQ 的原理主要基于两个重要的组成部分:语言集成和提供提供者模型。
语言集成:
LINQ 是 C# 语言的一部分,其核心是将查询能力直接集成到编程语言中。这使得可以使用类SQL的查询语法或方法调用的方式来对数据进行查询和操作。在编写代码时,可以像操作集合一样操作数据源,
无需使用额外的查询语言或 API。
提供者模型(Provider Model):
LINQ 使用提供者模型来实现对不同数据源的查询。LINQ 提供了一组标准的接口,以及抽象类和方法,允许针对特定数据源实现自定义的提供者。这些提供者将 LINQ 查询翻译成适合特定数据源的查询语言或命令(如 SQL、XML查询语言等),并执行这些查询。这种模型使得 LINQ 能够在各种数据源上工作,包括对象集合、数据库、XML 等。
LINQ 提供者的主要组成部分:
1. 查询提供者(Query Provider):这是 LINQ 的核心部分,负责接收 LINQ 查询并将其转换为目标数据源的查询。例如,对于数据库查询,有 Entity Framework 提供了 LINQ to SQL 提供者,将 LINQ 查询转换为 SQL 查询。
2. 表达式树(Expression Trees):LINQ 查询在编译时被转换为表达式树。表达式树是一个动态表示代码的树状结构,它代表了查询语句中的各个部分,如过滤、排序、投影等,这些表达式树可以被提供者解析并转换为目标查询语言。
3. 标准查询操作符(Standard Query Operators):LINQ 提供了一组标准的查询操作符(例如 `Where`、`Select`、`OrderBy` 等),这些操作符是对 LINQ 查询的构建块,使得可以构建灵活的查询表达式。
查询执行过程:
1. 查询构建阶段:在编写 LINQ 查询时,查询不会立即执行,而是被解释为表达式树。
2. 表达式树解析阶段:查询提供者接收到表达式树后,会解析表达式树,将其翻译成目标数据源能够理解的查询语言(如 SQL)或命令。
3. 查询执行阶段:解析后的查询语言或命令被传递给目标数据源执行,数据源执行查询并返回结果。
这个流程说明了 LINQ 如何将查询表达式转换为特定数据源的查询命令,并最终执行这些命令来获取所需的结果。通过语言集成和提供者模型的组合,LINQ 提供了一种统一的、方便的方式来查询和操作各种数据源。
需求:存在一个集合,要过滤其中的数据
(1)方案1:循环 + 判断
//要求查询Student中年龄小于30的; List<Student> studentList = this.GetStudentList(); List<Student> list = new List<Student>(); foreach (var item in studentList) { if (item.Age < 30) { list.Add(item); } } //要求Student名称长度大于2 List<Student> list2 = new List<Student>(); foreach (var item in studentList) { if (item.Name.Length > 2) { list2.Add(item); } } //N个条件叠加 List<Student> list3 = new List<Student>(); foreach (var item in studentList) { if (item.Id > 1 && item.Name != null && item.ClassId == 1 && item.Age > 20) { list.Add(item); } }
(2)方案2:扩展方法 + Lambda
可以把不变的业务逻辑保留,把可变的,不固定的业务逻辑转移出去,就可以用委托包装一个方法传递过来,简化重复代码
/// <summary> /// 泛型扩展方法 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="oldlist"></param> /// <param name="func"></param> /// <returns></returns> public static List<T> CustomWhere<T>(this List<T> oldlist, Func<T, bool> func) { List<T> newlist = new List<T>(); foreach (var item in oldlist) { if (func.Invoke(item)) { newlist.Add(item); } } return newlist; }
//要求查询Student中年龄小于30的; List<Student> studentList = this.GetStudentList(); List<Student> list = studentList.CustomWhere(item => item.Age < 30); //要求Student名称长度大于2 List<Student> list2 = studentList.CustomWhere(item => item.Name.Length > 2); //N个条件叠加 List<Student> list3 = studentList.CustomWhere(item => item.Id > 1 && item.Name != null && item.ClassId == 1 && item.Age > 20);
(3)方案3:Linq中的Where
- Linq实现原理和我们自己写的扩展方法类似
- Linq的底层都是通过迭代器来实现就是支持循环
- Linq的底层使用IEnumerable来承接数据
//来自于Linq的Where实现 public static IEnumerable<TSource> Where<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate) { if (source == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } if (predicate == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.predicate); } Iterator<TSource> iterator = source as Iterator<TSource>; if (iterator != null) { return iterator.Where(predicate); } TSource[] array = source as TSource[]; if (array != null) { if (array.Length != 0) { return new WhereArrayIterator<TSource>(array, predicate); } return Empty<TSource>(); } List<TSource> list = source as List<TSource>; if (list != null) { return new WhereListIterator<TSource>(list, predicate); } return new WhereEnumerableIterator<TSource>(source, predicate); }
//要求查询Student中年龄小于30的; List<Student> studentList = this.GetStudentList(); List<Student> list = studentList.Where(item => item.Age < 30).ToList(); //要求Student名称长度大于2 List<Student> list2 = studentList.Where(item => item.Name.Length > 2).ToList(); //N个条件叠加 List<Student> list3 = studentList.Where(item => item.Id > 1 && item.Name != null && item.ClassId == 1 && item.Age > 20).ToList();
Linq的优势
1. 减少编码:相比较传统的方式,LINQ减少了要编写的代码量。
2. 可读性强:LINQ增加了代码的可读性,开发人员可以很轻松地理解和维护。
3. 标准化的查询方式:可以使用相同的LINQ语法查询多个数据源。
4. 智能感知提示:LINQ为通用集合提供智能感知提示。
5. 统一的查询语法:LINQ 提供了类SQL的查询语法,使得对于不同数据源(如集合、数据库、XML等)的查询具有统一的语法和风格。这种统一性简化了代码,降低了学习成本,并提高了可读性。
6. 类型安全:LINQ 是强类型的,这意味着编译器在编译时可以检查查询的正确性,避免了一些在运行时才能发现的错误。这种类型安全性提高了代码的质量和稳定性。
7. 编译时错误检查:LINQ 查询在编写时被转换为表达式树,这使得编译器可以在编译时检查查询表达式的正确性,有助于发现和修复错误。
8.集成性:LINQ 是 C# 语言的一部分,可以直接嵌入到代码中。这种集成性使得可以更自然地进行数据查询和处理,无需切换到其他语言或工具。
9. 可组合性:LINQ 查询是可组合的,允许通过链式操作构建复杂的查询。这使得可以在不同的查询条件下动态构建查询,使代码更为简洁和灵活。
10. 适用范围广泛:LINQ 可以用于多种数据源,如对象集合、数据库(通过 Entity Framework、LINQ to SQL)、XML 等。这使得相同的查询技术可以应用于不同的数据存储系统。
11. 更高的抽象级别: LINQ 查询操作提供了更高的抽象级别,可以更直观地表达查询意图,而不必关注底层的实现细节。这使得代码更易读、易维护。
总体来说,LINQ 提供了一种强大且统一的方式来查询和操作数据,它的类型安全性、集成性、可读性和灵活性使得在处理数据时更为便捷和高效。
Linq写查询的两种形式
(1)查询语法
使用标准的方法调用,这些方法是一组叫做标准查询运算符的方法
(2)方法语法
看上去和SQL语句很相似,使用查询表达式形式书写。微软推荐使用查询语法,因为它更易读
在编译时,CLR会将查询语法转换为方法语法
int[] num = { 2, 4, 6, 8, 10 }; var numQuery = from number in num //查询语法 where number < 8 select number; var numMethod = num.Where(x => x < 8); //方法语法
Linq的延迟计算
一般步骤:获取数据源、创建查询、执行查询。
需要注意的是,尽管查询在语句中定义,但直到最后的foreach语句请求其结果的时候才会执行。
int[] number = { 2, 4, 6, 8, 10 }; //获取数据源 IEnumerable<int> lowNum = from n in number //创建并存储查询,不会执行操作 where n < 8 select n; foreach (var val in lowNum) //执行查询 { Console.Write("{0} ", val); }
Linq和Lambda语句示例
1.简单的查询语句
// Linq语法: var data=from a in db.Areas select a ; // Lamda语法: var data=db.Areas; // sql语法: string sqlStr=" SELECT * FROM Areas ";
2.带where的查询
// Linq语法: var data=from a in db.orderInfo where a.orderId > 20 select a ; // Lamda语法: var data=db.orderInfo.Where( t=>t.orderId > 20 ) ; // sql语法: string sqlStr=" SELECT * FROM orderInfo WHERE orderId > 20 ";
3."count,min,max,sum" 函数语句
// Linq语法: var data=( from a in db.orderInfo select a ).Max( p=>p.orderId ) ;//查询该表中最大编号Id var data=( from a in db.orderInfo select a ).Min( p=>p.orderId ) ;//查询该表中最小编号Id var data=( from a in db.orderInfo select a ).Count() ;//查询该表数据总条数 var data=( from a in db.orderInfo select a ).Sum( p=>p.orderMoney ) ;//查询该表中所有消费额的总数(求和) // Lamda语法: var data=db.orderInfo.Max( t=>t.orderId );//查询该表中最大编号Id var data=db.orderInfo.Min( t=>t.orderId );//查询该表中最小编号Id var data=db.orderInfo.Count();//查询该表数据总条数 var data=db.orderInfo.Sum( t=>t.orderMoney );//查询该表中所有消费额的总数(求和) // sql语法: string sqlStr=" SELECT MAX(orderId) FROM orderInfo "; string sqlStr=" SELECT MIN(orderId) FROM orderInfo "; string sqlStr=" SELECT COUNT(*) FROM orderInfo "; string sqlStr=" SELECT SUM(orderMoney ) FROM orderInfo ";
4.数据排序order by desc/asc语句
// Linq语法: var data=from a in db.orderInfo where a.orderId > 20 orderby a.orderId descending select a ;//倒序排序,升序可用ascending关键字 // Lamda语法: // 情况一,根据单字段排序: OrderByDescending var data=db.orderInfo.OrderByDescending( t=>t.orderId ).Where( t=>t.orderId > 20 ) .ToList();// 升序可用OrderBy关键字 // 情况二,根据多字段主次排序:OrderBy == ThenBy var priceMonthEntities = priceMonthApp.GetList().OrderBy(t => t.F_Year).ThenBy(t => t.F_Month).ToList();//先按年升序,再按月升序 // sql语法: string sqlStr=" SELECT * FROM orderInfo WHERE orderId > 20 ORDER BY orderId DESC ";//倒序排序,升序可用ASC关键字
5.top(1)语句
// Linq语法: 取集合中第一个记录 var ss = (from r in db.Am_recProScheme select r).FirstOrDefault(); // Lamda语法: var ss1 = db.Am_recProScheme.FirstOrDefault(); //var ss1 = db.Am_recProScheme.First(); // Sql语法: string sssql = "select top(1) * from Am_recProScheme"; // 取最后一个记录,先倒序排列再取值 ,实例如下: lists.Reverse(); // 先对集合进行倒序 var s2 = lists.FirstOrDefault(); MessageBox.Show(s2.ToString());
6.跳过前N条取余下的数据 Skip语句
//1 Linq语法: var ss = (from r in db.Am_recProScheme orderby r.rpId descending select r).Skip(10); //跳过前10条数据,取10条之后的所有数据 //2 Lamda语法: var ss1 = db.Am_recProScheme.OrderByDescending(p => p.rpId).Skip(10).ToList(); //3 Sql语法: string sssql = "select * from (select ROW_NUMBER()over(order by rpId desc) as rowNum, * from [Am_recProScheme]) as t where rowNum>10";
7.截取指定长度的数据 Take语句
//1 Linq语法: var ss = (from r in db.Am_recProScheme orderby r.rpId descending select r).Take(10); // 截取前10条数据 //2 Lamda语法: var ss1 = db.Am_recProScheme.OrderByDescending(p => p.rpId).Take(10).ToList(); //3 Sql语法: string sssql = "select * from (select ROW_NUMBER()over(order by rpId desc) as rowNum, * from [Am_recProScheme]) as t where rowNum<=10";
8.分页查询语句
【参考1:】 // Linq语法: var data=( from a in db.orderInfo select a ) .Skip((pageIndex-1) * pageSize).Take(pageSize).ToList(); // Lamda语法: pageIndex:当前页码,pageSize:分页数据显示条数 var data=db.orderInfo.Skip((pageIndex-1)* pageSize).Take(pageSize).ToList(); // sql语法: string sqlStr="SELECT TOP pageSize * FROM orderInfo WHERE orderId NOT IN(SELECT TOP( ( pageIndex - 1) * pageSize) orderId FROM orderInfo)"; 【参考2:】 // 1 Linq语法: var ss = (from r in db.Am_recProScheme where r.rpId > 10 orderby r.rpId descending select r).Skip(10).Take(10); //取第11条到第20条数据 //2 Lamda语法: Take(10): 数据从开始获取,获取指定数量(10)的连续数据 var ss1 = db.Am_recProScheme.OrderByDescending(p => p.rpId).Where(p => p.rpId > 10).Skip(10).Take(10).ToList(); //3 Sql语法: string sssql = "select * from (select ROW_NUMBER()over(order by rpId desc) as rowNum, * from [Am_recProScheme]) as t where rowNum>10 and rowNum<=20";
9.分组group by语句
【参考1:】 // Linq语法: var data= from a in db.orderInfo orderby a.orderId descending group a by a.orderType into s select new{ s.key,//分组字段 s.sMoney=s.Sum(a=>a.orderMoney),//分组后算出总的消费额 s.maMoney=s.Max(a=>a.orderMoney),//分组后算出最大的消费额 s.miMoney=s.Min(a=>a.orderMoney)//分组后算出最小的消费额 }; // Lamda语法: //使用GroupBy关键字进行分组查询(单个字段) var data=db.orderInfo.GroupBy(p => p.recType).Select(t=>t.Key).ToList(); //使用GroupBy关键字进行分组查询(多个字段) var data=db.orderInfo.GroupBy(p =>new{ p.recType,p.orderId}).Select(t=>new{ recType=t.Key.recType,orderId=t.Key.orderId}).ToList(); // sql语法: string sqlStr="SELECT orderType , SUM(orderMoney), MAX(orderMoney), MIN(orderMoney) FROM orderInfo GROUP BY orderType"; 【参考2】 //1 Linq语法: var ss = from r in db.Am_recProScheme orderby r.rpId descending group r by r.recType into n select new { n.Key, //这个Key是recType rpId = n.Sum(r => r.rpId), //组内rpId之和 MaxRpId = n.Max(r => r.rpId),//组内最大rpId MinRpId = n.Min(r => r.rpId), //组内最小rpId }; foreach (var t in ss) { Response.Write(t.Key + "--" + t.rpId + "--" + t.MaxRpId + "--" + t.MinRpId); } //2 Linq语法: var ss1 = from r in db.Am_recProScheme orderby r.rpId descending group r by r.recType into n select n; foreach (var t in ss1) { Response.Write(t.Key + "--" + t.Min(p => p.rpId)); } //3 Lamda语法: var ss2 = db.Am_recProScheme.GroupBy(p => p.recType); foreach (var t in ss2) { Response.Write(t.Key + "--" + t.Min(p => p.rpId)); } //4 sql语法: string sssql = "select recType,min(rpId),max(rpId),sum(rpId) from Am_recProScheme group by recType";
10.包含Contains , 类似like ‘%%’语句
// Linq语法: 使用Contains关键字进行模糊匹配 var data= from a in db.orderInfo where a.orderId.Contains(1) select a; // Lamda语法: 使用Contains关键字进行模糊匹配 var data=db.orderInfo.Where(t=>t.F_UserId.Contains("1")).ToList(); // sql语法: 使用like关键字进行模糊匹配 string sqlStr="SELECT * FROM orderInfo WHERE orderId LIKE '%12%'";
11.Sql中的In – 通过Contains实现
【实例1:】 // Linq语法: var data= from a in db.orderInfo where (new int?[2213,43311,32422]).Contains(a.orderId) select a ; // Lamda语法: var data=db.orderInfo.Where(t=>(new int?[2213,43311,32422]).Contains(t.orderId)).ToList(); // sql语法: string sqlStr="SELECT * FROM orderInfo WHERE orderId IN (2213,43311,32422)"; 【实例2:】 // Linq语法: var ss = from p in db.Am_recProScheme where (new int?[] { 24, 25,26 }).Contains(p.rpId) select p; foreach (var p in ss) { Response.Write(p.Sorts); } // sql语法: string st = "select * from Am_recProScheme where rpId in(24,25,26)";
12.内连接 Inner Join
【参考1】 // Linq语法: var ss = from r in db.Am_recProScheme join w in db.Am_Test_Result on r.rpId equals w.rsId orderby r.rpId descending select r; // Lamda语法: var ss1 = db.Am_recProScheme.Join(db.Am_Test_Result, p => p.rpId, r => r.rsId, (p, r) => p).OrderByDescending(p => p.rpId).ToList(); // sql语法: string sssql = "SELECT r.* FROM Am_recProScheme AS r INNER JOIN Am_Test_Result AS t ON r.[rpId] = t.[rsId] ORDER BY r.[rpId] DESC"; 【参考2】 //Linq var ss = from r in db.Am_recProScheme join w in db.Am_Test_Result on r.rpId equals w.rsId orderby r.rpId descending select r; //Lambda var ss1 = db.Am_recProScheme.Join(db.Am_Test_Result, p => p.rpId, r => r.rsId, (p, r) => p).OrderByDescending(p => p.rpId).ToList(); //SQL string sssql = "select r.* from [Am_recProScheme] as r inner join [dbo].[Am_Test_Result] as t on r.[rpId] = t.[rsId] order by r.[rpId] desc";
13.Linq和Lambda实现 左链接、右链接、内链接
-
先准备个数据类
//客户类 public class Customer { public int id { get; set; } public string name { get; set; } public string email { get; set; } } //联系方式类 public class CustomerContact { public int ccid { get; set; } public int CustomerId { get; set; }//客户的id,外键 public string Phone { get; set; } public string Address { get; set; } } //模拟数据 public static List<Customer> GetCustomer()//客户 { List<Customer> list = new List<Customer>(); list.Add(new Customer { id = 1, name = "刘德华", email = "ldh@net.cn" }); list.Add(new Customer { id = 2, name = "张学友", email = "zxy@net.cn" }); list.Add(new Customer { id = 3, name = "黎明", email = "lm@net.cn" }); list.Add(new Customer { id = 4, name = "郭富城", email = "gfc@net.cn" }); list.Add(new Customer { id = 4, name = "古天乐", email = "gtl@net.cn" }); return list; } public static List<CustomerContact> GetCustomerContact()//联系人 { List<CustomerContact> list = new List<CustomerContact>(); list.Add(new CustomerContact { ccid = 1,CustomerId=1, Phone="13566769988",Address="北京"}); list.Add(new CustomerContact { ccid = 2, CustomerId = 1, Phone = "13566769986", Address = "天津" }); list.Add(new CustomerContact { ccid = 3, CustomerId =2, Phone = "13677889900", Address = "香港" }); list.Add(new CustomerContact { ccid = 4, CustomerId = 8, Phone = "13677889332", Address = "上海" }); return list; }
-
Linq:左连接、右连接、内连接
// 1. 左连接 var LeftJoin = from cusetomer in GetCustomer() join cc in GetCustomerContact() on cusetomer.id equals cc.CustomerId // 内连接的基础上, 加入这行代码就实现类左连接 into JoinCC from cc in JoinCC.DefaultIfEmpty() select new { CustomerName = cusetomer.name, phone = cc != null ? cc.Phone : null }; // 2. 右连接: // 右链接跟左链接相反,只需要改一下顺序就可以了。 var LightJoin = from cc in GetCustomerContact() join cusetomer in GetCustomer() on cc.CustomerId equals cusetomer.id // 内连接的基础上, 加入这行代码就实现类左连接 into JoinCC from cusetomer in JoinCC.DefaultIfEmpty() select new { phone = cc.Phone, CustomerName =cusetomer != null ? cusetomer.name : null, }; // 3. 内连接: var InnerJoin = from cc in GetCustomerContact() join cusetomer in GetCustomer() on cc.CustomerId equals cusetomer.id select new { phone = cc.Phone, CustomerName = cusetomer.name }; // 3-2 内连接: 多个条件关联的join var whiteDiagList = (from r1 in diagControlList where r1.ControlRelation == 1 join r2 in args.DiagList on new { code = r1.DiagCode, name = r1.DiagName } equals new{code=r2.DiagCode,name=r2.DiagName} select r1).ToList<DiagControlModel>();
-
Lambda:左连接、右连接、内连接 [ 推荐使用上方Linq写法 ]
// 1. 左连接: var LMLeftJoin = GetCustomer().GroupJoin(GetCustomerContact(), a => a.id, b => b.CustomerId, (a, b) =>new { a,b }) .SelectMany(c=>c.b.DefaultIfEmpty(), (c, b) =>new { CustomerName= c.a.name,Phone=b!=null?b.Phone:null }); // 2. 右连接:参考左连接,两个集合对调位置就行了 // 3. 内连接: var LMInnerJoin = GetCustomer().Join(GetCustomerContact(), a => a.id, b => b.CustomerId , (a, b) => new { CustomerName = a.name, Phone = b.Phone });
14.交集、并集、差集
-
简单类型的交集、并集、差集用法:
List<string> ListA = new List<string>(); List<string> ListB = new List<string>(); List<string> ListResult = new List<string>(); ListResult = ListA.Distinct().ToList();//去重 ListResult = ListA.Except(ListB).ToList();//差集 ListResult = ListA.Union(ListB).ToList(); //并集 ListResult = ListA.Intersect(ListB).ToList();//交集
-
对象类型:List的交集、并集、差集用法
(1)先定义Model
public class ItemModel { public string ItemCode { get; set; } public string ItemName { get; set; } }
(2)定义Model间如何比较
说明: 若不定义,比较的是两个引用
public class ItemModelComparer : IEqualityComparer<ItemModel> { //比较 public bool Equals(ItemModel x, ItemModel y) { bool checkFlag = true; if (Object.ReferenceEquals(x, y)) { checkFlag = true; } else if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) { checkFlag = false; } else { if (x.ItemCode == y.ItemCode) //若Model有多个条件则需要添加。例如 if(x.ItemCode==y.ItemCode && x.Other==y.Other) { checkFlag = true; } else { checkFlag = false; } } return checkFlag; } //实现获取哈希值 public int GetHashCode(ItemModel model) { if (Object.ReferenceEquals(model, null)) return 0; int hashNurse = model.ItemCode.GetHashCode(); // 若两个集合有多个关联条件,则哈希值也需要进行计算 // int hashOther=model.Other.GetHashCode(); // int resultHash=hashNurse^hashOther; // return resultHash; return hashNurse; } }
(3)实例代码参考
List<ItemModel> ListA = new List<ItemModel>(); List<ItemModel> ListB = new List<ItemModel>(); List<ItemModel> ListResult = new List<ItemModel>(); ListResult = ListA.Distinct(new ItemModelComparer()).ToList();//去重 ListResult = ListA.Except(ListB, new ItemModelComparer()).ToList();//差集 ListResult = ListA.Union(ListB, new ItemModelComparer()).ToList(); //并集 ListResult = ListA.Intersect(ListB, new ItemModelComparer()).ToList();//交集
LINQ 提供了一种集成在 C# 中的查询语法,而 Lambda 表达式是一种用于创建匿名函数的简洁方式。它们都为开发者提供了更加简洁、灵活、高效的编程方式,使得处理数据和函数操作更为便捷和强大。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战