《C#本质论》读书笔记(14)支持标准查询操作符的集合接口


 


14.2.集合初始化器

使用集合初始化器,程序员可以采用和数组相似的方式,在集合的实例化期间用一套初始的成员来构造这个集合。
如果没有集合初始化器,就只有在集合实例化后才能显示添加到集合中——例如使用 System.Collections.Generic.ICollection<T>Add( )方法。

  1. static void Main(string[] args)    
  2. {    
  3.     List<string> sevenWorldBlunders;    
  4.     sevenWorldBlunders = new List<string>()    
  5.     {    
  6.         "Wealth without work",    
  7.         "Pleasure without conscience",    
  8.        "Knowledge without character",    
  9.        "Commerce without morality",    
  10.        "Science without humanity",    
  11.        "Worship without sacrifice",    
  12.        "Politics without principle"    
  13.     };    
  14.     Print(sevenWorldBlunders);    
  15.     Console.Read();    
  16. }    
  17. private static void Print<T>(IEnumerable<T> items)    
  18. {    
  19.     foreach (T item in items)    
  20.     {    
  21.         Console.WriteLine(item);    
  22.     }    
  23. }    


 

集合初始化器,基本条件


       应该实现 System.Collections.Generic.ICollection<T> 接口,就可以保证集合支持一个Add()方法,这个需求还有一个较为宽松的版本,只要求实现 IEnumerable<T>的类型上存在一个或多个Add()方法。
之所以允许不支持 ICollections<T> 的集合使用初始化器,原因有两个。

1.大多数集合(实现了  IEnumerable<T> 的类型)都没有实现 ICollections<T> ,这使得集合初始化器用处不大。
2.由于要匹配方法名,签名要和初始化兼容,所以可以在集合中初始化更加多样化的数据项。
例如,new DataStore(){a,{b,c}} —— 只要一个Add()方法的签名兼容于a,另一个Add()方法兼容b,c。

匿名类型集合初始化器


匿名类型不能使用集合初始化器。因为集合初始化器要求执行一次构造器的调用,但是你根本没有办法命名这个构造器。

解决方法1

是定义一个下面的方法:

  1. static List<T> CreateList<T>(T t)  
  2. {  
  3.     return new List<T>();  
  4. }  


解决方法2

使用数组初始化器。由于不能在构造器中指定数据类型,所以允许使用 new[ ] ,从而为匿名数组初始化器使用数组初始化语法。

  1. static void Main(string[] args)    
  2.  {    
  3.      var worldCup2006Finalists = new[]{    
  4.          new{    
  5.              TeamName = "France",    
  6.              Players = new string[]{    
  7.                  "Fabien Barthez","Gregory Coupet",    
  8.                  "Mickael Landreau","Eric Abidal"    
  9.              }    
  10.          },    
  11.          new{    
  12.              TeamName = "Italy",    
  13.              Players = new string[]{    
  14.                  "Gianlugi Buffon","Angelo Peruzzi",    
  15.                  "Marco Amelia","Cristian Zaccardo"    
  16.              }    
  17.          },     
  18.      };    
  19.      Print(worldCup2006Finalists);    
  20.      Console.ReadKey();    
  21.  }    
  22.     
  23.  private static void Print<T>(IEnumerable<T> items)    
  24.  {    
  25.      foreach (T item in items)    
  26.      {    
  27.          Console.WriteLine(item);    
  28.      }    
  29.  }    

 
 
 

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接口的设计允许使用迭代器模式遍历集合。
 

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>



前面代码忽略了CIL代码两个重要实现细节:交错和错误处理

   

1

状态共享


       假如同时两个循环交错遍历同一个集合(一个foreach嵌套另一个foreach),集合必须确保当前的MoveNext()能正确定位到下一个元素,问题是交错循环可能互相干扰(多线程执行循环会发生同样问题)。
        因为解决这个问题,集合类不直接支持 IEnumerator IEnumerator<T>接口。所以,用另一个接口,IEnumerable<T>,它唯一的方法就是GetEnumerator()。枚举数(enumerator)相当于一个“游标”或者“书签”。可以有多个书签,移动每个书签都可以独立于其他书签来遍历集合。

  1. Stack<int> stack = new Stack<int>();  
  2. Stack<int>.Enumerator stackEnumerator = stack.GetEnumerator();  
  3. int number;  
  4. while (stackEnumerator.MoveNext())  
  5. {  
  6.     number = stackEnumerator.Current;  
  7.     Console.WriteLine(number);  
  8. }  

   

2

清理状态


       由于实现 IEnumerator<T> 接口维持状态,退出循环后,有时需要对状态进行清理。为此,IEnumerator<T> 接口继承IDisposable。假如实现 IDisposable,就会调用Dispose()方法。就能在foreach循环退出之后调用Dispose()。与最终的CIL代码等价。

  1. System.Collections.Generic.Stack<int> stack = new System.Collections.Generic.Stack<int>();  
  2. Stack<int>.Enumerator stackEnumerator = stack.GetEnumerator();  
  3. IDisposable disposable;  
  4. try  
  5. {  
  6.     int number;  
  7.     while (stackEnumerator.MoveNext())  
  8.     {  
  9.         number = stackEnumerator.Current;  
  10.         Console.WriteLine(number);  
  11.     }  
  12. }  
  13. finally  
  14. {  
  15.     //显式类型转换用于IEnumerator < T >  
  16.     disposable = (IDisposable)stackEnumerator;  
  17.     disposable.Dispose();  
  18.     //IEnumerator将使用操作符,除非IDisposable支持在编译时是已知的  
  19.     disposable = (stackEnumerator as IDisposable);  
  20.     if (disposable != null)  
  21.     {  
  22.         disposable.Dispose();  
  23.     }  
  24. }  

由于IEnumerator<T>支持IDisposable接口,可以使用using关键字简化

  1. System.Collections.Generic.Stack<int> stack = new System.Collections.Generic.Stack<int>();    
  2. int number;    
  3. using (Stack<int>.Enumerator stackEnumerator = stack.GetEnumerator())    
  4. {    
  5.     while (stackEnumerator.MoveNext())    
  6.     {    
  7.         number = stackEnumerator.Current;    
  8.         Console.WriteLine(number);    
  9.     }    
  10. }    

14.4.标准查询操作符 

后面的使用标准查询操作符示例类:

Patent类和 Inventor
  1. public class Patent  
  2. {  
  3.     public string Title { getset; }  
  4.   
  5.     public string YearOfPublication { getset; }  
  6.   
  7.     public string ApplicationNumber { getset; }  
  8.   
  9.     public long[] InventorIds { getset; }  
  10.   
  11.     public override string ToString()  
  12.     {  
  13.         return string.Format("{0}({1})", Title, YearOfPublication);  
  14.     }  
  15. }  
  16.   

  17. public class Inventor  
  18. {  
  19.     public long Id{ getset; }  
  20.     public string Name { getset; }  
  21.   
  22.     public string City { getset; }  
  23.     public string State { getset; }  
  24.     public string Country { getset; }  
  25.     public override string ToString()  
  26.     {  
  27.         return string.Format("{0},({1},{2})", Name, City, State);  
  28.     }  
  29. }  


PatentData
  1. public static class PatentData  
  2.     {  
  3.         public static readonly Inventor[] Inventors = new Inventor[]  
  4.         {  
  5.             new Inventor()  
  6.             {  
  7.                Name = "胡韩三",City ="韩国",State="SE",Country="KOR", Id =1,  
  8.             },  
  9.             new Inventor()  
  10.             {  
  11.                Name = "Michael Jackson",City ="美国",State="indiana",Country="USA", Id =2,  
  12.             },  
  13.             new Inventor()  
  14.             {  
  15.                Name = "唐三三",City ="中国",State="CQ",Country="CHN", Id =3,  
  16.             },  
  17.             new Inventor()  
  18.             {  
  19.                Name = "John Michaelis",City ="芝加哥",State="IL",Country="USA", Id =4,  
  20.             },  
  21.             new Inventor()  
  22.             {  
  23.                Name = "Mary Phelps Jacob",City ="纽约",State="NY",Country="KOR", Id =5,  
  24.             }  
  25.         };  
  26.   
  27.         public static readonly Patent[] Patents = new Patent[]  
  28.         {  
  29.             new Patent()  
  30.             {  
  31.                 Title="留声机",YearOfPublication="1877",InventorIds = new long[]{1}  
  32.             },  
  33.             new Patent()  
  34.             {  
  35.                 Title="活动电影放映机",YearOfPublication="1888",InventorIds = new long[]{1}  
  36.             },  
  37.             new Patent()  
  38.             {  
  39.                 Title="电报机",YearOfPublication="1837",InventorIds = new long[]{4}  
  40.             },  
  41.             new Patent()  
  42.             {  
  43.                 Title="飞机",YearOfPublication="1903",InventorIds = new long[]{2,3}  
  44.             },  
  45.             new Patent()  
  46.             {  
  47.                 Title="蒸汽机车",YearOfPublication="1815",InventorIds = new long[]{5}  
  48.             },  
  49.                         new Patent()  
  50.             {  
  51.                 Title="露背胸罩",YearOfPublication="1914",InventorIds = new long[]{7}  
  52.             },  
  53.         };  
  54.     }  


输出Main()
  1. class Program  
  2. {  
  3.     static void Main(string[] args)  
  4.     {  
  5.        IEnumerable <Patent> patents = PatentData.Patents;  
  6.         Print(patents);  
  7.         Console.WriteLine();  
  8.         IEnumerable<Inventor> inventors = PatentData.Inventors;  
  9.         Print(inventors);  
  10.         Console.ReadKey();  
  11.     }  
  12.   
  13.     private static void Print<T>(IEnumerable<T> items)  
  14.     {  
  15.         foreach (T item in items)  
  16.         {  
  17.             Console.WriteLine(item);  
  18.         }  
  19.     }  
  20. }  

 

14.4.1

Where()筛选


  1. IEnumerable <Patent> patents = PatentData.Patents;  
  2.   //where  
  3.   patents = patents.Where(p => p.YearOfPublication.StartsWith("18"));  
  4.   Print(patents);  

14.4.2

Select()投射


这里的Select()调用,并未造成输出变化。这里只是巧合
  1. IEnumerable <Patent> patents = PatentData.Patents;  
  2.  IEnumerable<Patent> patentsOf1800 =   
  3.      patents.Where(p => p.YearOfPublication.StartsWith("18"));  
  4.  IEnumerable<string> items =  
  5.      patentsOf1800.Select(p => p.ToString());  
  6.  Print(items);  


例子2
  1. string rootDirectory = @"E:\MI-O2O商城APP\psSystem";  
  2.             string serchPattern = "*.html";  
  3.             IEnumerable<string> fileList = Directory.GetFiles(rootDirectory, serchPattern);  
  4.             IEnumerable<FileInfo> files = fileList.Select(file => new FileInfo(file));  
  5.             Print(files);  

 fileList的类型是  IEnumerable<string>  ,而使用Select()投射功能可以将集合中的每一项转换成一个  System.IO.FileInfo 对象。

例子3:匿名方法
  1. string rootDirectory = @"E:\MI-O2O商城APP\psSystem";  
  2. string serchPattern = "*.html";  
  3. IEnumerable<string> fileList = Directory.GetFiles(rootDirectory, serchPattern);  
  4. var items = fileList.Select(  
  5.     file =>  
  6.     {  
  7.         FileInfo fileinfo = new FileInfo(file);  
  8.         return new  
  9.         {  
  10.             FileName = fileinfo.Name,  
  11.             Size = fileinfo.Length  
  12.         };  
  13.     }); 

  14.  
Where():在“垂直”方向筛选集合(减少集合中项的数量)
Select():在“水平”方向减少集合的规模(减少列的数量)

14.4.3

