《LINQ技术详解C#》-4.延迟操作符(第2部分 LINQ到对象)

public static string[] Presidents { get; } =
        {
            "Adams", "Arthur", "Buchanan", "Bush", "Carter", "Cleveland",
            "Clinton", "Coolidge", "Eisenhower", "Fillmore", "Ford", "Garfield",
            "Grant", "Harding", "Harrison", "Hayes", "Hoover", "Jackson",
            "Jefferson", "Johnson", "Kennedy", "Lincoln", "Madison", "Mckinley",
            "Monroe", "Nixon", "Pierce", "polk", "Reagan", "Roosevelt", "Taft",
            "taylor", "Trumanyler", "Van Buren", "Washington", "Wilson"
        };
    /// <summary>
    /// 雇员
    /// </summary>
    public class Employee
    {
        public int id;
        public string firstName;
        public string lastName;

        public static ArrayList GetEmployeesArrayList()
        {
            ArrayList al = new ArrayList();

            al.Add(new Employee { id = 1, firstName="Joe",lastName = "Rattz"});
            al.Add(new Employee { id = 2, firstName="Willam",lastName = "Gates"});
            al.Add(new Employee { id = 3, firstName="Anders",lastName = "Hejlsberg"});
            al.Add(new Employee { id = 4, firstName="David",lastName = "Lightman"});
            al.Add(new Employee { id = 101, firstName="Kevin",lastName = "Flynn"});

            return (al);
        }

       // public static Employee[] GetEmployeesArray()img
        //{
          //  return (Employee[])GetEmployeesArrayList().ToArray();
        //}
        
        public static Employee[] GetEmployeesArray()
        {
            ArrayList objs = GetEmployeesArrayList();
            Employee[] em = new Employee[objs.Count];
            for (int i = 0; i < em.Length; i++)
            {
                em[i] = (Employee)objs[i];
            }
            return em;
        }
    }
 /// <summary>
    /// 特定雇员提供的股票期权奖励
    /// </summary>
    public class EmployeeOptionEntry
    {
        public int id;
        public long optionsCount;
        public DateTime dateAwarded;

        public static EmployeeOptionEntry[] GetEmployeeOptionEntries()
        {
            EmployeeOptionEntry[] empOptions = new EmployeeOptionEntry[]
            {
                new EmployeeOptionEntry
                {
                    id = 1,optionsCount = 2,dateAwarded = DateTime.Parse("1999/12/31")
                },  new EmployeeOptionEntry
                {
                    id = 2,optionsCount = 10000,dateAwarded = DateTime.Parse("1992/06/30")
                },  new EmployeeOptionEntry
                {
                    id = 2,optionsCount = 10000,dateAwarded = DateTime.Parse("1994/01/01")
                },  new EmployeeOptionEntry
                {
                    id = 3,optionsCount = 5000,dateAwarded = DateTime.Parse("1997/09/30")
                },  new EmployeeOptionEntry
                {
                    id =2,optionsCount = 10000,dateAwarded = DateTime.Parse("2003/04/01")
                },  new EmployeeOptionEntry
                {
                    id = 3,optionsCount = 7500,dateAwarded = DateTime.Parse("1998/09/30")
                },  new EmployeeOptionEntry
                {
                    id =3,optionsCount = 7500,dateAwarded = DateTime.Parse("1998/09/30")
                }, new EmployeeOptionEntry
                {
                    id =4,optionsCount = 1500,dateAwarded = DateTime.Parse("1997/12/31")
                }, new EmployeeOptionEntry
                {
                    id =101,optionsCount = 2,dateAwarded = DateTime.Parse("1998/12/31")
                }, 
            };
            return empOptions;
        }
    }

1.Where: 限定操作符

#region Where:限定操作符
            {
                //4.1 限定操作符 Where:  Func<TSource, bool> predicate)
                //说明:求Presidents数组,J开头的集合
                IEnumerable<string> president = Presidents.Where(s => s.StartsWith("J"));
            }

            {
                //4.2 限定操作符 Where:  Func<TSource, int, bool> predicate)
                //求Presidents数组中,索引为奇数的集合
                IEnumerable<string> president = Presidents.Where((p, i) => (i & 1) == 1);
                foreach (var item in president)
                {
                    Console.Write(item + ",");
                }
                //结果:Arthur,Bush,Cleveland,Coolidge,Fillmore,Garfield,Harding,Hayes,
                //Jackson,Johnson,Lincoln,Mckinley,Nixon,polk,Roosevelt,taylor,Van Buren, Wilson,
            }

