ToDictionary() and ToList()

前言:

有两个简单好用的LINQ扩展方法 ToDictionary() 和ToList(),你可能知道或不知道,但是它的的确确可以简化查询转化为集合的任务:

简介: LINQ和延迟执行

据你所认识的LINQ,你可能会不知道这些查询表达式在幕后做了些什么。 让我们说说今天我们示例的目的,我们有一些POCO类(POCO代表传统CLR对象,指的是一个类,它只有非常少的功能,这一概念源自Java POJO)。

1 // just a simple product POCO class.
2 public class Product
3 {
4 public string Name { get; set; }
5 public int Id { get; set; }
6 public string Category { get; set; }
7 }

非常简单的类,对吗? 我不是说程序需要如此简单,只是专注于LINQ本身,而且我们不一定要真正查询。 所以,在我们的程序中我们可以构建一个简单的例子,这些对象的集合的示例如下:

 1 var products = new List<Product>
2 {
3 new Product { Name = "CD Player", Id = 1, Category = "Electronics" },
4 new Product { Name = "DVD Player", Id = 2, Category = "Electronics" },
5 new Product { Name = "Blu-Ray Player", Id = 3, Category = "Electronics" },
6 new Product { Name = "LCD TV", Id = 4, Category = "Electronics" },
7 new Product { Name = "Wiper Fluid", Id = 5, Category = "Automotive" },
8 new Product { Name = "LED TV", Id = 6, Category = "Electronics" },
9 new Product { Name = "VHS Player", Id = 7, Category = "Electronics" },
10 new Product { Name = "Mud Flaps", Id = 8, Category = "Automotive" },
11 new Product { Name = "Plasma TV", Id = 9, Category = "Electronics" },
12 new Product { Name = "Washer", Id = 10, Category = "Appliances" },
13 new Product { Name = "Stove", Id = 11, Category = "Electronics" },
14 new Product { Name = "Dryer", Id = 12, Category = "Electronics" },
15 new Product { Name = "Cup Holder", Id = 13, Category = "Automotive" },
16 };

就是说,有这些产品的对象集合,你需要查询它们。 例如,我们可以这样得到一个所有产品实例的类别为“Electronics”的集合:

1 var electronicProducts = products.Where(p => p.Category == "Electronics");

许多扩展方法(包括Where() )的查询结果是创建一个迭代器通过移动列表来执行查询。 因此,此时的electronicProducts不是List<Product>,只是IEnumerable<Product>,它会在您使用这个列表时动态求值. 这就是LINQ中强大的延迟执行,在你需要结果前,都不会对表达式求值。 此时我们可以去查询electronicProducts,这样我们就可以得到结果列表!

让我看一下下面的结果是什么:

1 // select all electronics, there are 7 of them
2 IEnumerable<Product> electronicProducts = products.Where(p => p.Category == "Electronics");
3
4 // now clear the original list we queried
5 products.Clear();
6
7 // now iterate over those electronics we selected first
8 Console.WriteLine(electronicProducts.Count());

你认为结果是7还是0? 答案是0,因为即使我们第2行上设置一个查询所有电子产品,但是我们在第5行清除列表。 因此,当我们在第8行实际处理查询列表(执行Count())是空的,没有找到结果。

如果你感到困惑,认为它是这样的: 创建一个使用LINQ查询扩展方法(和LINQ表达式语法)很像定义一个存储过程, 在你调用它之前都没有“运行”。 我知道这不是100%准确的比喻,但是希望你要知道LINQ表达式在语句2是没有执行的,我们处理了IEnumerable才执行。

ToList() LINQ扩展方法

如果你想立即得到(存储)LINQ表达式的结果,你应该把它到导入到另一个集合,这样就可以修改。 当然,你可以手动建立一个列表,然后以各种方式填充。

 1  IEnumerable<Product> electronicProducts = products.Where(p => p.Category == "Electronics");
2
3 // You could create a list and then hand-iterate - BULKY!
4 var results = new List<Product>();
5
6 foreach (var product in electronicProducts)
7 {
8 results.Add(product);
9 }
10
11 // OR, you could take advantage of AddRange() - GOOD!
12 var results2 = new List<Product>();
13 results2.AddRange(electronicProducts);
14
15 // OR, you could take advantage of List's constructor that takes an IEnumerable<T> - BETTER!
16 var results3 = new List<Product>(electronicProducts);

实际上,使用一个循环,通常是非常冗长的,或者你可能利用AddRange()或List<T>函数构造 IEnumerable<T>列表。

但是你可以用另一种方式,。 LINQ扩展方法之上包含了ToList(),你可以将任何IEnumerable<T>来填充一个 List<T>。如果你想用一步执行查询和填充,这很方便:

var electronicProducts = products.Where(p =>  p.Category == "Electronics").ToList();

现在,  List<T>代替 electronicProducts 作为IEnumerable<T> 动态执行的原始集合,这将是另一个新的集合,修改不会影响原来的集合。

当然,这有优点也有缺点。 通常,如果你只是要遍历的结果和过程,你不需要(也不想)将它存储在一个单独的列表,这只会浪费内存,后来还需要垃圾收集。 然而,如果你想保存子集,并将它分配给另一个类,ToList()是非常方便的,你不需要担心改变原来的集合。

ToDictionary()LINQ扩展方法

ToList() 使用IEnumerable<T>并将其转换为 List<T>,那么 ToDictionary()也是类似的。大多数情况ToDictionary()是一个非常方便的方法,将查询的结果(或任何 IEnumerable<T>)转换成一个Dictionary<TKey,TValue>。 关键是您需要定义T如何分别转换TKey和TValue。

如果说我们有超级大的产品列表,希望把它放在一个Dictionary<int, product>,这样我们可以根据ID得到最快的查找时间。 你可能会这样做:

1  var results = new Dictionary<int, Product>();
2 foreach (var product in products)
3 {
4 results.Add(product.Id, product);
5 }

和它看起来像一个很好的代码,但是我们可以轻松地使用LINQ而无需手写一大堆逻辑:

1 var results = products.ToDictionary(product =>  product.Id);

它构造一个Dictionary<int, Product> ,Key是产品的Id属性,Value是产品本身。 这是最简单的形式ToDictionary(),你只需要指定一个key选择器。 如果你想要不同的东西作为你的value? 例如如果你不在乎整个Product,,你只是希望能够转换ID到Name? 我们可以这样做:

1 var results = products.ToDictionary(product =>  product.Id, product =>  product.Name);

这将创建一个 Key为Id,Value为Name 的Dictionary<int, string>,。由此来看这个扩展方法有很多的方式来处理IEnumerable<T> 集合或查询结果来生成一个dictionary。

注:还有一个Lookup<TKey, TValue>类和ToLookup()扩展方法,可以以类似的方式做到这一点。 他们不是完全相同的解决方案(Dictionary和Lookup接口不同,他们的没有找到索引时行为也是不同的)。

因此,在我们的Product 示例中,假设我们想创建一个Dictionary<string, List<Product>> ,Key是分类,Value是所有产品的列表。 在以前你可能自实现自己的循环:

 1 // create your dictionary to hold results
2 var results = new Dictionary<string, List<Product>>();
3
4 // iterate through products
5 foreach (var product in products)
6 {
7 List<Product> subList;
8
9 // if the category is not in there, create new list and add to dictionary
10 if (!results.TryGetValue(product.Category, out subList))
11 {
12 subList = new List<Product>();
13 results.Add(product.Category, subList);
14 }
15
16 // add the product to the new (or existing) sub-list
17 subList.Add(product);
18 }

但代码应该更简单! 任何新人看着这段代码可能需要去详细分析才能完全理解它,这给维护带来了困难

幸运的是,对我们来说,我们可以利用LINQ扩展方法GroupBy()提前助力ToDictionary()和ToList():

 // one line of code!

 var results = products.GroupBy(product =>  product.Category)

.ToDictionary(group =>  group.Key, group =>  group.ToList());

