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\t   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\t\t   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() 的方法參數不同。

 

重要的總結

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM