14.2.集合初始化器
- static void Main(string[] args)
- {
- List<string> sevenWorldBlunders;
- sevenWorldBlunders = new List<string>()
- {
- "Wealth without work",
- "Pleasure without conscience",
- "Knowledge without character",
- "Commerce without morality",
- "Science without humanity",
- "Worship without sacrifice",
- "Politics without principle"
- };
- Print(sevenWorldBlunders);
- Console.Read();
- }
- private static void Print<T>(IEnumerable<T> items)
- {
- foreach (T item in items)
- {
- Console.WriteLine(item);
- }
- }
集合初始化器,基本条件
之所以允许不支持 ICollections<T> 的集合使用初始化器,原因有两个。
1.大多数集合(实现了 IEnumerable<T> 的类型)都没有实现 ICollections<T> ,这使得集合初始化器用处不大。2.由于要匹配方法名,签名要和初始化兼容,所以可以在集合中初始化更加多样化的数据项。
例如, —— 只要一个Add()方法的签名兼容于a,另一个Add()方法兼容b,c。
匿名类型集合初始化器
解决方法1
是定义一个下面的方法:- static List<T> CreateList<T>(T t)
- {
- return new List<T>();
- }
解决方法2
使用数组初始化器。由于不能在构造器中指定数据类型,所以允许使用 ,从而为匿名数组初始化器使用数组初始化语法。
- static void Main(string[] args)
- {
- var worldCup2006Finalists = new[]{
- new{
- TeamName = "France",
- Players = new string[]{
- "Fabien Barthez","Gregory Coupet",
- "Mickael Landreau","Eric Abidal"
- }
- },
- new{
- TeamName = "Italy",
- Players = new string[]{
- "Gianlugi Buffon","Angelo Peruzzi",
- "Marco Amelia","Cristian Zaccardo"
- }
- },
- };
- Print(worldCup2006Finalists);
- Console.ReadKey();
- }
- private static void Print<T>(IEnumerable<T> items)
- {
- foreach (T item in items)
- {
- Console.WriteLine(item);
- }
- }
14.3.是什么使类成为一个集合:IEnumerable<T>
14.3.2 foreach和IEnumerable<T>
因为数组的长度固定,支持索引操作符( [ ])。但并不是所有类型的集合都包含已知数量的元素。包括Stack<T>、Queue<T>以及Dictionary<Tkey, Tvalue>,都不支持索引获取元素。迭代器(iterator)模式提供这个能力。
System.Collections.Generic.IEnumerator<T>和非泛型 System.Collections.Generic.IEnumerator接口的设计允许使用迭代器模式遍历集合。 前面代码忽略了CIL代码两个重要实现细节: 交错和错误处理。 假如同时两个循环交错遍历同一个集合(一个foreach嵌套另一个foreach),集合必须确保当前的 MoveNext()能正确定位到下一个元素,问题是交错循环可能互相干扰(多线程执行循环会发生同样问题)。 因为解决这个问题, 集合类不直接支持 IEnumerator 和 IEnumerator<T>接口。所以,用另一个接口, IEnumerable<T>,它唯一的方法就是 GetEnumerator()。枚举数(enumerator)相当于一个“游标”或者“书签”。可以有多个书签,移动每个书签都可以独立于其他书签来遍历集合。 由于实现 IEnumerator<T> 接口维持状态,退出循环后,有时需要对状态进行清理。为此, IEnumerator<T> 接口继承 IDisposable。假如实现 IDisposable,就会调用 Dispose()方法。就能在foreach循环退出之后调用Dispose()。与最终的CIL代码等价。 由于IEnumerator<T>支持IDisposable接口,可以使用 using关键字简化 后面的使用标准查询操作符示例类: Patent类和 Inventor类 PatentData 类 输出Main() 这里的Select()调用,并未造成输出变化。这里只是巧合 例子3:匿名方法 Where():在“垂直”方向筛选集合(减少集合中项的数量)Select():在“水平”方向减少集合的规模(减少列的数量) 部门和员工数据 先看看定义 定义里 Func<TOuter, IEnumerable<TInner>, TResult> //输出结果 处理集合构成的集合。
IEnumerator<T>从IEnumerator派生,后者包含3各成员。
1.bool MoveNext():从集合中一个元素移动到下一个元素,同时检测是否已经枚举完集合中的每个元素。
2.只读属性Current:返回当前元素。
利用这2个成员,只需要一个while循环即可遍历集合
3.Reset()方法一般抛出Notimplemented Exception异常,永远不要调用它。
使用while遍历集合
Stack<T>
public class Stack<T> : IEnumerable<T>, IEnumerable, ICollection, IReadOnlyCollection<T>
1
状态共享
- Stack<int> stack = new Stack<int>();
- Stack<int>.Enumerator stackEnumerator = stack.GetEnumerator();
- int number;
- while (stackEnumerator.MoveNext())
- {
- number = stackEnumerator.Current;
- Console.WriteLine(number);
- }
2
清理状态
- System.Collections.Generic.Stack<int> stack = new System.Collections.Generic.Stack<int>();
- Stack<int>.Enumerator stackEnumerator = stack.GetEnumerator();
- IDisposable disposable;
- try
- {
- int number;
- while (stackEnumerator.MoveNext())
- {
- number = stackEnumerator.Current;
- Console.WriteLine(number);
- }
- }
- finally
- {
- //显式类型转换用于IEnumerator < T >
- disposable = (IDisposable)stackEnumerator;
- disposable.Dispose();
- //IEnumerator将使用操作符,除非IDisposable支持在编译时是已知的
- disposable = (stackEnumerator as IDisposable);
- if (disposable != null)
- {
- disposable.Dispose();
- }
- }
- System.Collections.Generic.Stack<int> stack = new System.Collections.Generic.Stack<int>();
- int number;
- using (Stack<int>.Enumerator stackEnumerator = stack.GetEnumerator())
- {
- while (stackEnumerator.MoveNext())
- {
- number = stackEnumerator.Current;
- Console.WriteLine(number);
- }
- }
14.4.标准查询操作符
- public class Patent
- {
- public string Title { get; set; }
- public string YearOfPublication { get; set; }
- public string ApplicationNumber { get; set; }
- public long[] InventorIds { get; set; }
- public override string ToString()
- {
- return string.Format("{0}({1})", Title, YearOfPublication);
- }
- }
- public class Inventor
- {
- public long Id{ get; set; }
- public string Name { get; set; }
- public string City { get; set; }
- public string State { get; set; }
- public string Country { get; set; }
- public override string ToString()
- {
- return string.Format("{0},({1},{2})", Name, City, State);
- }
- }
- public static class PatentData
- {
- public static readonly Inventor[] Inventors = new Inventor[]
- {
- new Inventor()
- {
- Name = "胡韩三",City ="韩国",State="SE",Country="KOR", Id =1,
- },
- new Inventor()
- {
- Name = "Michael Jackson",City ="美国",State="indiana",Country="USA", Id =2,
- },
- new Inventor()
- {
- Name = "唐三三",City ="中国",State="CQ",Country="CHN", Id =3,
- },
- new Inventor()
- {
- Name = "John Michaelis",City ="芝加哥",State="IL",Country="USA", Id =4,
- },
- new Inventor()
- {
- Name = "Mary Phelps Jacob",City ="纽约",State="NY",Country="KOR", Id =5,
- }
- };
- public static readonly Patent[] Patents = new Patent[]
- {
- new Patent()
- {
- Title="留声机",YearOfPublication="1877",InventorIds = new long[]{1}
- },
- new Patent()
- {
- Title="活动电影放映机",YearOfPublication="1888",InventorIds = new long[]{1}
- },
- new Patent()
- {
- Title="电报机",YearOfPublication="1837",InventorIds = new long[]{4}
- },
- new Patent()
- {
- Title="飞机",YearOfPublication="1903",InventorIds = new long[]{2,3}
- },
- new Patent()
- {
- Title="蒸汽机车",YearOfPublication="1815",InventorIds = new long[]{5}
- },
- new Patent()
- {
- Title="露背胸罩",YearOfPublication="1914",InventorIds = new long[]{7}
- },
- };
- }
- class Program
- {
- static void Main(string[] args)
- {
- IEnumerable <Patent> patents = PatentData.Patents;
- Print(patents);
- Console.WriteLine();
- IEnumerable<Inventor> inventors = PatentData.Inventors;
- Print(inventors);
- Console.ReadKey();
- }
- private static void Print<T>(IEnumerable<T> items)
- {
- foreach (T item in items)
- {
- Console.WriteLine(item);
- }
- }
- }
14.4.1
Where()筛选
- IEnumerable <Patent> patents = PatentData.Patents;
- //where
- patents = patents.Where(p => p.YearOfPublication.StartsWith("18"));
- Print(patents);
14.4.2
Select()投射
- IEnumerable <Patent> patents = PatentData.Patents;
- IEnumerable<Patent> patentsOf1800 =
- patents.Where(p => p.YearOfPublication.StartsWith("18"));
- IEnumerable<string> items =
- patentsOf1800.Select(p => p.ToString());
- Print(items);
例子2
- string rootDirectory = @"E:\MI-O2O商城APP\psSystem";
- string serchPattern = "*.html";
- IEnumerable<string> fileList = Directory.GetFiles(rootDirectory, serchPattern);
- IEnumerable<FileInfo> files = fileList.Select(file => new FileInfo(file));
- Print(files);
fileList的类型是 IEnumerable<string> ,而使用 Select()投射功能可以将集合中的每一项转换成一个 System.IO.FileInfo 对象。
- string rootDirectory = @"E:\MI-O2O商城APP\psSystem";
- string serchPattern = "*.html";
- IEnumerable<string> fileList = Directory.GetFiles(rootDirectory, serchPattern);
- var items = fileList.Select(
- file =>
- {
- FileInfo fileinfo = new FileInfo(file);
- return new
- {
- FileName = fileinfo.Name,
- Size = fileinfo.Length
- };
- });
14.4.3
Count()计数
- IEnumerable<Patent> patents = PatentData.Patents ;
- Console.WriteLine("Patent Count:{0}",patents.Count());
- Console.WriteLine("Patent Count in 1800s:{0}",
- patents.Count(p=>p.YearOfPublication.StartsWith("18")));
ICollection<T>包含了 Count属性,所以如果支持ICollection<T>,调用Count()方法,会对集合转型,并直接调用Count。如果不支持, Enumerable.Count()就会枚举所有项,而不是调用内建Count机制。
如果计数的目的是看是否大于( if(patents.Count()>0){...}),那首选的做法应使用Any()操作符( if (patents.Any()){...})只尝试遍历集合中的一项,成功返回 true而不会遍历整个序列。
14.4.4
推迟执行
Console.WriteLine("1,在1900年代之前的专利:");是先于Lambda表达式执行的。
Lambda表达式在声明时不执行,除非被调用,否则其中代码不会执行。
- IEnumerable<Patent> patents = PatentData.Patents;
- bool result;
- patents = patents.Where(
- p =>
- {
- if (result = p.YearOfPublication.StartsWith("18"))
- {
- Console.WriteLine("\t" + p);
- }
- return result;
- });
- Console.WriteLine("1,在1900年代之前的专利:");
- //1.foreach会循环分解成一个MoveNext()调用,//触发每项Lambda表达式
- foreach (var patent in patents)
- {
- }
- Console.WriteLine();
- Console.WriteLine("2.第二个清单在1900年代之前的专利:");
- //2.Enumerable的Count()函数会再次触发每一项的Lambda表达式
- Console.WriteLine(" 这里有 {0} 个专利在1900年之前..",patents.Count());
- Console.WriteLine();
- Console.WriteLine("3.第三个清单在1900年代之前的专利:");
- //3.ToArray()(或ToList()、ToDictinary()或ToLiikup())会没想再次触发Lambda
- patents = patents.ToArray();
- Console.Write(" 这里有 ");
- Console.WriteLine("{0} 个专利在1900年之前..", patents.Count());
执行顺序:
为了避免反复性执行,一个查询后有必要把获取的数据缓存起来。谓词可以使用“ ToXXX”方法(比如 ToArray())将数据赋值给一个局部集合。返回结果时,查询会执行。但此后,对赋值的集合进行遍历,就不会再设计查询表达式了。
14.4.5
OrderBy()和ThenBy()
- OrderBy()返回的是是一个IOrderEnumerable<T>接口,而不是IEnumerable<T>。
- IOrderEnumerable<T>派生自 IEnumerable<T>。
- OrderBy().OrderBy() 重复调用就会撤销上一个OrderBy()的工作。
- 指定额外的排序条件,使用ThenBy()(ThenBy扩展自IOrderEnumerable<T>而不是IEnumerable<T>)。
14.4.6
Join()内部连接
- public class Department
- {
- public long Id { get; set; }
- public string Name { get; set; }
- public override string ToString()
- {
- return string.Format("{0}", Name);
- }
- }
- public class Employee
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public string Title { get; set; }
- public int DepartmentId { get; set; }
- public override string ToString()
- {
- return string.Format("{0}({1})", Name, Title);
- }
- }
- public static class CorporateData
- {
- public static readonly Department[] Departments = new Department[]
- {
- new Department() {Name = "Corporate", Id = 0},
- new Department() {Name = "Finance", Id = 1},
- new Department() {Name = "Engineering", Id = 2},
- new Department() {Name = "Information Technology", Id = 3},
- new Department() {Name = "Research", Id = 4},
- new Department() {Name = "Marketing", Id = 5},
- };
- public static readonly Employee[] Employee = new Employee[]
- {
- new Employee() {Name = "Mark Michaelis", Title = "Chief Computer Nerd", DepartmentId = 0},
- new Employee() {Name = "Michael Stokesbary", Title = "Senior Computer Wizard", DepartmentId = 2},
- new Employee() {Name = "Brian Jones", Title = "Enterprise Integration Guru", DepartmentId = 2},
- new Employee() {Name = "Jewel Floch", Title = "Bookkeeper Extraordinaire", DepartmentId = 1},
- new Employee() {Name = "Robert Stocksbary", Title = "Expert Mainframe Engineer", DepartmentId = 3},
- new Employee() {Name = "paul R.Bramsman", Title = "Programmer Extraodinaire", DepartmentId = 2},
- new Employee() {Name = "Thomas Heavey", Title = "Software Architect", DepartmentId = 2},
- new Employee() {Name = "John Michaelis", Title = "Inventor", DepartmentId = 4},
- };
- }
输出类...
- static void Main(string[] args)
- {
- Department[] departments = CorporateData.Departments;
- Employee[] employees = CorporateData.Employee;
- //写代码......
- Console.ReadKey();
- }
- private static void Print<T>(IEnumerable<T> items)
- {
- foreach (T item in items)
- {
- Console.WriteLine(item);
- }
- }
看看Join()定义的参数
- public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer,
- IEnumerable<TInner> inner, //指定连接的集合
- Func<TOuter, TKey> outerKeySelector, //指出外接键
- Func<TInner, TKey> innerKeySelector, //指定集合的键
- Func<TOuter, TInner, TResult> resultSelector); //输出结果
我们来输出
- var items = employees.Join(
- departments,//Enumerable<TInner> inner :指定连接的集合
- employee => employee.DepartmentId,//Func<TOuter, TKey> outerKeySelector:指出外接键
- department => department.Id,// Func<TInner, TKey> innerKeySelector:指定集合的键
- (employee, department) => new // Func<TOuter, TInner, TResult> resultSelector:输出结果
- {
- employee.Id,
- employee.Name,
- employee.Title,
- department = department
- }
- );
- Print(items);
14.4.7
GroupBy()分组
- Employee[] employees = CorporateData.Employee;
- IEnumerable<IGrouping<int, Employee>> groupedEmployees =
- employees.GroupBy(e => e.DepartmentId);
- foreach (IGrouping<int, Employee> employeeGroup in groupedEmployees)
- {
- Console.WriteLine();
- foreach (Employee employee in employeeGroup)
- {
- Console.WriteLine("\t" + employee);
- }
- Console.WriteLine("\tCount:" + employeeGroup.Count());
返回值为 IEnumerable<IGrouping<TKey, TSource>> ,根据指定的键选择器函数对序列中的元素进行分组。由于 IEnumerable<IGrouping<TKey, TSource>> 是从IEnumerable<T>派生的,所以可以用foreach枚举所有项。
14.4.8
GroupJoin()一对多关系
如果创建员工列表,Join()提供的代码就可提供正确的结果。但是想要表示部门,理想的是显示一个部门的所有员工集合,而不是部门——员工关系都创建一条匿名类型的记录。
- public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer,
- IEnumerable<TInner> inner, //指定连接的集合
- Func<TOuter, TKey> outerKeySelector, //指出外接键
- Func<TInner, TKey> innerKeySelector, //指定集合的键
- Func<TOuter, IEnumerable<TInner>, TResult> resultSelector); //输出结果
- Department[] departments = CorporateData.Departments;
- Employee[] employees = CorporateData.Employee;
- var items = departments.GroupJoin(
- employees,
- department => department.Id,
- employee => employee.DepartmentId,
- (department, departmentEmployees) => new
- {
- department.Id,
- department.Name,
- Employees = departmentEmployees
- });
- foreach (var item in items)
- {
- Console.WriteLine("{0}", item.Name);
- foreach (Employee employee in item.Employees)
- {
- Console.WriteLine("\t"+employee);
- }
- }
而这里的Lambda表达式类型是
Func< Department , IEnumerable < Employee >, TResult>
其中 TResult是所选的匿名类型。注意,使用第二个参数( IEnumerable < Employee > )将每个部门的员工集合投射到结果的部门匿名类型中。
注意
SQL中没有与GroupJoin()等价的东西,这是由于SQL返回的数据基于记录,而不分层次结构。
14.4.9
SelectMany()
- var worldCup2014Finalists = new[]
- {
- new
- {
- TeamName="France",
- Players =new string[]
- {
- "本泽马","里贝里","格列兹曼","吉鲁","雷米",
- "卡巴耶","波巴","马图伊迪","瓦拉内","萨科",
- "德比希","曼加拉","穆萨-西索科","萨尼亚","埃弗拉",
- "洛里","鲁菲尔","朗德罗","马武巴","马图伊迪"
- }
- },
- new
- {
- TeamName="Italy",
- Players =new string[]
- {
- "布冯","德希利奥","基耶利尼","达米安","蒂亚戈-莫塔",
- "坎德雷瓦","阿巴特","马尔基西奥","巴洛特利","卡萨诺",
- "切尔奇","巴尔扎利","西里古","佩林","阿奎拉尼",
- "德罗西","伊莫比莱","帕罗洛","博努奇","帕莱塔"
- }
- }
- };
IEnumerable<string> players = worldCup2014Finalists.SelectMany(t => t.Players);
Print(players);
输出所有
Select()和 SelectMany()的区别:
Select()返回两个球员数组,每个杜对应原始集合中的球员数组。可以一边投射以便转换,但项的数量不会变化。例如: worldCup2014Finalists.Select(team => team.Players) 返回的是 IEnumerable <string[]>
SelectMany()会遍历由Lambda表达式标识的每一项,并将每项都放在一个新集合中。
14.4.10
更多标准查询操作符
- static void Main(string[] args)
- {
- IEnumerable<object> stuff = new object[]
- {
- new object(),1,3,5,7,9,"\"thing\"",Guid.NewGuid()
- };
- Print("Stuff:{0}", stuff);
- IEnumerable<int> even = new int[] {0, 2, 4, 6, 8};
- Print("【even】:{0}", even);
- IEnumerable<int> odd = stuff.OfType<int>();//筛选指定类型<T>的元素
- Print("【odd】:{0}", odd);
- IEnumerable<int> numbers = even.Union(odd);//并集
- Print("【numbers】odd和even的并集:{0}", numbers);
- Print("numbers并集even(Union):{0}", numbers.Union(even));
- Print("numbers连接odd(Concat):{0}", numbers.Concat(odd));
- Print("numbers交集even(Intersection):{0}", numbers.Intersect(even));
- Print("去掉重复(Distinct):{0}", numbers.Concat(odd).Distinct());
- if (!numbers.SequenceEqual(numbers.Concat(odd).Distinct()))
- {
- throw new Exception("Unexpectedly unequal");
- }
- else
- {
- Console.WriteLine(@"Collection ""SequenceEquals"""+
- "collection.Concat(odd).Distinct())");
- Print("反转(Reverse):{0}", numbers.Reverse());
- Print("平均值(Average):{0}", numbers.Average());
- Print("和(Sum):{0}", numbers.Sum());
- Print("最大值(Max):{0}", numbers.Max());
- Print("最小值(Min):{0}", numbers.Min());
- }
- Console.ReadKey();
- }
- private static void Print<T>(string format,IEnumerable<T> items)
- {
- StringBuilder text = new StringBuilder();
- foreach (T item in items.Take(items.Count()-1))
- {
- text.Append(item + ",");
- }
- text.Append(items.Last());
- Console.WriteLine(format,text);
- }
- private static void Print<T>(string format, T item)
- {
- Console.WriteLine(format, item);
- }