GroupBy()是用Key和IEnumerable创建一个IGrouping的LINQ表达式查询语句。 所以一旦我们使用GroupBy() ,所有我们要做的就是把这些groups转换成dictionary,所以我们的key选择器 (group => group.Key) 分组字段(Category),使它的成为dictionary的key和Value择器((group =>  group.ToList()) 项目,并将它转换成一个List<Product>作为我们dictionary的Value!

这样更容易读和写,单元测试的代码也更少了! 我知道很多人会说lamda表达式更难以阅读,但他们是c#语言的一部分,高级开发人员也必须理解。我认为你会发现当你越来越多的使用他们后,代码能被更好的理解和比以前更具可读性。

译者按:我用Transmate翻译的同时,也尽量保留原文的描述,避免信息的丢失,如果这篇文章对你有一点儿用,请不要吝啬点推荐,让更多人看到,谢谢!

ToDictionary() and ToList()的更多相关文章

  1. [译文]c# /.Net 技巧: ToDictionary() and ToList()

    前言: 有两个简单好用的LINQ扩展方法 ToDictionary() 和ToList(),你可能知道或不知道,但是它的的确确可以简化查询转化为集合的任务: 简介: LINQ和延迟执行 据你所认识的L ...

  2. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  3. [.net 面向对象编程基础] (20) LINQ使用

    [.net 面向对象编程基础] (20)  LINQ使用 通过上节LINQ的基础知识的学习,我们可以开始使用LINQ来进行内存数据的查询了,我们上节说了LINQ的定义为:Language Integr ...

  4. LINQ实战

    转载:http://www.cnblogs.com/yubinfeng/p/4567064.html    原作者:尼古拉斯~yu 文章部分内容在原文的基础上有删改. 我相信在net进阶的这个阶段,很 ...

  5. 【Linq】常用语法汇总

    语言继承查询(Language Integrated Query, LINQ),在C#编程语言中集成了查询语法,可以用相同的语法访问不同的数据源,LINQ提供了不同数据源的抽象层,所以可以使用相同语法 ...

  6. Linq之旅:Linq入门详解(Linq to Objects)【转】

    http://www.cnblogs.com/heyuquan/p/Linq-to-Objects.html Linq之旅:Linq入门详解(Linq to Objects) 示例代码下载:Linq之 ...

  7. Linq之旅:Linq入门详解(Linq to Objects)(转)

    http://www.cnblogs.com/heyuquan/p/Linq-to-Objects.html 示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细 ...

  8. LINQ标准查询操作符(四) —AsEnumerable,Cast,OfType,ToArray,ToDictionary,ToList,ToLookup,First,Last,ElementAt

    十.转换操作符 转换操作符是用来实现将输入对象的类型转变为序列的功能.名称以“As”开头的转换方法可更改源集合的静态类型但不枚举(延迟加载)此源集合.名称以“To”开头的方法可枚举(即时加载)源集合并 ...

  9. Linq转换操作之ToArray,ToList,ToDictionary源码分析

    Linq转换操作之ToArray,ToList,ToDictionary源码分析 一:linq中的转换运算符 1. ToArray 我们经常用在linq查询上吧. linq只能运用在IEnumerab ...

随机推荐

  1. Cocos2d-x示例:单点触摸事件

    为了让大家掌握Cocos2d-x中的事件机制,以下我们以触摸事件为例.使用事件触发器实现单点触摸事件.该实比如图8-3所看到的,场景中有三个方块精灵,显示顺序如图8-3所看到的,拖拽它们能够移动它们. ...

  2. ProgressBar样式总结与自己主动填充方法(代码)

    有时候开发的时候须要用一个进度条告知用户眼下正在执行一个耗时操作,可是并不须要明白知道某个value来setProgress,所以就能够自己定义一个时间和进度让进度条自己主动执行了. 以下是代码: H ...

  3. ZOJ 3728 Collision

    ---恢复内容开始--- 今天无事水一水,结果就看到这个水题了! 题意思是 有俩个区域如图 求在俩个圆之间的运动时间 给出 初始的开始点和速度的矢量式;而且这个点 不再俩个圆之间的区域,且碰到内测园会 ...

  4. Nutch 二次开发parse纸

    大约nutch基础知识可以参考lemo柱 nutch支持二次开发,为了满足搜索的准确性的问题,内容提取出来作为索引的内容,相应的是parse_text的数据.我使用的事nutch1.4 版本号,在cy ...

  5. 白板编程浅谈——Why, What, How(转)

    原文链接:http://lucida.me/blog/whiteboard-coding-demystified/ 这篇文章节选自我正在撰写的一本关于应届生面试求职的书籍,欢迎在评论或微博(@peng ...

  6. BZOJ 1052 HAOI2007 覆盖问题 二分法答案+DFS

    标题效果:特定n点.涵盖所有的点与同方三面.斧头要求方垂直边界,最小平方的需求方长值 最大值至少.答案是很明显的二分法 但验证是一个问题 考虑仅仅有三个正方形,故用一个最小矩形覆盖这三个正方形时至少有 ...

  7. Winform WebBrowser引用IE版本问题

    做了一个Winform的项目.项目里使用了WebBrowser控件.以前一直都以为WebBrowser是直接调用的系统自带的IE,IE是呈现出什么样的页面WebBrowser就呈现出什么样的页面.其实 ...

  8. C# winForm里窗体嵌套

    ShowAllPage sAllPage = new ShowAllPage();            sAllPage.FormBorderStyle = FormBorderStyle.None ...

  9. [渣译文] SignalR 2.0 系列: 开始使用SignalR 2.0

    原文:[渣译文] SignalR 2.0 系列: 开始使用SignalR 2.0 英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with ASP ...

  10. atitit.404错误调查过程汇总

    atitit.404错误调查过程汇总 #----------jsp  head  errorPage="" del zeu ok le. #------resin server. ...