文章目录

  • 介绍
  • 具体案例
    • 将对象转为字典集合
    • 将原始序列进行分组
    • 按员工所属部门
    • DefaultIfEmpty方法的作用
    • 将分组后的序列重新排序
    • 使用并行LINQ
  • 总结

介绍

随着.net core越来越流行,对.net core 基础知识的了解,实际应用等相关的知识也应该有所了解。所以就有了这篇文章,案例都是来自阅读的书籍,或者实际工作中感觉比较有用的应用。分享亦总结。

本文主要介绍 .net core 相关的LINQ案例。

具体案例

将对象转为字典集合

【导语】

ToDictionary 扩展方法比较有趣,它可以将序列中的每个元素转换位Key-Value对,然后组成以一个字典集合。

本实例用到以下重载版本。

Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector);

其中,keySelector 参数与 elementSelector 参数都是委托类型,分别用于返回作为字典中元素的 KeyValue

【操作流程】

步骤1:新建控制台应用程序项目。

步骤2:引入命名空间。

using System.Collections.Generic;
using System.Linq;

步骤3:声明一个类,它表示一件产品的基础信息。

public class Production
{/// <summary>/// 产品编号/// </summary>public int PID { get; set; }/// <summary>/// 产品名称/// </summary>public string Name { get; set; }/// <summary>/// 产品尺寸/// </summary>public float Size { get; set; }/// <summary>/// 生产数量/// </summary>public int Quantity { get; set; }
}

步骤4:在 Main 方法中声明一个 Production 数组,并进行实例化。

Production[] prds =
{new Production{PID = 4007,Name = "产品 1",Size = 123.45f,Quantity = 65},new Production{PID = 4008,Name = "产品 2",Size = 77.01f,Quantity = 100},new Production{PID = 4012,Name = "产品 3",Size = 45.13f,Quantity = 25}
};

步骤5:调用 ToDictionary 方法将数组中的 Production 元素转为字典结构的数据。其中,PID 属性将作为字典的 KeyName 属性将作为字典的 Value

IDictionary<int, string> dic = prds.ToDictionary(p => p.PID, p => p.Name);

步骤6:输出新生成的字典集合中的元素信息。

Console.WriteLine("转化得到的字典数据:");
foreach(var kp in dic)
{Console.WriteLine("{0} - {1}", kp.Key, kp.Value);
}

步骤7:运行应用程序项目,结果如下。

将原始序列进行分组

【导语】

将原始序列中的元素进行分组,可以调用 GroupBy 扩展方法。此方法有多个重载的版本,本实例使用了以下重载的形式。

IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, IEnumerable<TSource>, TResult> resultSelector);

其中有三个类型参数:TSource 是原始序列中的元素,TKey 是分组依据(例如按某个对象的 Age 属性进行分组,那么 TKey 就是 Age 属性的类型),TResult 是返回给调用方的以分组序列中的元素类型。

keySelector 委托用于产生分组依据,resultSelector 委托则用于产生分组结果。resultSelector 委托有两个输入参数:第一个参数是分组依据,即该分组的“标题”;第二个擦拭你和是隶属该分组下的元素所组成的子序列。

本实例中,Student 类表示学生的信息,代码将对学生列表中的对象按照它们各自所参与的课程分组,例如,参与学习 C++ 语言的学生便构成了一个分组。

【操作流程】

步骤1:新建控制台应用程序项目。

步骤2:引入以下命名空间。

using System.Linq;
using System.Text;

步骤3:声明Student类,表示学生信息。

public class Student
{public int ID { get; set; }public string Name { get; set; }public string Course { get; set; }
}

步骤4:在 Main 方法中实例化一个 Student 数组并填充一些示例元素。

Student[] stus =
{new Student{ID = 201,Name = "小王",Course = "C"},new Student{ID = 202,Name = "小曾",Course = "C++"},new Student{ID = 203,Name = "小吕",Course = "C++"},new Student{ID = 204,Name = "小孙",Course = "C#"},new Student{ID = 205,Name = "小郑",Course = "C"},new Student{ID = 206,Name = "小叶",Course = "C"},new Student{ID = 207,Name = "小苏",Course = "C#"},new Student{ID = 208,Name = "小梁",Course = "Delphi"}
};

步骤5:调用 GroupBy 方法,按照学生所参与的课程进行分组。

var result = stus.GroupBy(s => s.Course, (gKey, gItems) => (GroupKey: gKey, ItemCount: gItems.Count(), Items: gItems));

调用以上方法后,产生的结果类型是三元素序列,其中 GroupKey 字段表示分组标题,ItemCount 字段表示该分组下的学生数量,Items 字段表示属于该分组的学生列表。

步骤6:输出分组后的序列信息。

Console.WriteLine("学员参与课程汇总:");
StringBuilder strbuilder = new StringBuilder();
foreach(var g in result)
{strbuilder.AppendFormat("课程:{0}\n", g.GroupKey);strbuilder.AppendFormat("  参与人数:{0}\n", g.ItemCount);strbuilder.AppendLine("  名单:");foreach (Student s in g.Items){strbuilder.AppendFormat("    {0} - {1}\n", s.ID, s.Name);}
}
Console.WriteLine(strbuilder);

以上代码使用了 StringBuilder 类来组装字符串,再通过 WriteLine 方法进行输出。

步骤7:运行应用程序项目,结果如下。

按员工所属部门

【导语】

本实例假设 Employee 类表示某公司的员工信息,其中,Name 属性是员工名称,Department 属性是员工所属的部门。随后将员工信息序列按照部门进行分组。

LINIQ 查询中对序列进行分组需要用到 group···by 子句,group 关键字之后是要进行分组的对象,by 关键字之后是分组标题,即依据什么进行分组。

分组后会返回一个实现了 IGrouping 接口的对象实例,该接口也继承了 IEnumerable 接口成员,使得代码支持枚举此分组下的元素,同时又带有要给 Key 属性,即分组标题。

【操作流程】

步骤1:新建控制台应用程序项目。

步骤2:定义 Employee 类。

public class Employee
{public string Name { get; set; }public string Department { get; set; }
}

步骤3:初始化一个 Employee 数组。

Employee[] emps =
{new Employee{ Name = "小黄", Department = "财务部" },new Employee{ Name = "小卢", Department = "开发部" },new Employee{ Name = "小邢", Department = "开发部" },new Employee{ Name = "小陈", Department = "财务部" },new Employee{ Name = "小卜", Department = "公关部" },new Employee{ Name = "小罗", Department = "仓储部" },new Employee{ Name = "小许", Department = "开发部" },new Employee{ Name = "小田", Department = "仓储部" }
};

步骤4:通过 LINQ 查询,将员工信息序列按部门名称分组。

var q = from e in empsgroup e by e.Department;

步骤5:输出分组后的序列信息。

foreach (var g in q)
{Console.WriteLine("{0}:", g.Key);foreach (var emp in g){Console.WriteLine("    {0}", emp.Name);}Console.WriteLine();
}

步骤6:运行应用程序项目,结果如下。

DefaultIfEmpty方法的作用

【导语】

DefaultIfEmpty 方法的作用是:当某个序列中没恶意元素时,将返回该元素类型的默认值,例如下面的序列。

List<int > l = new List<int>();

此时,列表中每月元素,调用以下代码返回一个只有单个元素的序列,其中包含 int 类型的默认值,即 0

var e = l.DefaultIfEmpty();

DefaultIfEmpty 方法一般用于联合查询中,当第二个序列中不存在与第一个序列匹配的元素时将返回元素的默认值,以保证第一规格序列中的元素能够全部查询出来,即“左外联”查询。

本实例将定义两个序列:一个是订单序列(Order 类表示),另一个是订单详细数据序列(OrderDetails 类表示),而 Order 类的 Details 属性会引用一个关联的 OrderDetails 实例。在两个序列联合查询是,一旦订单详细数据序列中不存在与 Details 属性匹配(Details 属性为 null),就返回一个固定的 OrderDetails 实例作为默认值。

【操作流程】

步骤1:新建控制台应用程序项目。

步骤2:声明 OrderDetails 类,表示订单详细信息。

public class OrderDetails
{public int Amount { get; set; }public decimal Price { get; set; }public string Code { get; set; }
}

步骤3:声明 Order 类,表示订单信息,其中 Details 属性应用 OrderDetails 实例。

public class Order
{public int ID { get; set; }public DateTime Date { get; set; }public bool State { get; set; }public OrderDetails Details { get; set; }
}

步骤4:实例化两个 OrderDetails 对象。

OrderDetails d1 = new OrderDetails
{Amount = 10,Price = 2.5M,Code = "T-70770"
};
OrderDetails d2 = new OrderDetails
{Amount = 12,Price = 3.2M,Code = "T-70778"
};

步骤5:实例化三个 Order 对象。第三个 Order 实例的 Details 属性每月引用任何对象。

Order o1 = new Order
{ID = 1,Date = new DateTime(2018, 3, 1),State = true,Details = d1
};
Order o2 = new Order
{ID = 2,Date = new DateTime(2018, 3, 13),State = false,Details = d2
};
Order o3 = new Order
{ID = 3,Date = new DateTime(2018, 3, 18),State = true,Details = null
};

步骤6:声明两个 List 集合,用于存放以上对象。

List<Order> orders = new List<Order> { o1, o2, o3 };
List<OrderDetails> details = new List<OrderDetails> { d1, d2 };

