zoukankan      html  css  js  c++  java
  • Linq 学习笔记(二)

    下面就来介绍一些查询的示例:

    1。Linq查询

    var racers = from r in Formula1.GetChampions()
    where r.Wins > 15 && (r.Country == "Brazil" || r.Country == "Austria") select r;
    foreach (var r in racers)
    {
    Responose.Write("{0:A}", r);
    }

    使用扩展方法的查询

    并不是所有的查询都可以用LINQ查询完成。也不是所有的扩展方法都映射到LINQ查询子句上。高级查询需要使用扩展方法。为了更好地理解带扩展方法的复杂查询,最好看看简单的查询是如何映射的。使用扩展方法Where()和Select(),会生成与前面LINQ 查询非常类似的结果:

    var racers = Formula1.GetChampions().Where(r => r.Wins > 15 && (r.Country == "Brazil" || r.Country == "Austria")).Select(r = > r);

    2。用索引来过滤

    不能使用LINQ 查询的一个例子是Where()方法的重载。在Where()方法的重载中,可以传送第二个参数——索引。索引是过滤器返回的每个结果的计数器。可以在表达式中使用这个索引,执行基于索引的计算。下面的代码由Where()扩展方法调用,它使用索引返回姓氏以A开头、索引为偶数的赛手:

    var racers = Formula1.GetChampions().Where((r, index) => r.LastName.StartsWith("A") &&

    index % 2 != 0);
    foreach (var r in racers)
    {
    Responose.Write("{0:A}", r);

    }

    3。类型过滤

    为了进行基于类型的过滤,可以使用OfType()扩展方法。这里数组数据包含string和int对象。使用OfType()扩展方法,把string类传送给泛型参数,就从集合中返回字符串。

    object[] data = { "one", 2, 3, "four", "five",6 };
    var query = data.OfType<string>();
    foreach (var s in query)
    {
    Console.WriteLine(s);
    }

    4。复合的from子句

    var ferrariDrivers = from r in Formula1.GetChampions() from c in r.Cars where c == "Ferrari"
    orderby r.LastName select r.FirstName + " " + r.LastName; 

    C#编译器把复合的from 子句和LINQ 查询转换为SelectMany()扩展方法。SelectMany()可用于迭代序列的序列。示例中SelectMany()方法的重载版本如下所示:

    public static IEnumerable<TResult> SelectMany<TSource,TCollection,TResult>(this IEnumerable<TSource> source,Func<TSource,IEnumerable<TCollection>> collectionSelector,Func<TSource,TCollection,TResult>resultSelector);

    第一个参数是隐式参数,从GetChampions()方法中接收Racer对象序列。第二个参数是collectionSelector委托,它定义了内部序列。在λ表达式r=>r.Cars中,应返回赛车集合。第三个参数是一个委托,现在为每个赛车调用该委托,接收Racer和Car对象。λ表达式创建了一个匿名类型,它带Racer和Car属性。这个SelectMany()方法的结果是摊平了赛手和赛车的层次结构,为每辆赛车返回匿名类型的一个新对象集合。

    这个新集合传送给Where()方法,过滤出驾驶Ferrari 的赛手。最后,调用OrderBy()和Select()方法:
    var ferrariDrivers = Formula1.GetChampions().SelectMany(r => r.Cars,
    (r, c) => new { Racer = r, Car = c }).Where(r => r.Car == "Ferrari").OrderBy(r =>r.Racer.LastName).Select(r => r.Racer.FirstName + " " +r.Racer.LastName);
    把SelectMany()泛型方法解析为这里使用的类型,所解析的类型如下所示。在这个例子中,数据源是
    Racer类型,所过滤的集合是一个string数组,当然所返回的匿名类型的名称是未知的,这里显示为TResult:
    public static IEnumerable<TResult> SelectMany<Racer, string, TResult>(
    this IEnumerable<Racer> source,Func<Racer,IEnumerable<string>> collectionSelector,
    Func<Racer,string,TResult> resultSelector);
    查询仅从LINQ 查询转换为扩展方法,所以结果与前面的相同。

    5。排序

    var racers = from r in Formula1.GetChampions() where r.Country == "Brazil" orderby r.Wins descending select r;

    orderby子句解析为OrderBy()方法,orderby descending子句解析为OrderBy Descending()方法:

    var racers = Formula1.GetChampions().Where(r => r.Country == "Brazil").OrderByDescending(r => r.Wins).Select(r => r);

    OrderBy()和OrderByDescending()方法返回IOrderEnumerable<TSource>。这个接口派生于接口IEnumerable<TSource>,但包含一个额外的方法CreateOrderedEnumerable-<TSource>()。这个方法用于进一步给序列排序。如果根据关键字选择器来排序,两项的顺序相同,就可以使用ThenBy()和ThenByDescending()方法继续排序。这两个方法需要IOrderEnumerable<TSource>才能工作,但也返回这个接口。所以,可以添加任意多个ThenBy()和ThenByDescending()方法,对集合排序。

    使用LINQ 查询时,只需把所有用于排序的不同关键字(用逗号分隔开)添加到orderby 子句中例如

    var racers = (from r in Formula1.GetChampions() orderby r.Country, r.LastName, r.FirstName select r).Take(10);

    6。分组

    var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key where g.Count() >= 2 select new { Country = g.Key, Count = g.Count() };
    foreach (var item in countries)
    {
    Response.Write("{0, -10} {1}",item.Country, item.Count);
    }

    扩展方法的分组表示:

    var countries = Formula1.GetChampions().GroupBy(r => r.Country).OrderByDescending(g => g.Count()).
    ThenBy(g => g.Key).Where(g => g.Count() >= 2).Select(g => new { Country = g.Key,Count = g.Count()});

    7。对嵌套的对象分组

    如果分组的对象应包含嵌套的对象,就可以改变select 子句创建的匿名类型。

    var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key where g.Count() >= 2 select new
    {
    Country = g.Key, Count = g.Count(),

    Racers = from r1 in g orderby r1.LastName select r1.FirstName + " " + r1.LastName
    };
    foreach (var item in countries)
    {
    Response.Write("{0, -10} {1}", item.Country, item.Count);
    foreach (var name in item.Racers)
    {
    Response.Write("{0}; ", name);
    }
    Response.Write("<br/>");
    }

    8。连接

    使用join 子句可以根据特定的条件合并两个数据源,但之前要获得两个要连接的列表。

    var racers = from r in Formula1.GetChampions() from y in r.Years where y > 2003 select new
    {
    Year = y,
    Name = r.FirstName + " " + r.LastName
    };

    var teams = from t in Formula1.GetContructorChampions() from y in t.Years where y > 2003

    select new { Year = y, Name = t.Name };

    有了这两个查询,再通过子句join t in teams on r.Year equals t.Year就可以得到结果集了。

    var racersAndTeams = from r in racers join t in teams on r.Year equals t.Year select new
    {
    Year = r.Year,
    Racer = r.Name,
    Team = t.Name
    };
    Response.Write("Year Champion " + "Constructor Title");
    foreach (var item in racersAndTeams)
    {
    Response.Write("{0}: {1,-20} {2}",item.Year, item.Racer, item.Team);
    }

    9。设置操作

    扩展方法Distinct()、Union()、Intersect()和Except()都是设置操作。

    var ferrariDrivers = from r in Formula1.GetChampions() from c in r.Cars where c == "Ferrari"
    orderby r.LastName select r;
    现在建立另一个相同的查询,但where 子句的参数不同,以获得所有驾驶McLaren 的冠军。最好不要再次编写相同的查询。而可以创建一个方法,给它传送参数car:
    private static IEnumerable<Racer> GetRacersByCar(string car)
    {
    return from r in Formula1.GetChampions() from c in r.Cars where c == car orderby r.LastName select r;
    }
    但是,因为该方法不需要在其他地方使用,所以应定义一个委托类型的变量来保存LINQ 查询。变量racerByCar 必须是一个委托类型,它需要一个字符串参数,返回IEnumerable <Racer>,类似于前面实现的方法。为此,定义了几个泛型委托Func<>,所以不需要声明自己的委托。把一个λ表达式赋予变量racerByCar。λ表达式的左边定义了一个car 变量,其类型是Func 委托的第一个泛型参数(字符串)。右边定义了LINQ 查询,它使用该参数和where 子句:
    Func<string, IEnumerable<Racer>> racersByCar = Car => from r in Formula1.GetChampions() from c in r.Cars where c == car orderby r.LastName select r;
    现在可以使用Intersect()扩展方法,获得驾驶Ferrari 和McLaren 的所有冠军:
    Response.Write("World champion with " + "Ferrari and McLaren");
    foreach (var racer in racersByCar("Ferrari").
    Intersect(racersByCar("McLaren")))
    {
    Response.Write(racer);
    }

    10。分区

    扩展方法Take()和Skip()等的分区操作可用于分页,例如显示5×5 个赛手。在下面的LINQ 查询中,扩展方法Take()和Skip()添加到查询的最后。Skip()方法先忽略根据页面的大小和实际的页数计算出的项数,再使用方法Take()根据页面的大小提取一定数量的项:
    int pageSize = 5;
    int numberPages = (int)Math.Ceiling(Formula1.GetChampions().Count()/(double)pageSize);

    for (int page = 0; page < numberPages; page++)
    {
    Response.Write("Page {0}", page);
    var racers = (from r in Formula1.GetChampions() orderby r.LastName select r.FirstName + " " + r.LastName).Skip(page * pageSize).Take(pageSize);
    foreach (var name in racers)
    {
    Response.Write(name);
    }
    Response.Write();
    }

    11。合计操作符

    合计操作符如Count()、Sum()、Min()、Max()、Average()和Aggregate(),不返回一个序列,而返
    回一个值。

    var query = from r in Formula1.GetChampions() where r.Years.Count() > 3 orderby r.Years.Count() descending select new
    {
    Name = r.FirstName + " " +r.LastName,
    TimesChampion = r.Years.Count()
    };
    foreach (var r in query)
    {
    Response.Write("{0} {1}", r.Name, r.TimesChampion);
    }

    Sum()方法汇总序列中的所有数字,返回这些数字的和。下面的Sum()用于计算一个国家赢得比赛的
    总次数。首先根据国家对赛手分组,再在新创建的匿名类型中,给Wins 属性赋予某个国家赢得比赛的总
    次数。

    var countries = (from c in from r in Formula1.GetChampions() group r by r.Country into c

    select new
    {
    Country = c.Key,
    Wins = (from r1 in c select r1.Wins).Sum()
    }
    orderby c.Wins descending, c.Country select c).Take(5);
    foreach (var country in countries)
    {
    Response.Write("{0} {1}",country.Country, country.Wins);
    }

    方法Min()、Max()、Average()和Aggregate()的使用方式与Count()和Sum()相同。Min()返回集合
    中的最小值,Max()返回集合中的最大值,Average()计算集合中的平均值。对于Aggregate()方法,可
    以传送一个λ表达式,对所有的值进行汇总。

    12。转换

    查询可以推迟到访问数据项时再执行。在迭代中使用查询,查询会执行。而使用转换操作符会立即执行查询,把结果放在数组、列表或字典中。在下面的例子中,调用ToList()扩展方法,立即执行查询,把结果放在List<T>中:

    List < Racer > racers = (from r in Formula1.GetChampions()
    where r.Starts > 150 orderby r.Starts descending select r).ToList();
    foreach (var racer in racers)
    {
    Response.Write("{0} {0:S}", racer);
    }

    把返回的对象放在列表中并没有这么简单。例如,对于集合中从赛车到赛手的快速访问,可以使用新类Lookup<TKey, TElement>。

    提示:
    Dictionary<TKey, TValue>只支持一个键对应一个值。在System.Linq 命名空间的类Lookup<TKey,
    TElement>中,一个键可以对应多个值。这些类详见第10 章。
    使用复合的from 查询,可以摊平赛手和赛车序列,创建带有Car 和Racer 属性的匿名类型。在返回的Lookup 对象中,键的类型应是表示汽车的string,值的类型应是Racer。为了进行这个选择,可以给ToLookup()方法的一个重载版本传送一个键和一个元素选择器。键选择器表示Car 属性,元素选择器表示Racer 属性。

    ILookup<string, Racer> racers = (from r in Formula1.GetChampions() from c in r.Cars select new
    { Car = c, Racer = r }).ToLookup(cr = > cr.Car, cr = > cr.Racer);
    if (racers.Contains("Williams"))
    {
    foreach (var williamsRacer in racers["Williams"])
    {
    Response.Write(williamsRacer);
    }
    }

    如果需要在未类型化的集合上使用LINQ查询,例如ArrayList,就可以使用Cast()方法。在下面的例子中,基于Object 类型的ArrayList 集合用Racer 对象填充。为了定义强类型化的查询,可以使用Cast()方法。
    System.Collections.ArrayList list = new System.Collections.ArrayList(Formula1.GetChampions() as
    System.Collections.ICollection);
    var query = from r in list.Cast <Racer>() where r.Country == "USA" orderby r.Wins descending select r;
    foreach (var racer in query)
    {
    Response.Write("{0:A}", racer);
    }

    13。生成操作符

    生成操作符Range()、Empty()和Repear()不是扩展方法,而是返回序列的正常静态方法。在LINQto Objects 中,这些方法可用于Enumerable 类。有时需要填充一个范围的数字,此时就应使用Range()方法。这个方法把第一个参数作为起始值,把第二个参数作为要填充的项数。
    var values = Enumerable.Range(1, 20);
    foreach (var item in values)
    {
    Response.Write("{0} ", item);
    }
    Range()方法不返回填充了所定义值的集合,这个方法与其他方法一样,也推迟执行查询,返回一个RangeEnumerator,其中只有一个yield return 语句,来递增值。可以把该结果与其他扩展方法合并起来,获得另一个结果,例如使用Select()扩展方法:
    var values = Enumerable.Range(1, 20).Select(n = > n * 3);
    Empty()方法返回一个不返回值的迭代器,它可以用于参数需要一个集合,且可以给参数传送空集
    合的情形。Repeat()方法返回一个迭代器,该迭代器把同一个值重复特定的次数。

    ASP.NET开发技术交流群: 67511751(人员招募中...)

  • 相关阅读:
    高斯消元学习
    HDU 4596 Yet another end of the world(解一阶不定方程)
    Codeforces Round #318 div2
    HDU 4463 Outlets(一条边固定的最小生成树)
    HDU 4458 Shoot the Airplane(计算几何 判断点是否在n边形内)
    HDU 4112 Break the Chocolate(简单的数学推导)
    HDU 4111 Alice and Bob (博弈)
    POJ 2481 Cows(线段树单点更新)
    HDU 4288 Coder(STL水过)
    zoj 2563 Long Dominoes
  • 原文地址:https://www.cnblogs.com/Juvy/p/2160847.html
Copyright © 2011-2022 走看看