Count()计数

  1. IEnumerable<Patent> patents = PatentData.Patents  ;  
  2. Console.WriteLine("Patent Count:{0}",patents.Count());
  3. Console.WriteLine("Patent Count in 1800s:{0}",  
  4.     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表达式在声明时不执行,除非被调用,否则其中代码不会执行。
  1. IEnumerable<Patent> patents = PatentData.Patents;  
  2. bool result;  
  3. patents = patents.Where(  
  4.     p =>  
  5.     {  
  6.         if (result = p.YearOfPublication.StartsWith("18"))  
  7.         {  
  8.             Console.WriteLine("\t" + p);  
  9.         }  
  10.         return result;  
  11.     });  
  12. Console.WriteLine("1,在1900年代之前的专利:");  
  13. //1.foreach会循环分解成一个MoveNext()调用,
    //触发每项Lambda表达式
  14. foreach (var patent in patents)  
  15. {        
  16. }  
  17. Console.WriteLine();  
  18. Console.WriteLine("2.第二个清单在1900年代之前的专利:");  
  19. //2.Enumerable的Count()函数会再次触发每一项的Lambda表达式
  20. Console.WriteLine("    这里有 {0} 个专利在1900年之前..",patents.Count());  
  21. Console.WriteLine();  
  22. Console.WriteLine("3.第三个清单在1900年代之前的专利:");  
  23. //3.ToArray()(或ToList()、ToDictinary()或ToLiikup())会没想再次触发Lambda
  24. patents = patents.ToArray();  
  25. Console.Write("    这里有 ");  
  26. Console.WriteLine("{0} 个专利在1900年之前..", patents.Count());  


执行顺序
 为了避免反复性执行,一个查询后有必要把获取的数据缓存起来。谓词可以使用“ToXXX”方法(比如ToArray())将数据赋值给一个局部集合。返回结果时,查询会执行。但此后,对赋值的集合进行遍历,就不会再设计查询表达式了。

14.4.5

OrderBy()和ThenBy()


  1. OrderBy()返回的是是一个IOrderEnumerable<T>接口,而不是IEnumerable<T>。  
  2. IOrderEnumerable<T>派生自 IEnumerable<T>。  
  3. OrderBy().OrderBy() 重复调用就会撤销上一个OrderBy()的工作。  
  4. 指定额外的排序条件,使用ThenBy()(ThenBy扩展自IOrderEnumerable<T>而不是IEnumerable<T>)。  

14.4.6

Join()内部连接


部门和员工数据

  1. public class Department  
  2. {  
  3.     public long Id { getset; }  
  4.     public string Name { getset; }  
  5.     public override string ToString()  
  6.     {  
  7.         return string.Format("{0}", Name);  
  8.     }  
  9. }  
  10.   
  11. public class Employee  
  12. {  
  13.     public int Id { getset; }  
  14.     public string Name { getset; }  
  15.     public string Title { getset; }  
  16.     public int DepartmentId { getset; }  
  17.     public override string ToString()  
  18.     {  
  19.         return string.Format("{0}({1})", Name, Title);  
  20.     }  
  21. }  
  22.   
  23. public static class CorporateData  
  24. {  
  25.     public static readonly Department[] Departments = new Department[]  
  26.     {  
  27.         new Department() {Name = "Corporate", Id = 0},  
  28.         new Department() {Name = "Finance", Id = 1},  
  29.         new Department() {Name = "Engineering", Id = 2},  
  30.         new Department() {Name = "Information Technology", Id = 3},  
  31.         new Department() {Name = "Research", Id = 4},  
  32.         new Department() {Name = "Marketing", Id = 5},  
  33.     };  
  34.   
  35.     public static readonly Employee[] Employee = new Employee[]  
  36.     {  
  37.         new Employee() {Name = "Mark Michaelis", Title = "Chief Computer Nerd", DepartmentId = 0},  
  38.         new Employee() {Name = "Michael Stokesbary", Title = "Senior Computer Wizard", DepartmentId = 2},  
  39.         new Employee() {Name = "Brian Jones", Title = "Enterprise Integration Guru", DepartmentId = 2},  
  40.         new Employee() {Name = "Jewel Floch", Title = "Bookkeeper Extraordinaire", DepartmentId = 1},  
  41.         new Employee() {Name = "Robert Stocksbary", Title = "Expert Mainframe Engineer", DepartmentId = 3},  
  42.         new Employee() {Name = "paul R.Bramsman", Title = "Programmer Extraodinaire", DepartmentId = 2},  
  43.         new Employee() {Name = "Thomas Heavey", Title = "Software Architect", DepartmentId = 2},  
  44.         new Employee() {Name = "John Michaelis", Title = "Inventor", DepartmentId = 4},  
  45.     };  
  46. }  


输出类...

  1. static void Main(string[] args)  
  2.  {  

  3.      Department[] departments = CorporateData.Departments;  
  4.      Employee[] employees = CorporateData.Employee;  
  5.   
  6.     //写代码......
  7.        
  8.      Console.ReadKey();  
  9.  }  
  10.   
  11.  private static void Print<T>(IEnumerable<T> items)  
  12.  {  
  13.      foreach (T item in items)  
  14.      {  
  15.          Console.WriteLine(item);  
  16.      }  
  17.  }  


看看Join()定义的参数

  1. public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, 
  2. IEnumerable<TInner> inner,                             //指定连接的集合  
  3. Func<TOuter, TKey> outerKeySelector,            //指出外接键  
  4. Func<TInner, TKey> innerKeySelector,            //指定集合的键  
  5. Func<TOuter, TInner, TResult> resultSelector);  //输出结果


