• C# LINQ(10)


    LINQ 查询

    var query = from r in Formula1.GetChampions()
        where r.Country == "Brazil"
        orderby r.Wins descending
        select r;
    
    foreach (Racer racer in query)
    {
        Console.WriteLine("{0:A}", racer);
    }

    扩展方法

    LINQ为IEnumerable<T>接口提供各种扩展方法,以便用户实现了该接口的任意集合上使用LINQ查询。扩展方法在静态类中声明,定义一个静态方法,第一参数定义扩展的类型。

    扩展方法可以将方法写入最初没有提供该方法的类中,可以把方法添加到实现某个特定接口的任何类中,这样多个类可以使用相同的实现代码。

    class Program
    {
        private static void Main(string[] args)
        {
            string s1 = "111111";
            s1.Function();
    
            string s2 = "2222222";
            s2.Function();
        }
    }
    
    public static class StringExtension 
    {
        public static void Function(this string s)
        {
            Console.WriteLine("this string is " + s);
        }
    }

    第一个参数 this 用来区分是扩展方法还是静态方法。

    第二个参数 需要对应扩展的类。

    注意 扩展方法里不能访问类型的私有成员。

    还可以这样调用

    StringExtension.Function(s1);

    LINQ 扩展示例

    var champions = new List<Racer>(Formula1.GetChampions());
    IEnumerable<Racer> brazilChampions =
        champions.Where(r => r.Country == "Brazil").
                OrderByDescending(r => r.Wins).
                Select(r => r);
    
    foreach (Racer r in brazilChampions)
    {
      Console.WriteLine("{0:A}", r);
    }

    推迟查询的执行

    var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" };
    
    var namesWithJ = from n in names
                     where n.StartsWith("J")
                     orderby n
                     select n;
    
    Console.WriteLine("First iteration");
    foreach (string name in namesWithJ)
    {
      Console.WriteLine(name);
    }
    Console.WriteLine();
    
    names.Add("John");
    names.Add("Jim");
    names.Add("Jack");
    names.Add("Denny");
    
    Console.WriteLine("Second iteration");
    foreach (string name in namesWithJ)
    {
      Console.WriteLine(name);
    }

    namesWithJ 一旦使用了ToArray、ToList之类的。就 names 了。如

    var namesWithJ = (from n in names
                           where n.StartsWith("J")
                           orderby n
                           select n).ToList();

    标准的查询操作

    Enumberable 类定义的标准查询操作符。 

    标准查询操作符

    说明

    Where

    OfType<TResult>

    称为筛选操作符定义返回元素的条件。

    Where     使用谓词,返回符合条件的元素。

    OfType<TResult>  返回符合类型的元素。

    Select

    SelectMany

    投射操作符用于把对象转换为另一个类型的新对象。

    Select 和 SelectMany 定义根据选择器函数选择结果值的投射。

    OrderBy

    ThenBy

    OrderByDescending

    ThenByDescending

    Reverse

    排序操作符改变返回的元素的顺序。

    Orderby 升序排序。

    OrderBydescending  降序排序。

    TheBy 和 ThenByDescending 二次排序。

    Reverse 反转集合元素。

    Join

    GroupJoin

    连接操作符。用于合并不直接相关的集合。

    Join     根据键选择器函数连接两个集合。

    GroupJoin   连接两个集合。

    GroupBy

    ToLookup

    组合操作符把数据放在组中。

    GroupBy   组合公共键的元素。

    Tookup    创建一个一对多字典,组合元素。

    Any

    All

    Contains

    限定操作符,元素满足指定的条件。

    Any 满足谓词函数的函数。

    All  所有元素是否都满足谓词函数。

    Contains   检查某个元素是否在集合中。

    Take

    Skip

    TakeWhile

    SkipWhile

    分区操作符返回集合的子集。

    Take 从集合提取元素个数。

    Skip 跳过指定的元素个数,提取其他元素。

    TakeWhile 提取条件为真的元素。

    Distinct

    Union

    Intersect

    Except

    Zip

    Set操作符返回一个集合。

    Distinct 删除重复的元素。

    Union 返回集合中唯一元素。

    Intersect  返回两个集合都有的元素。

    Except  只出现在一个集合中的元素。

    Zip 两个集合合并为一个元素。

    First

    FirstOrDefault

    Last

    LastOrDefault

    ElementAt

    ElementAtOrDefault

    Single

    SingleOrDefault

    元素操作符返回一个元素。

    First                 返回第一个满足条件的元素。

    FirstOrDefault  类似First,如果未找到满足条件元素,返回类型的默认值。

    Last                 返回最后一个满足条件的元素。

    ElementAt        返回元素的位置。

    Single               返回一个满足条件的元素。如果有多个元素都满足条件,就抛出一个异常。

    Count

    Sum

    Min

    Max

    Average

    Aggregate

    聚合操作符计算集合值。

    Sum 总和。

    Count  所有元素个数。

    Min  最小元素。

    Max  最大元素。

    Average 平均值。

    Aggregate  根据输入的表达式获取聚合值。

    ToArray

    AsEnumerable

    ToList

    ToDictionary

    Cast<TResult>

    转换操作符。

    Empty

    Range

    Repeat

    生成操作符。

    Empty 空集合。

    Range 返回一系列数字。

    Repeat 返回始终重复一直的集合。

    筛选

    where子句合并多个表达式。 

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

    下面代码有Where扩展方法Where和Select调用。

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

    索引器筛选

    索引是筛选器返回的每个结果的计数器。

    下面由Where扩展方法调用, 使用索引返回。

    var racers = Formula1.GetChampions().
        Where((r, index) => r.Wins > 15 && index % 2 != 0);

    类型筛选

    基于类型筛选,使用 OfType 扩展方法。

     object[] data = {"one", 1, 2, "li"};
     var query = data.OfType<int>();
    
     foreach (var intValue in query)
     {
         Console.WriteLine("{0}", intValue);
     }

    复合的from子句

    var ferrariDrivers = from r in Formula1.GetChampions()
        from c in r.Cars
        where c == "Ferrari"
        orderby r.LastName
        select r.FirstName + " " + r.LastName;
    
    
    foreach (string name in ferrariDrivers)
    {
        Console.WriteLine("{0}", name);
    }

    C#编译器把复合的from子句和LINQ查询转换为SelectMany扩展方法。SelectMany方法用于迭代序列的序列。

    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);

    排序

    对序列排序,前面使用了orderby 子句。使用两种方法。

    var racers = from r in Formula1.GetChampions()
        where r.Country == "Brazil"
        orderby r.Wins descending
        select r;
    
    var racers2 = 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 子句。 扩展方法 Take  提取前面 10 个元素。

    var racers = (from r in Formula1.GetChampions()
                  orderby r.Country, r.LastName, r.FirstName
                  select r).Take(10);
    foreach (var item in racers)
    {
        Console.WriteLine(item);
    }

    也可以使用 OrderBy() 和 ThenBy() 扩展方法执行相同的操作:

    var racers = (from r in Formula1.GetChampions()
                  orderby r.Country, r.LastName, r.FirstName
                  select r).Take(10);
    foreach (var item in racers)
    {
        Console.WriteLine(item);
    }
    
    Console.WriteLine("***********");
    
    var racers2 = Formula1.GetChampions().
        OrderBy(r => r.Country).
        ThenBy(r => r.LastName).
        ThenBy(r => r.FirstName).
        Take(10);
    
    foreach (var item in racers2)
    {
        Console.WriteLine(item);
    }

    分组

    根据一个关键字值对查询结果进行分组,使用 group 子句。

    子句 group r by r.Country into g 根据 Country 属性组合。并定义一个新的标识符g。它以后用于访问分组的结果信息。

    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 country in countries)
    {
        Console.WriteLine(format: "{0,-10} {1}", arg0: country.Country, arg1: country.Count);
    }

    使用扩展方法,子句 group r by r.Country into g 解析为 GroupBy(r => r.Country) 返回分组序列。

    var contries2 = 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()
        });

    对嵌套的对象分组

    分组的对象包含嵌套的序列,可以改变 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 racer in g
            orderby racer.LastName
            select racer.FirstName + " " + racer.LastName
        };
    
    
    foreach (var country in countries)
    {
        Console.WriteLine(format:"{0, -10} {1}", arg0:country.Country, arg1:country.Count);
        foreach (var racer in country.Racers)
        {
            Console.WriteLine(format:"{0}; ", arg0:racer);
        }
        Console.WriteLine();
    }

    内连接

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

    var racers = from r in Formula1.GetChampions()
                 from y in r.Years
                 select new
                 {
                   Year = y,
                   Name = r.FirstName + " " + r.LastName
                 };
    
    var teams = from t in Formula1.GetContructorChampions()
                from y in t.Years
                select new
                {
                  Year = y,
                  Name = t.Name
                };
    
    //var racersAndTeams =
    //      (from r in racers
    //       join t in teams on r.Year equals t.Year
    //       orderby t.Year
    //       select new
    //       {
    //         Year = r.Year,
    //         Champion = r.Name,
    //         Constructor = t.Name
    //       }).Take(10);
    
    var racersAndTeams =  
              (from r in
          from r1 in Formula1.GetChampions()
          from yr in r1.Years
          select new
          {
              Year = yr,
              Name = r1.FirstName + " " + r1.LastName
          }
    
          join t in 
          from t1 in Formula1.GetContructorChampions()
          from yt in t1.Years
          select new
          {
              Year = yt,
              Name = t1.Name
          }
          on r.Year equals t.Year
    
          orderby t.Year
          select new
          {
              Year = r.Year,
              Champion = r.Name,
              Constructor = t.Name
          }).Take(10);
      
    
    Console.WriteLine("Year  World Champion	   Constructor Title");
    foreach (var item in racersAndTeams)
    {
      Console.WriteLine("{0}: {1,-20} {2}",
         item.Year, item.Champion, item.Constructor);
    }

    左外连接

    左外连接返回左边序列中的全部元素,即使它们在右边的序列中并没有匹配的元素。 

    左外连接用join子句和 DefaultIfEmpty 方法定义。  使用 DefaultIfEmpty 定义其右侧的默认值。

    var racers = from r in Formula1.GetChampions()
                 from y in r.Years
                 select new
                 {
                   Year = y,
                   Name = r.FirstName + " " + r.LastName
                 };
    
    var teams = from t in Formula1.GetContructorChampions()
                from y in t.Years
                select new
                {
                  Year = y,
                  Name = t.Name
                };
    
    var racersAndTeams =
      (from r in racers
       join t in teams on r.Year equals t.Year into rt
       from t in rt.DefaultIfEmpty()
       orderby r.Year
       select new
       {
         Year = r.Year,
         Champion = r.Name,
         Constructor = t == null ? "no constructor championship" : t.Name
       }).Take(10);
    
    Console.WriteLine("Year  Champion		   Constructor Title");
    foreach (var item in racersAndTeams)
    {
      Console.WriteLine("{0}: {1,-20} {2}",
         item.Year, item.Champion, item.Constructor);
    }

    组连接

    左外连接使用了组连接和 into 子句。它有一部分与组连接相同,只不过组连接不适用 DefaultIfEmpty 方法。 使用组连接时,可以连接两个独立的序列,对于其中一个序列中的某个元素,另一个序列中存在对应项列表。

    var racers = Formula1.GetChampionships()
      .SelectMany(cs => new List<RacerInfo>()
       {
        new RacerInfo {
          Year = cs.Year,
          Position = 1,
          FirstName = cs.First.FirstName(),
          LastName = cs.First.LastName()        
        },
        new RacerInfo {
          Year = cs.Year,
          Position = 2,
          FirstName = cs.Second.FirstName(),
          LastName = cs.Second.LastName()        
        },
        new RacerInfo {
          Year = cs.Year,
          Position = 3,
          FirstName = cs.Third.FirstName(),
          LastName = cs.Third.LastName()        
        }
      });
    
    var q = (from r in Formula1.GetChampions()
             join r2 in racers on
             new
             {
                 FirstName = r.FirstName,
                 LastName = r.LastName
             }
             equals
             new
             {
                 FirstName = r2.FirstName,
                 LastName = r2.LastName
             }
             into yearResults
             select new
             {
                 FirstName = r.FirstName,
                 LastName = r.LastName,
                 Wins = r.Wins,
                 Starts = r.Starts,
                 Results = yearResults
             });
    
    foreach (var r in q)
    {
        Console.WriteLine("{0} {1}", r.FirstName, r.LastName);
        foreach (var results in r.Results)
        {
            Console.WriteLine("{0} {1}", results.Year, results.Position);
        }
    }

    集合操作

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

    Func<string, IEnumerable<Racer>> racersByCar =
        car => from r in Formula1.GetChampions()
               from c in r.Cars
               where c == car
               orderby r.LastName
               select r;
    
    Console.WriteLine("World champion with Ferrari and McLaren");
    foreach (var racer in racersByCar("Ferrari").Intersect(racersByCar("McLaren")))
    {
        Console.WriteLine(racer);
    }

    集合操作通过调用实体类的 GetHashCode() 和 Equals() 方法比较对象。 对于自定义比较,可以传递实现 IEqualityComparer<T>接口的对象。

    合并

    Zip 方法。 .Net 4.0 新加的,用一个谓词函数把两个相关的序列合并为一个。

     var racerNames = from r in Formula1.GetChampions()
                      where r.Country == "Italy"
                      orderby r.Wins descending
                      select new
                      {
                          Name = r.FirstName + " " + r.LastName
                      };
    
     var racerNamesAndStarts = from r in Formula1.GetChampions()
                               where r.Country == "Italy"
                               orderby r.Wins descending
                               select new
                               {
                                   LastName = r.LastName,
                                   Starts = r.Starts
                               };
    
    
     var racers = racerNames.Zip(racerNamesAndStarts, (first, second) => first.Name + ", starts: " + second.Starts);
     foreach (var r in racers)
     {
         Console.WriteLine(r);
     }

    分区

    扩展方法 Take() 和 Skip() 等的分区操作可以用于分页。

    Skip() 忽略根据页面大小和实际页数计算出的项数。

    Take() 根据页面大小提取一定数量的项。

    int pageSize = 5;
    
    int numberPages = (int)Math.Ceiling(Formula1.GetChampions().Count() /
          (double)pageSize);
    
    for (int page = 0; page < numberPages; page++)
    {
        Console.WriteLine("Page {0}", page);
    
        var racers =
           (from r in Formula1.GetChampions()
            orderby r.LastName, r.FirstName
            select r.FirstName + " " + r.LastName).
           Skip(page * pageSize).Take(pageSize);
    
        foreach (var name in racers)
        {
            Console.WriteLine(name);
        }
        Console.WriteLine();
    }

    对应的扩展方法 TakeWhile() 和 SkipWhile() 。

    聚合操作符

    聚合操作符(如 Count()、Sum()、Min()、Max()、Average() 、Aggregate() )返回一个值。

    Count 返回集合项数。

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

    Sum 序列中的所有数字的和。

    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)
    {
        Console.WriteLine("{0} {1}", country.Country, country.Wins);
    }

    Min 返回集合中的最小值。

    Max 返回集合中的最大值。

    Average 返回集合中的平均值。

    Aggregate 传递一个 lambda 表达式,该表达式对所有的值进行聚合。

    转换操作符

    查询可以推迟到访问数据项时再执行。在迭代中使用查询时,查询会执行。而使用转换操作符会立即执行查询,把查询结果放在数组、列表或字典中。

    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)
    {
        Console.WriteLine("{0} {0:S}", racer);
    }

    也可以用 ToLookup<TKey, TElement> 类中,键可以对应多个值。

    ToDictionary 支持键对应一个值。

    var racers = (from r in Formula1.GetChampions()
        from car in r.Cars
        select new
        {
            Car = car,
            Racer = r
        }
        ).ToLookup(cr => cr.Car, cr => cr.Racer);
    
    if (racers.Contains("Williams"))
    {
        foreach (var williamsRacer in racers["Williams"])
        {
            Console.WriteLine(williamsRacer);
        }
    }

    Cast 在非类型化的集合上查询

    var 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)
    {
        Console.WriteLine("{0:A}", racer);
    }

    生成操作符

    生成操作符 Range()、Empty() 和 Repear() 是返回序列的正常静态方法。

    在 Linq to Objects 中,这些方法可用于 Enumerable 类。

    如 需要填充一二范围的数字,此时就应使用 Range() 方法。这个方法第一个参数作为起始值,把第二个参数作为要填充的项数。

    var values = Enumerable.Range(1, 20);
    
    foreach (var value in values)
    {
        Console.WriteLine(value);
    }

    Range() 方法不返回填充所定义值的集合,与其他方法一样,推迟查询,返回一个 RangeEnumerator。其中用 yield return 语句,来递增值。

    该结果也可以与其他扩展方法一起用。

     var values = Enumerable.Range(1, 20).Select(n => n * 3);
    
     foreach (var value in values)
     {
         Console.WriteLine(value);
     }

    Empty() 方法返回一个不返回值的迭代器,用于需要一个集合的参数,可以给参数传递空集合。

    string[] names1 = { "Hartono, Tommy" };
    string[] names2 = { "Adams, Terry", "Andersen, Henriette Thaulow",
                          "Hedlund, Magnus", "Ito, Shu" };
    string[] names3 = { "Solanki, Ajay", "Hoeing, Helge",
                          "Andersen, Henriette Thaulow",
                          "Potra, Cristina", "Iallo, Lucio" };
    
    List<string[]> namesList =
        new List<string[]> { names1, names2, names3 };
    
    IEnumerable<string> allNames =
        namesList.Aggregate(Enumerable.Empty<string>(),
        (current, next) => next.Length > 3 ? current.Union(next) : current);
    
    foreach (string name in allNames)
    {
        Console.WriteLine(name);
    }

    Repeat() 方法    返回一个迭代器,把同一个值重复特定的次数。

    IEnumerable<string> strings =
        Enumerable.Repeat("I like programming.", 15);
    
    foreach (String str in strings)
    {
        Console.WriteLine(str);
    }

    并行Linq

    System.Linq 名称空间中的包含的类 ParallelEnumerable 可以分解查询的工作, 使其分布在多个线程上。尽管 Enumerable 类给 IEnumerable<T> 接口定义了扩展方法,但 ParallelEnumerable 类的大多数扩展方法是 ParallelQuery<TSource>类的扩展。一个重要的例外是 AsParallel() 方法,扩展 IEnumerable<TSource> 接口,返回 ParallelQuery<TSource>类,所以正常的集合类可以以平行方式查询。

    static IEnumerable<int> SampleData()
    {
      const int arraySize = 100000000;
      var r = new Random();
      return Enumerable.Range(0, arraySize).Select(x => r.Next(140)).ToList();
    }
    
    var data = SampleData();
    

    var res = (from x in data.AsParallel() where Math.Log(x) < 4 select x).Average();
    // 修改后的语法
    var res2 = data.AsParallel().Where(x => Math.Log(x) < 4).Select(x => x).Average();

    与Linq查询一样,编译器会修改语法,调用AsParallel()、Where()、Select()、Average()。 Asparallel() 方法调用用 ParallelEnumerable 类定义,扩展 IEnumberable<T>接口,所以可以对简单的数组调用它。 AsParallel() 方法返回 ParallelQuery<TSource>。因为返回的类型,所以编译器选择的Where()方法是ParallelEnumerable.Where(),而不是 Enumerable.Where()。 其他的 Select 和 Average 也是来自 ParallelEnumerable 类。与 Enumerable 类相反,对于 ParllelEnumerable类,查询是分区的,以便多个线程可以同时处理该查询。集合可以分为多个部分,其中每个部分由不同的线程处理,以筛选其余项。完成分区的工作后,就需要合并,获得所有部分的总和。

    运行时,可以打开任务管理器,就可以发现所有的CPU都处于忙碌的状态。

    分区器

    AsParallel()方法不仅扩展了 IEnumerable<T> 接口,还扩展了 Partitioner 类。通过它,可以影响创建的分区。

    手动创建一个分区器

    var result = (from x in Partitioner.Create(data).AsParallel() where Math.Log(x) < 4 select x).Average();

    也可以调用WithExecutionMode() 和 WithDegreeOfParallelism() 方法,影响并行机制。对于 WithExecutionMode() 方法传递 ParallelExecutionMode的一个Default值或者 ForceParallelism值。默认情况下,并行Linq避免使用系统开销很高的并行机制。对于WithDegreeOfParallelism()方法,可以传递一个整数值,制定并行运行的最大任务数。

    取消

    .NET 提供一个标准方法,来取消长时间运行的任务,也适用于并行Linq。

    要取消长时间运行的查询可以给查询添加WithCancellation() 方法,并传递一个 CancellactionToken令牌作为参数。CancelllationToken令牌从CancellactionTokenSource类中创建。该查询在单独的线程中运行,在该线程中,捕获一个OperationCanceledException类型的异常。如果取消了查询就出发这个异常。在主线程中,调用CancellationTokenSource类的Cancel()方法可以取消任务。

     var data = SampleData();
     CancellationTokenSource cts = new CancellationTokenSource();
    
     Task.Factory.StartNew(() =>
     {
         try
         {
             var res = (from x in data.AsParallel().WithCancellation(cts.Token)
                 where Math.Log(x) < 4
                 select x).Average();
             Console.WriteLine("query finished, sum:{0}",res);
         }
         catch (OperationCanceledException ex)
         {
             Console.WriteLine("canceled!");
             Console.WriteLine(ex.Message);
         }
     });
    
     string input = Console.ReadLine();
     if (input.ToLower().Equals("y"))
     {
         cts.Cancel();
         Console.WriteLine("canceled 2!");
     }

    表达式树

    扩展方法需要将一个委托类型作为参数,这就可以将 lambda 表达式赋予参数。 lambda 表达式赋予 Expression<T>类型的参数。C#编译器根据类型给 lambda表达式定义不同的行为。如果类型是 Express<T>,编译器就从 lambda 表达式中创建一个表达式树,并存储在程序集中。这样,就可以在运行期间分析表达式树,并进行优化,以便于查询数据源。

    http://www.cnblogs.com/mcgrady/archive/2014/05/17/3732694.html#_label5

    private static void DisplayTree(int indent, string message, Expression expression)
    {
        string output = String.Format("{0} {1} ! NodeType: {2}; Expr: {3} ",
              "".PadLeft(indent, '>'), message, expression.NodeType, expression);
    
        indent++;
        switch (expression.NodeType)
        {
            case ExpressionType.Lambda:
                Console.WriteLine(output);
                LambdaExpression lambdaExpr = (LambdaExpression)expression;
                foreach (var parameter in lambdaExpr.Parameters)
                {
                    DisplayTree(indent, "Parameter", parameter);
                }
                DisplayTree(indent, "Body", lambdaExpr.Body);
                break;
            case ExpressionType.Constant:
                ConstantExpression constExpr = (ConstantExpression)expression;
                Console.WriteLine("{0} Const Value: {1}", output, constExpr.Value);
                break;
            case ExpressionType.Parameter:
                ParameterExpression paramExpr = (ParameterExpression)expression;
                Console.WriteLine("{0} Param Type: {1}", output, paramExpr.Type.Name);
                break;
            case ExpressionType.Equal:
            case ExpressionType.AndAlso:
            case ExpressionType.GreaterThan:
                BinaryExpression binExpr = (BinaryExpression)expression;
                if (binExpr.Method != null)
                {
                    Console.WriteLine("{0} Method: {1}", output, binExpr.Method.Name);
                }
                else
                {
                    Console.WriteLine(output);
                }
                DisplayTree(indent, "Left", binExpr.Left);
                DisplayTree(indent, "Right", binExpr.Right);
                break;
            case ExpressionType.MemberAccess:
                MemberExpression memberExpr = (MemberExpression)expression;
                Console.WriteLine("{0} Member Name: {1}, Type: {2}", output,
                   memberExpr.Member.Name, memberExpr.Type.Name);
                DisplayTree(indent, "Member Expr", memberExpr.Expression);
                break;
            default:
                Console.WriteLine();
                Console.WriteLine("{0} {1}", expression.NodeType, expression.Type.Name);
                break;
        }
    }
    
    
    static void Main()
    {
        Expression<Func<Racer, bool>> expression = r => r.Country == "Brazil" && r.Wins > 6;
        DisplayTree(0, "Lambda", expression);
    }

    LINQ提供程序

    LINQ提供程序为特定的数据源实现了标准的查询操作符。LINQ提供程序也许会实现比LINQ定义的更多扩展方法,但至少要实现标准操作符。LINQ to XML 如 Extensions 类定义 Elements()、Descendants() 和 Ancestors() 。

    LINQ提供程序的实现方案是根据名称空间和第一个参数的类型来选择的。如 LINQ to Objects 定义 Where() 方法的参数 和 在 LINQ to Entities 中定义的 Where() 的方法参数不同。

    重要的总结

     

  • 相关阅读:
    回流和重绘
    php 异常捕获的坑
    每周散记 20180806
    转: Linux mount/unmount命令
    python http 请求 响应 post表单提交
    每周散记 20180723
    优惠劵产品分析
    c++ 软件版本比较函数
    每周散记
    转: 系统问题排查思路
  • 原文地址:https://www.cnblogs.com/z888/p/5856320.html
Copyright © 2020-2023  润新知