2.Select: 投影操作符

1.Select

可以返回一个输出元素序列。输出序列元素的数据类型 可能 与输入序列元素的数据类型不通。
第一种原型:

            //Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
            {
                IEnumerable<int> nameLength = Presidents.Select(p => p.Length);
                 foreach (var item in nameLength)
                    Console.Write(item + ",");
                //结果:5,6,8,4,6,9,7,8,10,8,4,8,5,7,8,5,6,7,9,7,7,7,7,8,6,5,6,4,6,9,4,6,10,9,10,6,
            }
            {
                var nameObjs = Presidents.Select(p => new { p, p.Length });
                foreach (var item in nameObjs)
                    Console.Write(item + ",");
                //结果:{ p = Adams, Length = 5 },{ p = Arthur, Length = 6 },{ p = Buchanan, Length = 8 } ....
            }
            {
                var nameObjs = Presidents.Select(p => new { LastName = p, Length = p.Length });
                foreach (var item in nameObjs)
                    Console.WriteLine("{0} is {1} characters long.",item.LastName,item.Length);
                /*结果:Arthur is 6 characters long.
                        Buchanan is 8 characters long.
                        Bush is 4 characters long.
                        Carter is 6 characters long. ...   */
            }

第二种原型:

//Select<TSource, TResult>(this IEnumerable < TSource > source, Func<TSource, int, TResult> selector);
            {
                var nameObjs = Presidents.Select((p, i) => new {Index = i, LastName = p});
                foreach (var item in nameObjs)
                    Console.Write("{0}.{1} ",item.Index+1,item.LastName);
                /*结果:1.Adams 2.Arthur 3.Buchanan 4.Bush 5.Carter 6.Cleveland 7.Clinton 8.Coolidge...... */
            }

2.SelectMany

可以基于输入序列创建一个一到多的输出投影序列。

Select操作符可以为每个输入元素返回一个输出元素,而SelectMany可以为每个输入元素返回空或多个输出元素。

第一种原型

        //
        // 摘要:
        //     一个序列的每个元素投影 System.Collections.Generic.IEnumerable`1 并将合并为一个序列将结果序列。
        //
        // 参数:
        //   source:
        //     一个要投影的值序列。
        //
        //   selector:
        //     应用于每个元素的转换函数。
        //
        // 类型参数:
        //   TSource:
        //     中的元素的类型 source。
        //
        //   TResult:
        //     返回的序列的元素的类型 selector。
        //
        // 返回结果:
        //     System.Collections.Generic.IEnumerable`1 其元素是一种一对多转换函数对输入序列中的每个元素调用的结果。
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     source 或 selector 为 null。
        public static IEnumerable<TResult> SelectMany<TSource, TResult>(
		this IEnumerable<TSource> source, 
		Func<TSource, IEnumerable<TResult>> selector);

  SelectMany操作符的这个原型需要传递一个类型为TSource的输入源序列元素和一个选择器方法委托,并将返回一个对象,在枚举该对象时,将会枚举输入源序列,从输入序列分别将每个元素传递到选择器方法中。

  然后,选择器方法将返回一个对象,在枚举该对象时,将在一个中间输出序列中生成空或多个类型为TSource的元素。SelectMany 操作符最后将返回一个将中间输出序列连接在一起的输出序列,这些中间输出序列是在每次调用选择器方法时产生的。

示例4-7

          //第一种原型:SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
            {
                //返回一个字符类型的数组chars
                IEnumerable<char> chars = Presidents.SelectMany(p => p.ToArray());
               foreach (var item in chars)
                 Console.Write("{0} ", item);
               
               //结果:A d a m s A r t h u r B u c h a n a n B u s h C a r t e r C l e v e l a n d C ......
            }