我们来输出

  1. var items = employees.Join(  
  2.     departments,//Enumerable<TInner> inner    :指定连接的集合  
  3.     employee => employee.DepartmentId,//Func<TOuter, TKey> outerKeySelector:指出外接键  
  4.     department => department.Id,// Func<TInner, TKey> innerKeySelector:指定集合的键  
  5.     (employee, department) => new  // Func<TOuter, TInner, TResult> resultSelector:输出结果  
  6.     {  
  7.         employee.Id,  
  8.         employee.Name,  
  9.         employee.Title,  
  10.         department = department  
  11.     }  
  12.     );  
  13.   
  14. Print(items);  

 

14.4.7

GroupBy()分组


  1. Employee[] employees = CorporateData.Employee;  
  2.   
  3. IEnumerable<IGrouping<int, Employee>> groupedEmployees =  
  4.     employees.GroupBy(e => e.DepartmentId);  
  5.   
  6. foreach (IGrouping<int, Employee> employeeGroup in groupedEmployees)  
  7. {  
  8.     Console.WriteLine();  
  9.     foreach (Employee employee in employeeGroup)  
  10.     {  
  11.         Console.WriteLine("\t" + employee);  
  12.     }  
  13.     Console.WriteLine("\tCount:" + employeeGroup.Count());  

 返回值为IEnumerable<IGrouping<TKey, TSource>>,根据指定的键选择器函数对序列中的元素进行分组。由于IEnumerable<IGrouping<TKey, TSource>>是从IEnumerable<T>派生的,所以可以用foreach枚举所有项。

14.4.8

GroupJoin()一对多关系


如果创建员工列表,Join()提供的代码就可提供正确的结果。但是想要表示部门,理想的是显示一个部门的所有员工集合,而不是部门——员工关系都创建一条匿名类型的记录。


先看看定义
  1. public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, 
  2. IEnumerable<TInner> inner,                 //指定连接的集合
  3. Func<TOuter, TKey> outerKeySelector,  //指出外接键  
  4. Func<TInner, TKey> innerKeySelector, //指定集合的键  
  5. Func<TOuter, IEnumerable<TInner>, TResult> resultSelector);  //输出结果

  1. Department[] departments = CorporateData.Departments;  
  2. Employee[] employees = CorporateData.Employee;  
  3.   
  4. var items = departments.GroupJoin(  
  5.     employees,  
  6.     department => department.Id,  
  7.     employee => employee.DepartmentId,  
  8.     (department, departmentEmployees) => new  
  9.     {  
  10.         department.Id,  
  11.         department.Name,  
  12.         Employees = departmentEmployees  
  13.     });  
  14.   
  15. foreach (var item in items)  
  16. {  
  17.     Console.WriteLine("{0}", item.Name);  
  18.     foreach (Employee employee in item.Employees)  
  19.     {  
  20.         Console.WriteLine("\t"+employee);  
  21.     }  
  22. }  


定义里 Func<TOuter, IEnumerable<TInner>, TResult>   //输出结果
而这里的Lambda表达式类型是
Func<DepartmentIEnumerable<Employee>, TResult>
其中TResult是所选的匿名类型。注意,使用第二个参数(IEnumerable<Employee>)将每个部门的员工集合投射到结果的部门匿名类型中。
注意