步骤7:对上述两个序列进行联合查询。

var q = from o in ordersjoin d in details on o.Details equals d into gfrom x in g.DefaultIfEmpty(new OrderDetails { Amount = 0, Price = 0.00M,Code = "未知编码" })select (OrderID: o.ID, Amout: x.Amount, Code: x.Code);

当每月匹配项时,产生一个固定的 OrderDetails 对象作为默认值,其中 Amount 属性为 0Price 属性为 0.00Code 属性为"未知编码"。

步骤8:输出查询下结果到控制台。

foreach (var i in q)
{Console.WriteLine("{0,-11}{1,-10}{2,-20}", i.OrderID, i.Amout, i.Code);
}

步骤9:运行应用程序项目,结果如下。

将分组后的序列重新排序

【导语】

group 子句可以搭配 into 关键字,暂时存放依据分好组的元素,例如:

group x by x.Type into gs

其中,gs 就是保存该分组序列的临时变量名。

在完成对数据序列的分组后,可以嵌套一个 LINQ 查询对组内元素进行重新排列,例如:

from a in someListgroup a by a.Type int gklet q2 = (from b in gk orderby b select b)select new { Key = gk.Key, SubItems = q2 };

其中,临时变量 q2 所引用的就是一个嵌套 LINQ 查询。

【操作流程】

步骤1:新建控制台应用程序项目。

步骤2:初始一个 string 类型的数组。

string[] arrsrc =
{"at", "act", "market", "fable", "also", "alt", "bee", "back", "book", "build", "face", "full", "fish", "food", "find", "meet", "make", "moo", "muklek"
};

步骤3:

 var q = from s in arrsrcgroup s by s[0].ToString().ToUpper() into gorderby g.Keylet nq = (from w in gorderby wselect w)select (Key: g.Key, Items: nq);

步骤4:

foreach (var t in q)
{Console.WriteLine(t.Key);foreach (var sub in t.Items){Console.WriteLine("  {0}", sub);}
}

步骤5:运行应用程序项目,结果如下。

使用并行LINQ

【导语】

开启 LINQ 查询的并行模式,只需要在原序列上调用 AsParallel 扩展方法,但是不应该滥用并行模式。如果查询的量很小,并且在查询的过程中每月过于复杂的处理,一般不建议使用并行模式。

满足以下条件的查询,可以考虑以并行模式执行:

  • 序列中数量很大。
  • LINQ 查询中 whereselect 子句上需要额外的处理工作(例如要转换类型)
  • 对产生的结果没有严格的顺序要求(尽管并行查询可以调用 AsOrdered 扩展方法来维持序列的顺序,但在一定程度上会降低新能,仅在必要时使用)。

本实例将定义一个 Rectangle 结构,它表示一个矩形的信息,其中包含宽度和高度两个字段。实例代码以并行方式生成要给庞大的 Rectangle 序列,仍然会分别用普通和并行两种模式进行 LINQ 查询,最后分别统计出两种模式下执行 LINQ 查询所消耗的时间(单位是 ms)。

【操作流程】

步骤1:新建控制台应用程序项目。

步骤2:定义 Rectangle 结构。

public struct Rectangle
{public double Width;public double Height;
}

步骤3:初始化 Rectangle 序列,本例使用 ConcurrentQueue<T> 集合来包装,该集合支持并行操作,并且是线程安全的。

ConcurrentQueue<Rectangle> testList = new ConcurrentQueue<Rectangle>();

步骤4:以并行方式向集合中添加元素。

Parallel.For(20, 300000000, n =>
{testList.Enqueue(new Rectangle{Width = n,Height = n});
});

步骤5:分别以普通模式、并行模式执行 LINQ 查询,在select子句中计算矩形的面积。Stopwatch 组件的作用是计算执行代码所消耗的时间。

Stopwatch watch = new Stopwatch();
watch.Restart();
var q1 = from x in testListselect x.Width * x.Height;
watch.Stop();
Console.WriteLine("普通模式,耗时:{0} ms", watch.ElapsedMilliseconds);
watch.Restart();
var q2 = from x in testList.AsParallel()select x.Width * x.Height;
watch.Stop();
Console.WriteLine("并行模式,耗时:{0} ms", watch.ElapsedMilliseconds);

步骤6:运行应用程序项目,结果如下。

出输出来看,在并行模式下执行 LINQ 查询确实提升了性能。

注意:Stopwatch 组件自身在运行期间也会占用一定的 CPU 资源,因此该组件所统计的耗时并非完全准确,仅供参考。

总结

本文到这里就结束了,下一篇将介绍文件与I/O的知识案例。