示例4-8
将对Employee元素的数组调用SelectMany 操作符,对于该数组中的每个Employee元素,选择器方法委托将返回空或创建的多个匿名类元素,这些元素中包含来自该Employee对象的EmployeeOptionEntry元素数组中的idoptionsCount。请参见示例4-8中的代码,这些代码完成了,上述操作。

            {
                Employee[] employees = Employee.GetEmployeesArray();
                EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

                //Employee对象的EmployeeOptionEntry元素数组中的id和optionsCount
                var employeeOptions = employees.SelectMany(e => empOptions
                                                                .Where(eo => eo.id == e.id))
                                                                .Select(eo => new { id = eo.id, optionsCount = eo.optionsCount });

					foreach (var item in employeeOptions)
                    Console.WriteLine("{0} ", item);
					
                /*结果:
				{ id = 1, optionsCount = 2 }
				{ id = 2, optionsCount = 10000 }
				{ id = 2, optionsCount = 10000 } 
				{ id = 2, optionsCount = 10000 }
				{ id = 3, optionsCount = 5000 }
				{ id = 3, optionsCount = 7500 }
				{ id = 3, optionsCount = 7500 }
				{ id = 4, optionsCount = 1500 }
				{ id = 101, optionsCount = 2 }      */
            }

我觉得这样看的出来这里的SelectMany的作用,反过来写,可以看出差别

var employeesWhere = empOptions.SelectMany(e => employees.Where(eo => eo.id == e.id))
                                                .Select(eo => new { id = eo.id, firstName = eo.firstName, lastName = eo.lastName }); 
												
//结果:
                //{ id = 1, firstName = Joe, lastName = Rattz }
                //{ id = 2, firstName = Willam, lastName = Gates } 
                //{ id = 2, firstName = Willam, lastName = Gates }
                //{ id = 3, firstName = Anders, lastName = Hejlsberg }
                //{ id = 2, firstName = Willam, lastName = Gates }
                //{ id = 3, firstName = Anders, lastName = Hejlsberg }
                //{ id = 3, firstName = Anders, lastName = Hejlsberg }
                //{ id = 4, firstName = David, lastName = Lightman }
                //{ id = 101, firstName = Kevin, lastName = Flynn }

类似于 多个(From)语句
from c in customers						from t in customers
from o in c.Orders						.SelectMany(c => from o in c.Orders
select new							->		select new { 
{ c.Name, o.OrderID, o.OrderDate }				c.Name, o.OrderID, o.OrderDate
													})
											select t

第二种原型,多一个 int

        public static IEnumerable<TResult> SelectMany<TSource, TResult>(
			this IEnumerable<TSource> source, 
			Func<TSource, int, IEnumerable<TResult>> selector);

下面代码输出前5个字符串字符

            {
                //索引小于5,则输出字符串数组--------------(输出前5个字符串字符)
                IEnumerable<char> chars = Presidents.SelectMany((p, i) => i < 5 ? p.ToArray() : new char[] { });
                foreach (var item in chars)
                    Console.Write("{0} ", item);
               
               //结果:A d a m s A r t h u r B u c h a n a n B u s h C a r t e r
            }

第三种原型,相当于2个for循环

//
        // 摘要:
        //     一个序列的每个元素投影 System.Collections.Generic.IEnumerable`1, 将平展为一个序列,将结果序列,其中调用结果选择器函数对每个元素。
        //
        // 参数:
        //   source:
        //     一个要投影的值序列。
        //
        //   collectionSelector:
        //     要应用于输入序列中的每个元素转换函数。
        //
        //   resultSelector:
        //     要应用于中间序列的每个元素的转换函数。
        //
        // 类型参数:
        //   TSource:
        //     中的元素的类型 source。
        //
        //   TCollection:
        //     中间元素的类型由收集 collectionSelector。
        //
        //   TResult:
        //     结果序列的元素的类型。
        //
        // 返回结果:
        //     System.Collections.Generic.IEnumerable`1 其元素是调用一种一对多转换函数的结果 collectionSelector
        //     的每个元素 source 然后将每个这些序列元素和其相应的源元素映射到一个结果元素。
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     source、collectionSelector 或 resultSelector 为 null。
        public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(
				this IEnumerable<TSource> source, 
				Func<TSource, IEnumerable<TCollection>> collectionSelector, 
				Func<TSource, TCollection, TResult> resultSelector);