SQL中没有与GroupJoin()等价的东西,这是由于SQL返回的数据基于记录,而不分层次结构。


14.4.9

SelectMany()


处理集合构成的集合。
  1. var worldCup2014Finalists = new[]  
  2. {  
  3.     new  
  4.     {  
  5.         TeamName="France",  
  6.         Players =new string[]  
  7.         {  
  8.             "本泽马","里贝里","格列兹曼","吉鲁","雷米",  
  9.             "卡巴耶","波巴","马图伊迪","瓦拉内","萨科",  
  10.             "德比希","曼加拉","穆萨-西索科","萨尼亚","埃弗拉",  
  11.             "洛里","鲁菲尔","朗德罗","马武巴","马图伊迪"  
  12.         }  
  13.     },  
  14.     new  
  15.     {  
  16.         TeamName="Italy",  
  17.         Players =new string[]  
  18.         {  
  19.             "布冯","德希利奥","基耶利尼","达米安","蒂亚戈-莫塔",  
  20.             "坎德雷瓦","阿巴特","马尔基西奥","巴洛特利","卡萨诺",  
  21.             "切尔奇","巴尔扎利","西里古","佩林","阿奎拉尼",  
  22.             "德罗西","伊莫比莱","帕罗洛","博努奇","帕莱塔"  
  23.         }  
  24.     }  
  25. };  

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

更多标准查询操作符



  1. static void Main(string[] args)  
  2. {  
  3.     IEnumerable<object> stuff = new object[]  
  4.     {  
  5.         new object(),1,3,5,7,9,"\"thing\"",Guid.NewGuid()  
  6.     };  
  7.     Print("Stuff:{0}", stuff);  
  8.     IEnumerable<int> even = new int[] {0, 2, 4, 6, 8};  
  9.     Print("【even】:{0}", even);  
  10.   
  11.     IEnumerable<int> odd = stuff.OfType<int>();//筛选指定类型<T>的元素  
  12.     Print("【odd】:{0}", odd);  
  13.   
  14.     IEnumerable<int> numbers = even.Union(odd);//并集  
  15.     Print("【numbers】odd和even的并集:{0}", numbers);  
  16.   
  17.     Print("numbers并集even(Union):{0}", numbers.Union(even));  
  18.     Print("numbers连接odd(Concat):{0}", numbers.Concat(odd));  
  19.     Print("numbers交集even(Intersection):{0}", numbers.Intersect(even));  
  20.     Print("去掉重复(Distinct):{0}", numbers.Concat(odd).Distinct());  
  21.   
  22.     if (!numbers.SequenceEqual(numbers.Concat(odd).Distinct()))  
  23.     {  
  24.         throw new Exception("Unexpectedly unequal");  
  25.     }  
  26.     else  
  27.     {  
  28.         Console.WriteLine(@"Collection ""SequenceEquals"""+  
  29.             "collection.Concat(odd).Distinct())");  
  30.         Print("反转(Reverse):{0}", numbers.Reverse());  
  31.   
  32.         Print("平均值(Average):{0}", numbers.Average());  
  33.         Print("和(Sum):{0}", numbers.Sum());  
  34.         Print("最大值(Max):{0}", numbers.Max());  
  35.         Print("最小值(Min):{0}", numbers.Min());  
  36.     }  
  37.   
  38.     Console.ReadKey();  
  39. }  
  40. private static void Print<T>(string format,IEnumerable<T> items)  
  41. {  
  42.     StringBuilder text = new StringBuilder();  
  43.     foreach (T item in items.Take(items.Count()-1))  
  44.     {  
  45.         text.Append(item + ",");  
  46.     }  
  47.     text.Append(items.Last());  
  48.   
  49.     Console.WriteLine(format,text);  
  50. }  
  51.   
  52. private static void Print<T>(string format, T item)  
  53. {  
  54.     Console.WriteLine(format, item);  
  55. }  


 













posted @ 2016-11-10 21:54  【唐】三三  阅读(343)  评论(0编辑  收藏  举报