.net core精彩实例分享 -- LINQ相关推荐

  1. .net core精彩实例分享 -- 泛型和集合

    文章目录 介绍 具体案例 限制泛型参数只能使用值类型 泛型参数的输入和输出 将抽象类作为类型约束 使用Span提升处理字符串的性能 多个Task同时操作ConcurrenBag集合 跨线程访问Bloc ...

  2. .net core精彩实例分享 -- 应用配置和数据库访问

    文章目录 介绍 具体案例 自定义环境变量的命名前缀 自定义命令行参数映射 使用JSON文件来配置选项类 在应用程序运行期间创建SQLite数据库 总结 介绍 随着.net core越来越流行,对.ne ...

  3. .net core精彩实例分享 -- 依赖注入和中间件

    文章目录 介绍 具体案例 临时访问服务 以委托形式定义中间件 带参数中间件 IMiddleware中间件的用途 让 HTTP 管道"短路" 中间件的分支映射 文件服务 总结 介绍 ...

  4. .net core精彩实例分享 -- 应用启动

    文章目录 介绍 具体案例 配置Web服务器的URL 配置Web项目的调试方案 基于方法约定的Startup类 使用非预定义环境 总结 介绍 随着.net core越来越流行,对.net core 基础 ...

  5. .net core精彩实例分享 -- 反射与Composition

    文章目录 介绍 具体案例 用Activator类创建类型实例 检查类型上所应用的自定义Attribute 通过协定来约束导出类型 导入多个类型 封装元数据 总结 介绍 随着.net core越来越流行 ...

  6. .net core精彩实例分享 -- 网络编程

    文章目录 介绍 具体案例 从Web服务器上下载图片 使用HttpClient类向Web服务器提交数据 总结 介绍 随着.net core越来越流行,对.net core 基础知识的了解,实际应用等相关 ...

  7. .net core精彩实例分享 -- 异步和并行

    文章目录 介绍 具体案例 等待线程信号--ManualResetEvent 等待线程信号--AutoResetEvent 多个线程同时写一个文件 串联并行任务 使用Parallel类执行并行操作 为每 ...

  8. .net core精彩实例分享 -- 序列化

    文章目录 介绍 具体案例 将类型实例序列号危机JSON格式 将数据协定序列化为JSON格式 总结 介绍 随着.net core越来越流行,对.net core 基础知识的了解,实际应用等相关的知识也应 ...

  9. .net core精彩实例分享 -- 文件与I/O

    文章目录 介绍 具体案例 创建Zip压缩文件 使用GZipStream类压缩文件 实现本地进程之间的通信 单向管道通信 总结 介绍 随着.net core越来越流行,对.net core 基础知识的了 ...

最新文章

  1. 代码审查规范(试用版)
  2. 一文带你搞懂 MySQL 分区!
  3. 将两个数组河滨_两名3岁男孩在河滨公园玩耍迷了路 幸亏被好心人“捡”到
  4. 微信8年,你从中学到了什么?
  5. JMS ActiveMQ案例
  6. 【界面无法显示】getStorageInfoSync和getStorageSync的区别
  7. Layui 是否开启合计行区域
  8. VirtualLab专题实验教程-1.超表面纳米柱及其相位分析
  9. 异速联服务器配置 虚拟机,异速联 安装教程
  10. Complete Internet Repair(电脑网络修复工具)官方中文版V6.0.3.5003 | 富有成效的电脑网络修复大师 | 电脑网络修复怎么修复?
  11. matlab求松弛迭代发,[求助]Matlab超松弛迭代法 高手帮忙看看
  12. Android集成bilibili播放器以及弹幕
  13. Aggressive cows(c语言)
  14. 服务器负载过高的处理方式
  15. ukf 在matlab 下的实现,ukf在matlab下的实现
  16. Win11的两个实用技巧系列之dns异常和打印机重命名
  17. Qt实现一个简易截图工具(支持缩放、移动、保存、复制到粘贴板)
  18. C++语言程序设计第五版 - 郑莉(第三章课后习题)
  19. List集合中的常见面试题以及简单思路
  20. 【重识云原生】第三章云存储3.5节——商用分布式云存储方案

热门文章

  1. c语言求字符串转换成双精度_C语言实现把字符串中的数字转换成整数
  2. 写python代码的心得体会_写python代码的一点感想
  3. java父类引用子类_java多态,如何理解父类引用指向子类对象
  4. 为你的平面海报设计提供灵感和思路
  5. 优秀APP UI设计作品可以临摹学习,还没灵感就撞墙吧!
  6. 插画在UI的应用体验,太美好了!这样的模板让你的用户更加喜欢!
  7. h5的fetch方法_HTML5 fetch API
  8. c++判断奇偶_第十一届(今年)蓝桥杯省模拟赛 试题+源码 C/C++详解
  9. JDK,JRE和JVM之间的区别
  10. 构建一个自定义CentOS7内核