第四种原型

跟第2种与第1种的区别一样,有个int。

3.分区操作符

(1)Take

(2)TakeWhile

可以在一些条件为true时,从输入序列中提取元素。

//TakeWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
            {
            
            
                //将会提取元素,直到遇到第一个长度为10或者10字以上的字符串
                IEnumerable<string> items = Presidents.TakeWhile(s => s.Length < 10);
                foreach (var item in items)
                    Console.Write("{0} ", item);
                
                //结果:Adams Arthur Buchanan Bush Carter Cleveland Clinton Coolidge
            }

            {
                //将会提取元素,直到遇到第一个长度大于9个字符串并且大于第6个元素
                IEnumerable<string> items = Presidents.TakeWhile((s, i) => s.Length < 10 && i < 5);
                foreach (var item in items)
                    Console.Write("{0} ", item);
                
                //结果:Adams Arthur Buchanan Bush Carter
            }

(3)Skip

操作符跳过序列中从改序列的第一个元素开始指定数量的元素,并取出其余元素。

//跳过"Adams"
 IEnumerable<string> items = Presidents.Skip(1);

(4)SkipWhile

在指定条件为true时,跳过一些元素,然后将剩余元素提取到输出序列。

//SkipWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
            {
                //以“A”开头的元素全部跳过
                IEnumerable<string> items = Presidents.SkipWhile(s => s.StartsWith("A"));
                foreach (var item in items)
                    Console.Write("{0} ", item);
                //结果:Buchanan Bush Carter Cleveland Clinton Coolidge Eisenhower Fillmore Ford ....
            }
            
//SkipWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate);
            {
                //直到长度不大于4个字符,或者到达第10个元素为止
                //跳过“Bush”之前的元素,因为这个元素长度不大于4,尽管这个元素的索引只是3,还不到10
                IEnumerable<string> items = Presidents.SkipWhile((s, i) => s.Length > 4 && i < 10);
                foreach (var item in items)
                    Console.Write("{0} ", item);
                //结果:Buchanan Bush Carter Cleveland Clinton Coolidge Eisenhower Fillmore Ford ....
            }

4.串联操作符

1.Concat

将两个输入序列串联在一起,并生成单个输出序列。

// IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second);
//两个串联
			{
                //提取前5个元素,并串联Presidents除前5元素之外的元素,生成一个与Presidents内容一样的元素
                IEnumerable<string> items = Presidents.Take(5).Concat(Presidents.Skip(5));
                foreach (var item in items)
                    Console.Write("{0} ", item);
            }
//多个串联
            {
                //这个示例与上一个示例相似,区别在于对序列数组调用SelectMany
                //区别:Concat只允许两个序列串联,这个技术允许一个序列数组(多个)串联
                IEnumerable<string> items = new[] { Presidents.Take(5), Presidents.Take(5), Presidents.Skip(5)}
                                                   .SelectMany(s => s);
                foreach (var item in items)
                    Console.Write("{0} ", item);
            }

5.排序操作符

  OrderBy和OrderByDescending返回IOrderdEnumerable<T>序列。意味着,不能从OrderBy或OrderByDescending操作符返回的序列继续传递到下一个OrderBy或OrderByDescending操作符调用中。如果要进行更多排序,必须调用ThenByThenByDescending

1.OrderBy (升序)##

2.OrderByDescending (降序)##

3.ThenBy

4.ThenByDescending

5.Reverse (反转)

                //顺序相反的序列
                IEnumerable<string> items = Presidents.Reverse();
                                foreach (var item in items)
                                    Console.Write("{0} ", item);
                //结果:Wilson Washington Van Buren Trumanyler taylor Taft Roosevelt Reagan polk Pierce ...

6.连接操作符

1.Join

