<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          [C#.NET 拾遺補(bǔ)漏]08:強(qiáng)大的LINQ

          共 16630字,需瀏覽 34分鐘

           ·

          2020-09-01 10:16

          閱讀本文大概需要 13 分鐘。

          大家好,這是 [C#.NET 拾遺補(bǔ)漏] 系列的第 08 篇文章,今天講 C# 強(qiáng)大的 LINQ 查詢。LINQ 是我最喜歡的 C# 語言特性之一。

          LINQ 是?Language?INtegrated?Query 單詞的首字母縮寫,翻譯過來是語言集成查詢。它為查詢跨各種數(shù)據(jù)源和格式的數(shù)據(jù)提供了一致的模型,所以叫集成查詢。由于這種查詢并沒有制造新的語言而只是在現(xiàn)有的語言基礎(chǔ)上來實(shí)現(xiàn),所以叫語言集成查詢。

          一些基礎(chǔ)


          在 C# 中,從功能上 LINQ 可分為兩類:LINQ to Object 和 LINQ to XML;從語法上 LINQ 可以分為 LINQ to Object 和 LINQ 擴(kuò)展方法。大多數(shù) LINQ to Object 都可以用 LINQ 擴(kuò)展方法實(shí)現(xiàn)等同的效果,而且平時(shí)開發(fā)中用的最多的是 LINQ 擴(kuò)展方法。

          LINQ to Object 多用于映射數(shù)據(jù)庫的查詢,LINQ to XML 用于查詢 XML 元素?cái)?shù)據(jù)。使用 LINQ 查詢的前提是對(duì)象必須是一個(gè) IEnumerable 集合(注意,為了描述方便,本文說的集合都是指 IEnumerable 對(duì)象,包含字面上的 ICollection 對(duì)象)。另外,LINQ 查詢大多是都是鏈?zhǔn)讲樵儯床僮鞯臄?shù)據(jù)源是?IEnumerable?類型,返回的是?IEnumerable?類型。

          形如下面這樣的查詢就是 LINQ to Object:

          var list = from user in users
          where user.Name.Contains("Wang")
          select user.Id;

          等同于使用下面的 LINQ 擴(kuò)展方法:

          var list = users
          .Where(u => user.Name.Contains("Wang"))
          .Select(u => u.id);

          LINQ 查詢支持在語句中間根據(jù)需要定義變量,比如取出數(shù)組中平方值大于平均值的數(shù)字:

          int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
          var result = from number in numbers
          let average = numbers.Average()
          let squared = Math.Pow(number, 2)
          where squared > average
          select number;
          // 平均值為 4.5, result 為 { 3, 4, 5, 6, 7, 8, 9 }

          其中的 Select 方法接收的參數(shù)用的最多的是?Func,它還可以接收?Func?參數(shù),示例:

          var collectionWithRowNumber = collection.
          .Select((item, index) => new { Item = item, RowNumber =index })
          .ToList();

          再來看一下 LINQ to XML 的示例。假如我們有如下 XML 文件:


          <Employees>
          <Employee>
          <EmpId>1EmpId>
          <Name>LiamName>
          <Sex>Sex>
          Employee>
          <Employee>
          <EmpId>2EmpId>
          ...
          Employee>
          Employees>

          使用 LINQ to XML 查詢所有含有指定節(jié)點(diǎn)值的元素:

          XElement xelement = XElement.Load("Employees.xml");
          var els = from el in xelement.Elements("Employee")
          where (string)el.Element("Sex") == "Male"
          select el;

          等同于使用 LINQ 擴(kuò)展方法:

          var els = xelement.Elements("Employee")
          .Where(el => (string)el.Element("Sex") == "Male");

          LINQ to XML 操作 XML 非常方便和靈活,大家可以在具體使用的時(shí)候去探索,這里就不展開講了。

          LINQ 查詢有很多方法,由于篇幅原因,就不一一列舉演示了,這里只選取一些強(qiáng)大的查詢方法,這些方法若使用非 LINQ 來實(shí)現(xiàn)可能會(huì)比較麻煩。

          LINQ 之所以強(qiáng)大,是因?yàn)樗梢暂p松實(shí)現(xiàn)復(fù)雜的查詢,下面我們來總結(jié)一下 C# LINQ 的強(qiáng)大之處。

          Fist、Last 和 Single 等


          First、FirstOrDefault、Last、LastOrDefault、Single 和 SingleOrDefault 是快速查詢集合中的第一個(gè)或最后一個(gè)元素的方法。如果集合是空的,F(xiàn)ist、Last 和 Single 都會(huì)報(bào)錯(cuò),如果使其不報(bào)錯(cuò)而在空集合時(shí)使用默認(rèn)值可以使用 FirstOrDefault、LastOrDefault 和 SingleOrDefault。Single/SingleOrDefault 和其它方法的區(qū)別是,它限定查詢結(jié)果只有一個(gè)元素,如果查詢結(jié)果集合中包含多個(gè)元素時(shí)會(huì)報(bào)錯(cuò)。具體看下面幾個(gè)示例:

          new[] { "a", "b" }.First(x => x.Equals("b")); // 返回 ”b“
          new[] { "a", "b" }.First(x => x.Equals("c")); // 拋出 InvalidOperationException 異常
          new[] { "a", "b" }.FirstOrDefault(x => x.Equals("c")); // 返回 null

          new[] { "a", "b" }.Single(x => x.Equals("b")); // 返回 ”b“
          new[] { "a", "b" }.Single(x => x.Equals("c")); // 拋出 InvalidOperationException 異常
          new[] { "a", "b" }.SingleOrDefault(x => x.Equals("c")); // 返回 null
          new[] { "a", "a" }.Single(); // 拋出 InvalidOperationException 異常

          在實(shí)際應(yīng)用中,如果要確保查詢結(jié)果的唯一性(比如通過手機(jī)號(hào)查詢用戶),使用 Single/SingleOrDefaut,其它情況應(yīng)盡量使用 First/FirstOrDefault。雖然 FirstOrDefault 也可以根據(jù)條件判斷元素是否存在,但使用 Any 更高效。

          Except 取差集


          LINQ 的 Except 方法用來取差集,即取出集合中與另一個(gè)集合所有元素不同的元素。

          示例:

          int[] first = { 1, 2, 3, 4 };
          int[] second = { 0, 2, 3, 5 };
          IEnumerable<int> result = first.Except(second);
          // result = { 1, 4 }

          注意 Except 方法會(huì)去除重復(fù)元素:

          int[] second = { 0, 2, 3, 5 };
          int[] third = { 1, 1, 1, 2, 3, 4 };
          IEnumerable<int> result = third.Except(second);
          // result = { 1, 4 }

          對(duì)于簡單類型(int、float、string 等)使用 Except 很簡單,但對(duì)于自定義類型(或者叫復(fù)合類型,下同)的 Object 如何使用 Except 呢?此時(shí)需要將自定義類型實(shí)現(xiàn)IEquatable接口,示例:

          class?User : IEquatable
          {
          public?string Name { get; set; }

          public?bool?Equals(User other)
          {
          return Name == other.Name;
          }

          public?override?int?GetHashCode()
          {
          return Name?.GetHashCode() ?? 0;
          }
          }

          class?Program
          {
          static?void?Main(string[] args)
          {
          var list1 = new List
          {
          new User{ Name = "User1"},
          new User{ Name = "User2"},
          };

          var list2 = new List
          {
          new User{ Name = "User2"},
          new User{ Name = "User3"},
          };

          var result = list1.Except(list2);
          result.ForEach(u => Console.WriteLine(u.Name));
          // 輸出:User1
          }
          }

          SelectMany 集合降維


          SelectMany 可以把多維集合降維,比如把二維的集合平鋪成一個(gè)一維的集合。舉例:

          var collection = new?int[][]
          {
          new?int[] {1, 2, 3},
          new?int[] {4, 5, 6},
          };
          var result = collection.SelectMany(x => x);
          // result = [1, 2, 3, 4, 5, 6]

          再來舉個(gè)更貼合實(shí)際應(yīng)用的例子。例如有如下實(shí)體類(一個(gè)部門有多個(gè)員工):

          class?Department
          {
          public Employee[] Employees { get; set; }
          }

          class?Employee
          {
          public?string Name { get; set; }
          }

          此時(shí),我們擁有一個(gè)這樣的數(shù)據(jù)集合:

          var departments = new[]
          {
          new Department()
          {
          Employees = new []
          {
          new Employee { Name = "Bob" },
          new Employee { Name = "Jack" }
          }
          },
          new Department()
          {
          Employees = new []
          {
          new Employee { Name = "Jim" },
          new Employee { Name = "John" }
          }
          }
          };

          現(xiàn)在我們可以使用 SelectMany 把各部門的員工查詢到一個(gè)結(jié)果集中:

          var allEmployees = departments.SelectMany(x => x.Employees);
          foreach(var emp in allEmployees)
          {
          Console.WriteLine(emp.Name);
          }
          // 依次輸出:Bob Jack Jim John

          SelectMany 迪卡爾積運(yùn)算


          SelectMany 不光適用于單個(gè)包含多維集合對(duì)象的降維,也適用于多個(gè)集合之前的兩兩相互操作,比如進(jìn)行迪卡爾積運(yùn)算。比如我們有這樣兩個(gè)集合:

          var list1 = new List<string> { "a1", "a2" };
          var list2 = new List<string> { "b1", "b2", "b3" };

          現(xiàn)在我們需要把它進(jìn)行兩兩組合,使用普通的方法,我們需要用嵌套循環(huán)語句來實(shí)現(xiàn):

          var result = newList<string>();
          foreach (var s1 in list1)
          foreach (var s2 in list2)
          result.Add($"{s1}{s2}");
          // result = ["a1b1", "a1b2", "a1b3", "a2b1", "a2b2", "a2b3"]

          改用 SelectMany 實(shí)現(xiàn):

          var result = list1.SelectMany(x => list2.Select(y => $"{x}{y}"));
          // result = ["a1b1", "a1b2", "a1b3", "a2b1", "a2b2", "a2b3"]

          具有挑戰(zhàn)性的問題來了,如何對(duì) N 個(gè)集合進(jìn)行迪卡爾積運(yùn)算呢,比如有這樣的集合數(shù)據(jù):

          var arrList = new List<string[]>
          {
          new?string[] { "a1", "a2" },
          new?string[] { "b1", "b2", "b3" },
          new?string[] { "c1" },
          // ...
          };

          如何對(duì)上面的 arrList 中的各個(gè)集合進(jìn)行兩兩組合呢?在電商業(yè)務(wù)尤其是零售業(yè)務(wù)中的產(chǎn)品組合促銷中這種需求很常見。

          下面是一個(gè)使用 SelectMany 的實(shí)現(xiàn),需要用到遞歸:

          class?Program
          {
          static?void?Main(string[] args)
          {
          var arrList = new List<string[]>
          {
          new?string[] { "a1", "a2" },
          new?string[] { "b1", "b2", "b3" },
          new?string[] { "c1" },
          // ...
          };

          var result = Recursion(arrList, 0, new List<string>());
          result.ForEach(x => Console.WriteLine(x));
          }

          static List<string> Recursion(List<string[]> list, int start, List<string> result)
          {
          if (start >= list.Count)
          return result;

          if (result.Count == 0)
          result = list[start].ToList();
          else
          result = result.SelectMany(x => list[start].Select(y => x + y)).ToList();

          result = Recursion(list, start + 1, result);

          return result;
          }
          }

          輸出:

          a1b1c1
          a1b2c1
          a1b3c1
          a2b1c1
          a2b2c1
          a2b3c1

          類似這種集合的迪卡爾積運(yùn)算操作,也可以用 LINQ to Object 來代替 SelectMany 實(shí)現(xiàn):

          result = result.SelectMany(x => list[start].Select(y => x + y)).ToList();
          // 等同使用擴(kuò)展方法:
          result = (from a in result from b in list[start] select a + b).ToList();

          LINQ to Object 比擴(kuò)展方法看上去易讀性更好,但寫起來擴(kuò)展方法更方便。

          Aggregate 聚合


          Aggregate 擴(kuò)展方法可以對(duì)一個(gè)集合依次執(zhí)行類似累加器的操作,就像滾雪球一樣把數(shù)據(jù)逐步聚集在一起。比如實(shí)現(xiàn)從 1 加到 10,用 Aggregate 擴(kuò)展方法就很方便:

          int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
          int sum = numbers.Aggregate((prevSum, current) => prevSum + current);
          // sum = 55

          我們來解析一下它的執(zhí)行步驟

          • 第一步,prevSum 取第一個(gè)元素的值,即 prevSum = 1

          • 第二步,把第一步得到的 prevSum 的值加上第二個(gè)元素,即 prevSum = prevSum + 2

          • 依此類推,第 i 步把第 i-1 得到的 prevSum 加上第 i 個(gè)元素

          再來看一個(gè)字符串的例子加深理解:

          string[] stringList = { "Hello", "World", "!" };
          string joinedString = stringList.Aggregate((prev, current) => prev + " " + current);
          // joinedString = "Hello World !"

          Aggregate 還有一個(gè)重載方法,可以指定累加器的初始值。我們來看一個(gè)比較綜合的復(fù)雜例子。假如我們有如下 1-12 的一個(gè)數(shù)字集合:

          var items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

          現(xiàn)在我們想做如下計(jì)算:

          • 計(jì)算集合元素的總數(shù)個(gè)數(shù)

          • 計(jì)算值為奇數(shù)的元素個(gè)數(shù)

          • 收集每第 4 個(gè)元素

          當(dāng)然通過普通的循環(huán)遍歷也可以實(shí)現(xiàn)這三個(gè)計(jì)算,但使用 Aggregate 會(huì)更簡潔,下面是 Aggregate 的實(shí)現(xiàn):

          var result = items.Aggregate(new { Total = 0, Even = 0, FourthItems = new List<int>() },
          (accum, item) =>
          new
          {
          Total = accum.Total + 1,
          Even = accum.Even + (item % 2 == 0 ? 1 : 0),
          FourthItems = (accum.Total + 1) % 4 == 0 ? new List<int>(accum.FourthItems) { item } : accum.FourthItems
          }
          );

          // result:
          // Total = 12
          // Even = 6
          // FourthItems = [4, 8, 12]

          這里為了簡單起見使用匿名類型作為累加器的初始值,由于匿名類型的屬性是只讀的,所以在累加的過程都 new 了一個(gè)新對(duì)象。如果初始值使用的是自定義類型,那累加時(shí)就不需 new 新對(duì)象了。

          Jion 關(guān)聯(lián)查詢


          和 SQL 查詢一樣,LINQ 同樣支持 Inner Join、Left Join、Right Join、Cross Join 和 Full Outer Join,有時(shí)候你可能看到不同的寫法,其實(shí)是同一個(gè)意思,比如 Left Outer Join 就是 Left Join,Join 是 Inner Join 省略了 Inner 等。

          假設(shè)我們有下面兩個(gè)集合,分別表示左邊的數(shù)據(jù)和右邊的數(shù)據(jù)。

          var first = new List<string>() { "a","b","c" }; // 左邊
          var second = new List<string>() { "a", "c", "d" }; // 右邊

          下面以此數(shù)據(jù)為例來演示各種關(guān)聯(lián)查詢。

          Inner Join

          var result = from f in first
          join s in second on f equals s
          select?new { f, s };
          // 等同使用擴(kuò)展方法:
          var result = first.Join(second,
          f => f,
          s => s,
          (f, s) => new { f, s });

          // result: {"a","a"}
          // {"c","c"}

          Left Join

          var result = from f in first
          join s in second on f equals s into temp
          from t in temp.DefaultIfEmpty()
          select?new { First = f, Second = t };
          // 或者:
          var result = from f in first
          from s in second.Where(x => x == f).DefaultIfEmpty()
          select?new { First = f, Second = s };

          // 等同使用擴(kuò)展方法:
          var result = first.GroupJoin(second,
          f => f,
          s => s,
          (f, s) => new { First = f, Second = s })
          .SelectMany(temp => temp.Second.DefaultIfEmpty(),
          (f, s) => new { First = f.First, Second = s });

          // result: {"a","a"}
          // {"b", null}
          // {"c","c"}

          Right Join

          var result = from s in second
          join f in first on s equals f into temp
          from t in temp.DefaultIfEmpty()
          select?new { First = t, Second = s };
          // 其它和 Left Join 類似

          // result: {"a","a"}
          // {"c","c"}
          // {null,"d"}

          Cross Join

          var result = from f in first
          from s in second
          select?new { f, s };

          // result: {"a","a"}
          // {"a","c"}
          // {"a","d"}
          // {"b","a"}
          // {"b","c"}
          // {"b","d"}
          // {"c","a"}
          // {"c","c"}
          // {"c","d"}

          Full Outer Join

          var leftJoin = from f in first
          join s in second on f equals s into temp
          from t in temp.DefaultIfEmpty()
          select?new { First = f, Second = t };
          var rightJoin = from s in second
          join f in first on s equals f into temp
          from t in temp.DefaultIfEmpty()
          select?new { First = t, Second = s };
          var fullOuterJoin = leftJoin.Union(rightJoin);

          根據(jù)多個(gè)鍵關(guān)聯(lián)

          在 SQL 中,表與表進(jìn)行關(guān)聯(lián)查詢時(shí) on 條件可以指定多個(gè)鍵的邏輯判斷,用 and 或 or 連接。但 C# 的 LINQ 不支持 and 關(guān)鍵字,若要根據(jù)多鍵關(guān)聯(lián),需要把要關(guān)聯(lián)的鍵值分別以相同的屬性名放到匿名對(duì)象中,然后使用 equals 比較兩個(gè)匿名對(duì)象是否相等。示例:

          var stringProps = typeof(string).GetProperties();
          var builderProps = typeof(StringBuilder).GetProperties();
          var query =
          from s in stringProps
          join b in builderProps
          on?new { s.Name, s.PropertyType } equals?new { b.Name, b.PropertyType }
          select?new
          {
          s.Name,
          s.PropertyType
          };

          以上均使用兩個(gè)集合做為示例,LINQ 關(guān)聯(lián)查詢也支持多個(gè)集合關(guān)聯(lián),就像 SQL 的多表關(guān)聯(lián),只需往后繼續(xù)追加 join 操作即可,不再累述。

          LINQ 關(guān)聯(lián)查與 SQL 相似,但使用上有很大區(qū)別。LINQ 關(guān)聯(lián)查詢的用法有很多,也很靈活,不用刻意去記住它們,只要熟悉簡單常用的,其它的在實(shí)際用到的時(shí)候再查詢相關(guān)文檔。

          Skip & Take 分頁


          Skip 擴(kuò)展方法用來跳過從起始位置開始的指定數(shù)量的元素讀取集合;Take 擴(kuò)展方法用來從集合中只讀取指定數(shù)量的元素。

          var values = new[] { 5, 4, 3, 2, 1 };
          var skipTwo = values.Skip(2); // { 3, 2, 1 }
          var takeThree = values.Take(3); // { 5, 4, 3 }
          var skipOneTakeTwo = values.Skip(1).Take(2); // { 4, 3 }

          Skip 與 Take 兩個(gè)方法結(jié)合即可實(shí)現(xiàn)我們常見的分頁查詢:

          public IEnumerable GetPage(this IEnumerable collection, int pageNumber, int pageSize)
          {
          int startIndex = (pageNumber - 1) * pageSize;
          return collection.Skip(startIndex).Take(pageSize);
          }

          使用過 EF (Core) 的同學(xué)一定很熟悉。

          另外,還有?SkipWhile?和?TakeWhile?擴(kuò)展方法,它與 Skip 和 Take 不同的是,它們的參數(shù)是具體的條件。SkipWhile 從起始位置開始忽略元素,直到匹配到符合條件的元素停止忽略,往后就是要查詢的結(jié)果;TakeWhile 從起始位置開始讀取符合條件的元素,一旦遇到不符合條件的就停止讀取,即使后面還有符合條件的也不再讀取。示例:

          SkipWhile:

          int[] list = { 42, 42, 6, 6, 6, 42 };
          var result = list.SkipWhile(i => i == 42);
          // result: 6, 6, 6, 42

          TakeWhile:

          int[] list = { 1, 10, 40, 50, 44, 70, 4 };
          var result = list.TakeWhile(item => item < 50).ToList();
          // result = { 1, 10, 40 }

          Zip 拉鏈


          Zip 擴(kuò)展方法操作的對(duì)象是兩個(gè)集合,它就像拉鏈一樣,根據(jù)位置將兩個(gè)系列中的每個(gè)元素依次配對(duì)在一起。其接收的參數(shù)是一個(gè) Func 實(shí)例,該 Func 實(shí)例允許我們成對(duì)在處理兩個(gè)集合中的元素。如果兩個(gè)集合中的元素個(gè)數(shù)不相等,那么多出來的將會(huì)被忽略。

          示例:

          int[] numbers = { 3, 5, 7 };
          string[] words = { "three", "five", "seven", "ignored" };
          IEnumerable<string> zip = numbers.Zip(words, (n, w) => n + "=" + w);

          foreach (string s in zip)
          {
          Console.WriteLine(s);
          }

          輸出:

          3=three
          5=five
          7=seven

          OfType 和 Cast 類型過濾與轉(zhuǎn)換


          OfType 用于篩選集合中指定類型的元素,Cast 可以把集合轉(zhuǎn)換為指定類型,但要求源類型必須可以隱式轉(zhuǎn)換為目標(biāo)類型。假如有如下數(shù)據(jù):

          interface?IFoo { }
          class?Foo : IFoo { }
          class?Bar : IFoo { }

          var item0 = new Foo();
          var item1 = new Foo();
          var item2 = new Bar();
          var item3 = new Bar();
          var collection = new IFoo[] { item0, item1, item2, item3 };

          OfType 示例:

          var foos = collection.OfType(); // result: item0, item1
          var bars = collection.OfType(); // result: item2, item3
          var foosAndBars = collection.OfType(); // result: item0, item1, item2, item3

          // 等同于使用 Where
          var foos = collection.Where(item => item is Foo); // result: item0, item1
          var bars = collection.Where(item => item is Bar); // result: item2, item3

          Cast 示例:

          var bars = collection.Cast();  // InvalidCastException 異常
          var foos = collection.Cast(); // InvalidCastException 異常
          var foosAndBars = collection.Cast(); // OK

          ToLookup 索引式查找


          ToLookup 擴(kuò)展方法返回的是可索引查找的數(shù)據(jù)結(jié)構(gòu),它是一個(gè) ILookup 實(shí)例,所有元素根據(jù)指定的鍵進(jìn)行分組并可以按鍵進(jìn)行索引。這樣說有點(diǎn)抽象,來看具體示例:

          string[] array = { "one", "two", "three" };
          // 根據(jù)元素字符串長度創(chuàng)建一個(gè)查找對(duì)象
          var lookup = array.ToLookup(item => item.Length);

          // 查找字符串長度為 3 的元素
          var result = lookup[3];
          // result: one,two

          再來一個(gè)示例:

          int[] array = { 1,2,3,4,5,6,7,8 };
          // 創(chuàng)建一個(gè)奇偶查找(鍵為 0 和 1)
          var lookup = array.ToLookup(item => item % 2);

          // 查找偶數(shù)
          var even = lookup[0];
          // even: 2,4,6,8

          // 查找奇數(shù)
          var odd = lookup[1];
          // odd: 1,3,5,7

          Distinct 去重


          Distinct 方法用來去除重復(fù)項(xiàng),這個(gè)容易理解。示例:

          int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };
          var distinct = array.Distinct();
          // distinct = { 1, 2, 3, 4, 5 }

          簡單類型的集合調(diào)用 Distinct 方法使用的是默認(rèn)的比較器,Distinct 方法用此比較器來判斷元素是否與其它元素重復(fù),但對(duì)于自定義類型要實(shí)現(xiàn)去重則需要自定義比較器。示例:

          public?class?IdEqualityComparer : IEqualityComparer
          {
          public?bool?Equals(Person x, Person y) => x.Id == y.Id;
          public?int?GetHashCode(Person p) => p.Id;
          }

          public?class?Person
          {
          public?int Id { get; set; }
          public?string Name { get; set; }
          }

          class?Program
          {
          static?void?Main(string[] args)
          {
          var people = new List();
          var distinct = people.Distinct(new IdEqualityComparer());
          }
          }

          ToDictionary 字典轉(zhuǎn)換


          ToDictionary 擴(kuò)展方法可以把集合?IEnumerable?轉(zhuǎn)換為?Dictionary?結(jié)構(gòu)的字典,它接收一個(gè)?Func?參數(shù)用來返回每個(gè)元素指定的鍵與值。示例:

          IEnumerable users = GetUsers();
          Dictionary<int, User> usersById = users.ToDictionary(x => x.Id);

          如果不用 ToDictionary,你需要這樣寫:

          IEnumerable users = GetUsers();
          Dictionary<int, User> usersById = new Dictionary<int, User>();
          foreach (User u in users)
          {
          usersById.Add(u.Id, u);
          }

          上面 ToDictionary 返回的字典數(shù)據(jù)中的值是整個(gè)元素,你也可以通過它的第二個(gè)參數(shù)來自定義字典的值。示例:

          Dictionary<int, string> userNamesById = users.ToDictionary(x => x.Id, x => x.Name);

          你也可以為轉(zhuǎn)換的字典指定其鍵是否區(qū)分大小寫,即自定義字典的 IComparer,示例:

          Dictionary<string, User> usersByCaseInsenstiveName = users.ToDictionary(x =>x.Name,
          StringComparer.InvariantCultureIgnoreCase);

          var user1 =usersByCaseInsenstiveName["liam"];
          var user2 =usersByCaseInsenstiveName["LIAM"];
          user1 == user2; // true

          注意,字典類型要求所有鍵不能重復(fù),所以在使用 ToDictionary 方法時(shí)要確保作為字典的鍵的元素屬性不能有重復(fù)值,否則會(huì)拋出異常。

          其它常見擴(kuò)展方法


          LINQ 還有很多其它常見的擴(kuò)展方法,大家在平時(shí)應(yīng)該用的比較多,比如 Where、Any、All 等,這里也選幾個(gè)簡單舉例介紹一下。

          Range 和 Repeat

          Range 和 Repeat 用于生成簡單的數(shù)字或字符串系列。示例:

          // 生成 1-100 的數(shù)字,即結(jié)果為 [1, 2, ..., 99, 100]
          var range = Enumerable.Range(1, 100);

          // 生成三個(gè)重復(fù)的字符串“a”,即結(jié)果為 ["a", "a", "a"]
          var repeatedValues = Enumerable.Repeat("a", 3);

          Any 和 All

          Any 用來判斷集合中是否存在任一一個(gè)元素符合條件,All 用來判斷集合中是否所有元素符合條件。示例:

          var numbers = new?int[] {1, 2, 3, 4, 5 };
          bool result = numbers.Any(); // true
          bool result = numbers.Any(x => x == 6); // false
          bool result = numbers.All(x => x > 0); // true
          bool result = numbers.All(x => x > 1); // false

          Concat 和 Union

          Concat 用來拼接兩個(gè)集合,不會(huì)去除重復(fù)元素,示例:

          List<int> foo = newList<int> { 1, 2, 3 };
          List<int> bar = newList<int> { 3, 4, 5 };
          // 通過 Enumerable 類的靜態(tài)方法
          var result = Enumerable.Concat(foo, bar).ToList(); // 1,2,3,3,4,5
          // 通過擴(kuò)展方法
          var result = foo.Concat(bar).ToList(); // 1,2,3,3,4,5

          Union 也是用來拼接兩個(gè)集合,與 Concat 不同的是,它會(huì)去除重復(fù)項(xiàng),示例:

          var result = foo.Union(bar); // 1,2,3,4,5

          GroupBy 分組

          GroupBy 擴(kuò)展方法用來對(duì)集合進(jìn)行分組,下面是一個(gè)根據(jù)奇偶進(jìn)行分組的示例:

          var list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
          var grouped = list.GroupBy(x => x % 2 == 0);
          // grouped: [1, 3, 5, 7, 9] 和 [2, 4, 6, 8]

          還可以根據(jù)指定屬性進(jìn)行分組:

          public?class?Person
          {
          public?int Age { get; set; }
          public?string Name { get; set; }
          }

          var people = new List();
          var query = people
          .GroupBy(x => x.Age)
          .Select(g => { Age = g.Key, Count = g.Count() });

          DefaultIfEmpty 空替換

          在上面的關(guān)聯(lián)查詢中我們使用了 DefaultIfEmpty 擴(kuò)展方法,它表示在沒有查詢到指定條件的元素時(shí)使用元素的默認(rèn)值代替。其實(shí) DefaultIfEmpty 還可以指定其它的默認(rèn)值,示例:

          var chars = new List<string>() { "a", "b", "c", "d" };
          chars.Where(s => s.Length > 1).DefaultIfEmpty().First(); // 返回 null
          chars.DefaultIfEmpty("N/A").FirstOrDefault(); // 返回 "a"
          chars.Where(s => s.Length > 1).DefaultIfEmpty("N/A").FirstOrDefault(); // 返回 "N/A"

          SequenceEqual 集合相等

          SequenceEqual 擴(kuò)展方法用于比較集合系列各個(gè)相同位置的元素是否相等。示例:

          int[] a = new?int[] {1, 2, 3};
          int[] b = new?int[] {1, 2, 3};
          int[] c = new?int[] {1, 3, 2};

          bool result1 = a.SequenceEqual(b); // true
          bool result2 = a.SequenceEqual(c); // false

          最后


          還有一些常用和簡單的擴(kuò)展方法就不舉例了,比如 OrderBy(排序)、Sum(求和)、Count(計(jì)數(shù))、Reverse(反轉(zhuǎn))等,同時(shí)歡迎大家補(bǔ)充本文遺漏的強(qiáng)大或好用的 LINQ 語法糖。

          瀏覽 31
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  簧片视频在线观看免费 | 国产成人精品小电影 | 日韩操逼大片 | 欧洲久久电影 | 亚洲黄页视频 |