先構造幾個集合,方便後面使用
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
List<string> names = new List<string> { "Alice", "Bob", "Charlie", "David", "Eve" };
var people = new List<Person>
{
new Person { Name = "Alice", Age = 30, City = "Beijing" },
new Person { Name = "Bob", Age = 25, City = "Shanghai" },
new Person { Name = "Charlie", Age = 35, City = "Beijing" },
new Person { Name = "David", Age = 28, City = "Guangzhou" },
new Person { Name = "Eve", Age = 22, City = "Shanghai" },
};
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }
}
篩選
Where過濾序列,返回滿足條件的所有元素
Where(Func<TSource, bool> predicate)
Where(Func<TSource, int, bool> predicate) — 第二個參數是元素的索引
// 年齡 >= 30 的人
var age30Plus = people.Where(p => p.Age >= 30);
// 使用索引:選出索引為偶數的位置上的名字
var evenIndexNames = names.Where((name, idx) => idx ==2);
投影
Select
作用:把序列的每個元素映射成新形式(投影)。 常見重載:
Select(Func<TSource, TResult> selector)Select(Func<TSource, int, TResult> selector)— 可使用元素索引
// 只投影名字和城市(匿名類型)
var nameAndCity = people.Select(p => new { p.Name, p.City });
// 使用索引:給名字附加序號
var numberedNames = names.Select((n, i) => $"{i + 1}. {n}");
SelectMany
作用:把每個元素映射到一個序列,然後將所有子序列“扁平化”為一個序列(常用於一對多關係)。 常見重載:
SelectMany(Func<TSource, IEnumerable<TResult>> selector)SelectMany(Func<TSource, int, IEnumerable<TResult>> selector)(帶索引)SelectMany(selector, resultSelector)— 可以在扁平化的同時生成複合結果
和Select的區別
|
Select
|
SelectMany
|
|
我給你每個元素轉換一下(不會打散內部集合) |
我給你每個元素轉換一下,順便把內部集合展開成一個大列表 |
//先構建個數據,每個人對應一個數組,可以居住多個城市
class Person
{
public string Name { get; set; }
public List<string> Cities { get; set; }
}
var people = new List<Person>
{
new Person { Name = "Alice", Cities = new List<string> { "Beijing", "Shanghai" }},
new Person { Name = "Bob", Cities = new List<string> { "Guangzhou" }},
new Person { Name = "Charlie", Cities = new List<string> { "Beijing", "Shenzhen", "Hangzhou" }}
};
- Select
people.Select(p =>p.Cities );
- SelectMany
people.SelectMany(p => p.Cities );
還可以用帶結果選擇器的高級用法,先把每個外層元素映射到一個子序列,然後把所有子序列“扁平化”成一個一維序列。當你需要在“展開”的同時把外層元素和內層元素組合成新的結果時,就用 帶結果選擇器(result selector) 的重載。
people.SelectMany(p => p.Cities, (person, city) => new {person.Name,city});這個案例,不僅有城市序列,還有對應的Name序列。
排序
OrderBy升序
people.OrderBy(p => p.Age)
OrderByDescending降序
people.OrderByDescending(p => p.Name)
ThenBy / ThenByDescending在主排序的基礎上做次級排序(用於多鍵排序)
// 先按城市排序,再按年齡降序
var cityThenAge = people.OrderBy(p => p.City).ThenByDescending(p => p.Age)
分組
GroupBy
作用:按照鍵把序列分成多個組,每組是 IGrouping<TKey, TElement>。 常見重載:
GroupBy(Func<TSource, TKey> keySelector)GroupBy(keySelector, elementSelector)(將元素投影成其他類型)GroupBy(keySelector, elementSelector, resultSelector)(可以直接構造自定義結果)- 可帶
IEqualityComparer<TKey>
// 按城市分組
var byCity = people.GroupBy(p => p.City).Dump();
// 如果只想統計每個城市的人名列表
var cityToNames = people.GroupBy(
p => p.City,
p => p.Name, // elementSelector:組內只保留名字
(city, namesInCity) => new { City = city, Names = namesInCity.ToList() } // resultSelector
);
集合操作
Distinct返回序列中不重複的元素
var numsWithDup = new List<int> { 1, 2, 2, 3, 3, 3 };
var distinctNums = numsWithDup.Distinct().Dump();
Union返回兩個序列的並集(去重)
var a = new List<int> { 1, 2, 3 };
var b = new List<int> { 3, 4, 5 };
var union = a.Union(b).Dump(); // 1,2,3,4,5
Intersect返回兩個序列的交集(共有元素)
var intersect = a.Intersect(b).Dump(); // 3
Except返回第一個序列中存在但第二個序列中不存在的元素(差集)
分區
Skip跳過前 n 個元素
var skip2 = numbers.Skip(2).Dump(); // 3,4,5,6
Take 取前 n 個元素
var first3 = numbers.Take(3).Dump(); // 1,2,3
SkipWhile根據條件從序列開頭開始判斷,直到條件不再滿足為止,序列開頭跳過元素,一旦 predicate 返回 false 則後續元素都保留。
var skipWhileLess4 = numbers.SkipWhile(n => n < 4).Dump(); // 4,5,6
TakeWhile根據條件從序列開頭開始判斷,直到條件不再滿足為止,從序列開頭取元素,一旦 predicate 返回 false 則停止(剩餘元素丟棄)
var takeWhileLess4 = numbers.TakeWhile(n => n < 4).Dump(); // 1,2,3
量詞
Any判斷序列中是否存在至少一個滿足條件的元素
bool anyAdults = people.Any(p => p.Age >= 18).Dump();
bool hasElements = numbers.Any().Dump(); //判斷序列是否為空
All判斷序列中的所有元素是否都滿足條件
bool allAbove20 = people.All(p => p.Age > 20).Dump();
注意:空序列對 All 返回 true(邏輯上所有元素都滿足)。
var ll = new List<Person>();
bool allAbove20 = ll.All(p=>p.Age>20).Dump(); //true
Contains 判斷序列是否包含某個具體值
bool hasThree = numbers.Contains(3);
元素操作
First
作用:返回序列中的第一個元素或第一個滿足條件的元素。
First():如果找不到元素會拋出InvalidOperationException。FirstOrDefault():找不到則返回類型默認值(引用類型為null,值類型為default(T))。
FirstOrDefault
Last / LastOrDefault,與 First 類似,但返回序列中的最後一個元素(或滿足條件的最後一個)
Single / SingleOrDefault
作用:要求序列恰好包含一個元素(或恰好有一個滿足條件的元素)。
Single():如果序列為空或包含多個元素會拋異常。SingleOrDefault():如果序列為空返回默認值;如果包含多個元素仍會拋異常。
聚合
Count返回序列元素數量。可帶謂詞計算滿足條件的數量。
int beijingCount = people.Count(p => p.City == "Beijing")
Sum對數值序列求和,或投影后求和
int sumNums = numbers.Sum();
int totalAge = people.Sum(p => p.Age);
Max / Min獲取序列的最大/最小值,或按投影選擇最大/最小。
int maxNum = numbers.Max();
int oldest = people.Max(p => p.Age);
string maxName = names.Max(); // 字符串按字典序
Average計算平均值(適用於數值序列)
double avgAge = people.Average(p => p.Age);
double avgNum = numbers.Average();
Aggregate對集合元素進行累積/摺疊運算,可以自定義累加邏輯
//1累加求和
int sum = numbers.Aggregate((acc, x) => acc + x);
//帶初始值求和(安全處理空集合)
int sum2 = numbers.Aggregate(0, (acc, x) => acc + x);
//拼接字符串
names.Aggregate((acc, word) => acc + "-" + word);//Alice-Bob-Charlie-David-Eve
//生成字典(統計字母長度)
var lengthDict = words.Aggregate(
new Dictionary<string, int>(),
(dict, word) =>
{
dict[word] = word.Length;
return dict;
}
);
//初始值 + 最終投影(轉換類型)
numbers.Aggregate("總數:",(acc,x)=>acc+" "+x,ac=>ac+"結束"); //總數: 1 2 3 4 5 6結束
轉換
ToList / ToArray將 IEnumerable<T> 枚舉序列轉換為 List<T> 或 T[],觸發延遲執行並把結果緩存到集合中。
var list = people.Where(p => p.Age > 25).ToList();
var arr = names.Where(n => n.StartsWith("A")).ToArray();
ToDictionary將序列轉換為字典
var dictByName = people.ToDictionary(p => p.Name);
// 以 City 為鍵,保存該城市第一個人的年齡(注意鍵衝突需謹慎)
var cityToFirstAge = people.ToDictionary(p => p.Name, p => p.Age).Dump();
OfType過濾並轉換序列中可以轉換為 TResult 的元素(安全地篩選類型)
IEnumerable<object> mixed = new object[] { 1, "two", 3, "four" };
var strings = mixed.OfType<string>().Dump(); // "two", "four"
var ints = mixed.OfType<int>().Dump(); // 1,3
Cast嘗試把所有元素強制轉換為 TResult(如果某個元素無法轉換會拋異常)。
var castToInt = mixed.Cast<int>().Dump(); // 會在遇到 "two" 時拋異常