对两个序列执行内部连接,该连接基于从序列中的每个元素提取的键值。
Join

			/* 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); 
             * 
             *  枚举该对象时,首先枚举类型为 TInner 的元素的 inner 序列,为每个元素调用一次 innerKeySelector 方法,并在一个哈希表中存储该元素,通过键值对其引用。
             *  在返回的对象枚举每个 outer 序列元素时,将会调用 outerKeySelector 方法获取其键值,并使用该键值从哈希表中提取 inner 序列元素。
             *  对于每个 outer 序列元素和 inner 序列的元素对,返回的对象将调用 resultSelector 方法以传递 outer 元素和匹配的 inner 元素。
             *  resultSelector 方法将返回一个实例化的类型为 TResult 的对象,返回的对象将位于类型为 TResult 的输出序列中。
             */
            {
                Employee[] employees = Employee.GetEmployeesArray();
                EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

                var employeeOptions = employees
                    .Join(empOptions,       // inner序列
                        e => e.id,          // outerKeySelector
                        o => o.id,          // innerKeySelector
                        (e, o) => new       // resultSelector
                        {
                            id = e.id,
                            name = string.Format("{0} {1} ", e.firstName, e.lastName),
                            option = o.optionsCount
                        }
                    );
                foreach (var item in employeeOptions)
                    Console.Write("{0} ", item);
                /*结果:
                 { id = 1, name = Joe Rattz , option = 2 } { id = 2, name = Willam Gates , option = 10000 } 
                 { id = 2, name = Willam Gates , option = 10000 } { id = 2, name = Wil ....
                 */
            }

2.GroupJoin

可以根据从序列中的每个元素提取的键值对两个序列执行分组连接功能。

            /*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)
             * 
             * 相对于Join,GroupJoin里的 resultSelector 方法传递的第二个输入参数为一个序列。
             */

            {
                Employee[] employees = Employee.GetEmployeesArray();
                EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

                var employeeOptions = employees
                    .GroupJoin(empOptions,       // inner序列
                        e => e.id,          // outerKeySelector
                        o => o.id,          // innerKeySelector
                        (e, os) => new       // resultSelector
                        {
                            id = e.id,
                            name = string.Format("{0} {1} ", e.firstName, e.lastName),
                            option = os.Sum(o=>o.optionsCount)
                        }
                    );
                foreach (var item in employeeOptions)
                    Console.Write("{0} ", item);
                /*结果:
                 { id = 1, name = Joe Rattz , option = 2 } { id = 2, name = Willam Gates , option = 30000 } 
                 { id = 3, name = Anders Hejlsberg , option = 20000 } { id = 4, name = David Lightman , option = 1500 } 
                 { id = 101, name = Kevin Flynn , option = 2 }
                 */
            }

7.分组操作符

1.GroupBy

可以通过公共键将一个序列的元素组合在一起。

/* 第一种原型:
             * IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
             * this IEnumerable<TSource> source, 
             * Func<TSource, TKey> keySelector);
             */
            {
                EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();

                IEnumerable<IGrouping<int, EmployeeOptionEntry>> outerSequence =
                    empOptions.GroupBy(o => o.id);

                foreach (IGrouping<int, EmployeeOptionEntry> keyGroupSequence in outerSequence)
                {
                    Console.WriteLine("员工的选项记录: {0} ", keyGroupSequence.Key);

                    foreach (EmployeeOptionEntry element in keyGroupSequence)
                    {
                        Console.WriteLine("id={0} : optionsCount={1} : dateAwarded={2:d}",
                            element.id, element.optionsCount, element.dateAwarded);
                    }
                }
                /*结果:员工的选项记录: 1
                        id=1 : optionsCount=2 : dateAwarded=1999/12/31
                        员工的选项记录: 2
                        id=2 : optionsCount=10000 : dateAwarded=1992/6/30
                        id=2 : optionsCount=10000 : dateAwarded=1994/1/1
                        id=2 : optionsCount=10000 : dateAwarded=2003/4/1
                        员工的选项记录: 3
                        id=3 : optionsCount=5000 : dateAwarded=1997/9/30
                        id=3 : optionsCount=7500 : dateAwarded=1998/9/30
                        id=3 : optionsCount=7500 : dateAwarded=1998/9/30
                        员工的选项记录: 4
                        id=4 : optionsCount=1500 : dateAwarded=1997/12/31
                        员工的选项记录: 101
                        id=101 : optionsCount=2 : dateAwarded=1998/12/31
                 */
            }

第二个GroupBy原型示例假定开发者已经知道id小于100的所有雇员都可以认为是这家公司创始人。
示例代码任务是列出创始人状态分组的股票记录。

            //第二种原型:
             public static  IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
             this IEnumerable<TSource> source, 
             Func<TSource, TKey> keySelector, 
             IEqualityComparer<TKey> comparer);  //区别在于没有使用默认相等比较器 EqualityComparerDefault

需要一个相等比较器来处理这个关键的比较功能。必须实现IEqualityComparer接口。

    public class MyFounderNumberComparer:IEqualityComparer<int>
    {
        public bool Equals(int x, int y)
        {
            return(isFounder(x) == isFounder(y));
        }

        public int GetHashCode(int obj)
        {
            int f = 1;
            int nf = 100;
            return (isFounder(obj) ? f.GetHashCode() : nf.GetHashCode());
        }

        public bool isFounder(int id)
        {
            return (id < 100);
        }
    }
    
                {
                MyFounderNumberComparer comp = new MyFounderNumberComparer();
                EmployeeOptionEntry[] empOptions = EmployeeOptionEntry.GetEmployeeOptionEntries();
                IEnumerable<IGrouping<int, EmployeeOptionEntry>> opts = empOptions.GroupBy(o => o.id, comp);
                foreach (IGrouping<int, EmployeeOptionEntry> keyGroup in opts)
                {
                    Console.WriteLine("员工的选项记录: {0} ", (comp.isFounder(keyGroup.Key) ? "founder" : "non-founder"));

                    foreach (EmployeeOptionEntry element in keyGroup)
                    {
                        Console.WriteLine("id={0} : optionsCount={1} : dateAwarded={2:d}",
                            element.id, element.optionsCount, element.dateAwarded);
                    }
                }
                /*结果:员工的选项记录: founder
                        id=1 : optionsCount=2 : dateAwarded=1999/12/31
                        id=2 : optionsCount=10000 : dateAwarded=1992/6/30
                        id=2 : optionsCount=10000 : dateAwarded=1994/1/1
                        id=3 : optionsCount=5000 : dateAwarded=1997/9/30
                        id=2 : optionsCount=10000 : dateAwarded=2003/4/1
                        id=3 : optionsCount=7500 : dateAwarded=1998/9/30
                        id=3 : optionsCount=7500 : dateAwarded=1998/9/30
                        id=4 : optionsCount=1500 : dateAwarded=1997/12/31
                        员工的选项记录: non-founder
                        id=101 : optionsCount=2 : dateAwarded=1998/12/31
                */
            }

8.集合操作符

1.Distinct (去重)##

删除输入序列中的重复元素。

			//Distinct<TSource>(this IEnumerable<TSource> source);
            {
                Console.WriteLine("Presidents count:" + Presidents.Count());
                //串联Presidents数组本身,现在每个元素重复两次
                IEnumerable<string> presidentsWithDupes = Presidents.Concat(Presidents);

                Console.WriteLine("presidentsWithDupes count:" + presidentsWithDupes.Count());

                //删除重复元素,并显示元素数
                IEnumerable<string> presidentsDistinct = presidentsWithDupes.Distinct();

                Console.WriteLine("presidentsWithDupes count:" + presidentsDistinct.Count());

                /*结果:  Presidents count:36
                          presidentsWithDupes count:72
                          presidentsWithDupes count:36 */
            }

2.Union (联合)##

返回一个由两个源序列的集合联合组成的序列。

            {
                //因为只跳过4个元素,因此两个序列都包含第5个元素
                IEnumerable<string> first = Presidents.Take(5);
                IEnumerable<string> second = Presidents.Skip(4);

                IEnumerable<string> concat = first.Concat<string>(second);
                IEnumerable<string> union = first.Union<string>(second);

                Console.WriteLine("the count of the presidents array is:" + Presidents.Count());
                Console.WriteLine("the count of the first sequence is:" + first.Count());
                Console.WriteLine("the count of the second sequence is:" + second.Count());
                Console.WriteLine("the count of the concat sequence is:" + concat.Count());
                Console.WriteLine("the count of the union sequence is:" + union.Count());

                /*结果:
                        the count of the presidents array is:36
                        the count of the first sequence is:5
                        the count of the second sequence is:32
                        the count of the concat sequence is:37
                        the count of the union sequence is:36	//union自动去重
                        */
            }

3.Intersect (交集)##

返回两个源序列的交集

4.Except (差集)

返回第一个序列中没有出现在第二个序列中的所有元素组成的序列。

            {
                IEnumerable<string> first = Presidents.Take(4); //Adams Arthur Buchanan Bush
                //除了first这4个名字以外的其他数据
                IEnumerable<string> exceptions = Presidents.Except(first);
                foreach (var item in first)
                    Console.Write("{0} ", item);
                //结果: Carter Cleveland Clinton Coolidge Eisenhower Fillmore Ford Garfield Grant Hardin ......
            }

9.转换操作符

1.Cast

提供一种简单和方便的方法将序列转换为其他的集合类型。

//IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
//IEnumerable 转 IEnumerable<T>

2.OfType

用于构建一个输出序列,该序列只包含那些可以被转换为指定类型的元素。


与Cast区别:OfType只转换成功的,不报错,Cast不能转换时,报错。

            {
                ArrayList al = new ArrayList();
                al.Add(new Employee() { id = 1, firstName = "Joe", lastName = "Rattz" });
                al.Add(new Employee { id = 2, firstName = "Willam", lastName = "Gates" });
                al.Add(new EmployeeOptionEntry() { id = 1, optionsCount = 0 });
                al.Add(new EmployeeOptionEntry() { id = 2, optionsCount = 999999999 });
                al.Add(new Employee { id = 3, firstName = "Anders", lastName = "Hejlsberg" });
                al.Add(new EmployeeOptionEntry() { id = 3, optionsCount = 848475745 });

                var items = al.Cast<Employee>();
                try
                {
                    foreach (var item in items)
                        Console.WriteLine("{0} {1} {2}", item.id, item.firstName, item.lastName);

                }
                catch (Exception ex)
                {
                    //Cast<Employee> 转换 EmployeeOptionEntry 报错
                    Console.WriteLine("{0} {1}", ex.Message, System.Environment.NewLine);
                }

                var items2 = al.OfType<Employee>(); //OfType<Employee>只转换成功的
                foreach (var item in items2)
                    Console.WriteLine("{0} {1} {2}", item.id, item.firstName, item.lastName);
                /*结果:
                 *  1 Joe Rattz
                    2 Willam Gates
                    Unable to cast object of type 'Base.EmployeeOptionEntry' to type 'Base.Employee'.

                    1 Joe Rattz
                    2 Willam Gates
                    3 Anders Hejlsberg
                 */
            }

3.AsEnumerable

IEnumerable<T>转换为IEnumerable<T>
其他域的集合可以选择实现其自己的序列类型和操作符,比如用来访问数据库的集合。AsEnumerable允许输入序列被转换为一个常规IEnumerable<T>序列,
允许调用标准操作符方法。
例如,LINQ到SQL使用的IQueryable<T>,没有Reverse()方法。可以在Reverse()之前调用AsEnumerable

					(from c in db.Customers
                        where c.City == "Rio"
                        select c)
                    .AsEnumerable()
                    .reverse();

10.元素操作符

1.DefaultEmpty (提取单个元素)##

从输入序列中提取单个元素。

            //第一种原型:DefaultIfEmpty<TSource>(this IEnumerable<TSource> source);
            {
                //不使用DefaultIfEmpty
                //string jones = Presidents.Where(n => n.Equals("Jones")).First();
                //异常:Sequence contains no elements
                //没找到Jones时,向First()传递一个空序列,报错

                string jones = Presidents.Where(n => n.Equals("Jones")).DefaultIfEmpty().First();
                //使用DefaultIfEmpty,一个包含null元素的序列传递给First(),而不是一个空序列

                if (jones != null)
                    Console.WriteLine("Jones was found");
                else
                    Console.WriteLine("Jones was not found");
            }

            //第二种原型:DefaultIfEmpty<TSource>(this IEnumerable<TSource> source, TSource defaultValue);
            {
                string jones = Presidents.Where(n => n.Equals("Jones")).DefaultIfEmpty("Missing").First();
                //使用DefaultIfEmpty,返回指定的值,而不是一个空序列

                Console.WriteLine(jones);  //输出:Missing
            }
            

更复杂的 左外部连接

// GroupJoin 与 DefaultIfEmpty 组成的 
{
                ArrayList employeesAL = Employee.GetEmployeesArrayList();
                employeesAL.Add(new Employee() { id = 102, firstName = "Michael", lastName = "Bolton" });
                Employee[] employees = employeesAL.Cast<Employee>().ToArray();
                EmployeeOptionEntry[] empOtions = EmployeeOptionEntry.GetEmployeeOptionEntries();
                /*左外部连接:
                 1.没有.DefaultIfEmpty(),则不能找到 { id = 102, name = Michael Bolton, options = 0 }
                 2.因为os为 IEnumerable<EmployeeOptionEntry> ,要使用optionsCount,所以用os.Select()返回一个输出序列
                 如果直接 (e, os) => new{} ,不能直接返回序列os里的optionsCount
                 3.因为os.Select(),所以最后需要.SelectMany()
                 */
                var employeeOptions = employees
                    .GroupJoin(empOtions,
                        e => e.id,
                        o => o.id,
                        (e, os) => os
                            //.DefaultIfEmpty()                  
                            .Select(p => new
                            {
                                id = e.id,
                                name = string.Format("{0} {1}", e.firstName, e.lastName),
                                options = p != null ? p.optionsCount : 0
                            }))
                .SelectMany(r => r);

                //换成 lambda表达式
                var employeeOptionsLinq = (from e in employees
                                           join o in empOtions on e.id equals o.id
                                           into os
                                           from o in os.DefaultIfEmpty()
                                           select new
                                           {
                                               id =  e.id,
                                               name = string.Format("{0} {1}", e.firstName, e.lastName),
                                               options = o != null ? o.optionsCount : 0
                                           });


                foreach (var option in employeeOptions)
                {
                    Console.WriteLine(option);
                }
                /*结果:{ id = 1, name = Joe Rattz, options = 2 }
                        { id = 2, name = Willam Gates, options = 10000 }
                        { id = 2, name = Willam Gates, options = 10000 }
                        { id = 2, name = Willam Gates, options = 10000 }
                        { id = 3, name = Anders Hejlsberg, options = 5000 }
                        { id = 3, name = Anders Hejlsberg, options = 7500 }
                        { id = 3, name = Anders Hejlsberg, options = 7500 }
                        { id = 4, name = David Lightman, options = 1500 }
                        { id = 101, name = Kevin Flynn, options = 2 }
                        { id = 102, name = Michael Bolton, options = 0 }    <-- DefaultIfEmpty()效果
                */
            }


看看执行的 from o in os.DefaultIfEmpty()

最后生成的结果

11.生成操作符

1.Range (生成整数序列)##

生成一个整数序列。
Range操作符不是一个扩展方法,而是Enuerable类的静态方法。

/* IEnumerable<int> Range(int start, int count);  */
            {
                IEnumerable<int> ints = Enumerable.Range(4, 10);
                foreach (var i in ints)
                    Console.Write(i+",");
                //结果:4,5,6,7,8,9,10,11,12,13,
            }

2.Repeat (生成重复元素序列)##

通过指定次数,并重复一个指定的元素来生成一个序列。

 /*IEnumerable<TResult> Repeat<TResult>(TResult element, int count);
枚举时,生成 count 数量的 element 元素*/
            {
                IEnumerable<int> ints = Enumerable.Repeat(2, 10);
                foreach (var i in ints)
                    Console.Write(i + ",");
                //结果:2,2,2,2,2,2,2,2,2,2,
            }

3.Empty (生成空序列)##

生成一个特定类型的空序列。

            //IEnumerable<TResult> Empty<TResult>();
            {
                IEnumerable<string> strings = Enumerable.Empty<string>();
                foreach (var i in strings)
                    Console.Write(i + ",");

                Console.WriteLine(strings.Count());
                //结果:0
            }
posted @ 2018-01-08 13:55  【唐】三三  阅读(352)  评论(0编辑  收藏  举报