C# 當中 LINQ 的常規用法(Lambda 方式)


僅以本篇博文記錄 LINQ 相關操作的基本知識,原型參考自 MSDN 相關知識,中間加以自己的理解與 DEMO。

1. IEnuemrable<T>.Select()

Select 方法比較簡單,就是在原有序列的基礎上,為每個元素建立一個新的輸出形式(類型)。

標准用法如下:

public class TestClass
{
	public string Name { get; set; }

	public int Age { get; set; }
}

void Main()
{
	var testList = new List<TestClass>
	{
		new TestClass{Name = "張三",Age = 18},
		new TestClass{Name="李四",Age = 32},
		new TestClass{Name="王二",Age = 24}
	};

	var selectResult = testList.Select(student => new
	{
		Name = student.Name,
		NewAge = student.Age + 20
	});

	foreach (var student in selectResult)
	{
		Console.WriteLine($"姓名:{student.Name},新的年齡:{student.NewAge}");
	}
}

輸出結果:

姓名:張三,新的年齡:38
姓名:李四,新的年齡:52
姓名:王二,新的年齡:44

這樣 newResult 的結果就是我們所投射出來新序列,同時 IEnumerbale<T>.Select() 也擁有 延遲執行 的特性,只會在我們需要用到的時候才會進行計算。

2. IEnumerable<T>.SelectMany()

SelectMany() 方法的作用則與 Select() 方法不同,SelectMany() 是用於將每個元素的子集合合並為一個新的集合。

標准用法如下:

void Main()
{
	var demoList = new List<Demo>()
	{
		new Demo(){Names = new List<string>{"a","b","c","d"}},
        new Demo(){Names = new List<string>{"e","f","g","h"}},
        new Demo(){Names = new List<string>{"i","j","k","l"}},
        new Demo(){Names = new List<string>{"m","n","o","p"}},
	};
	
	var selectResult = demoList.Select(item=>item.Names);
	Console.WriteLine("Select 操作的結果...");
	foreach(var selectItem in selectResult)
	{
		foreach(var value in selectItem)
		{
			Console.WriteLine($"Value:{value}");
		}
	}
	
	Console.WriteLine("================================");
	Console.WriteLine("SelectMany 操作的結果...");
	
	var selectManyResult = demoList.SelectMany(item=>item.Names);
	foreach(var selectManyItem in selectManyResult)
	{
		Console.WriteLine($"Value:{selectManyItem}");
	}
}

public class Demo
{
	public List<string> Names { get; set; }
}

在本例當中這兩個方法分別輸出的是 IEnumerable<List<string>>IEnumerable<string> ,這里就可以看出來 SelectionMany() 方法將子集合扁平化輸出成一個結果集。

輸出結果:

Select 操作的結果...
Value:a
Value:b
Value:c
Value:d
Value:e
Value:f
Value:g
Value:h
Value:i
Value:j
Value:k
Value:l
Value:m
Value:n
Value:o
Value:p
================================
SelectMany 操作的結果...
Value:a
Value:b
Value:c
Value:d
Value:e
Value:f
Value:g
Value:h
Value:i
Value:j
Value:k
Value:l
Value:m
Value:n
Value:o
Value:p

IEnumerable<T>.SelectMany() 還擁有另外一個重載方法,這個新的重載方法多了一個 resultSelector 參數。在這個委托當中,可以傳入 TSourceTCollection 讓我們將主表的數據與從表進行合並。

標准用法如下:

void Main()
{
	Store[] stores = new Store[]
	{
		new Store()
		{
			Name = "App Store",
			Products = new string[] {"iPhone 8", "iPhone 8s", "iPhone X"}
		},
		new Store()
		{
			Name = "Google Store",
			Products = new string[] {"Pixel", "Pixel 2"}
		}
	};

	var result = stores.SelectMany(store  => store.Products, (store, product) => new 
	{
		StoreName = store.Name,
		ProductName = product
	});
	
	foreach(var item in result)
	{
		Console.WriteLine($"商店名稱:{item.StoreName},產品名稱:{item.ProductName}");
	}
}

class Store
{
	public string Name { get; set; }
	public string[] Products { get; set; }
}

輸出結果:

商店名稱:App Store,產品名稱:iPhone 8
商店名稱:App Store,產品名稱:iPhone 8s
商店名稱:App Store,產品名稱:iPhone X
商店名稱:Google Store,產品名稱:Pixel
商店名稱:Google Store,產品名稱:Pixel 2

3. IEnuemrable<T>.Where()

IEnumerable<T>.Where(Func<T,bool>) 主要用於過濾序列當中需要的元素,與 Select() 一樣也是擁有 延遲執行 的特性。

標准用法:

void Main()
{
	var integers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	var result = integers.Where(x => x >= 5);

	foreach (var @int in result)
	{
		Console.WriteLine($"整形數據:{@int}");
	}
}

輸出結果:

整形數據:5
整形數據:6
整形數據:7
整形數據:8
整形數據:9

4. IEnuemrable<T>.OrderBy() 與 IEnuemrable<T>.OrderByDescending()

上述兩個方法主要用於針對序列進行排序操作,OrderBy() 方法是升序排列,而 OrderByDescending() 則是降序排列。

標准用法:

void Main()
{
	var integers = new[] { 3, 1, 2, 8, 5, 6, 7, 4, 9 };
	var orderByResult = integers.OrderBy(i=>i);

	Console.WriteLine("升序排列結果.");
	foreach (var @int in orderByResult)
	{
		Console.WriteLine($"整形數據:{@int}");
	}
	
	Console.WriteLine("降序排列結果.");
	var orderByDescResult = integers.OrderByDescending(i => i);
	foreach (var @int in orderByDescResult)
	{
		Console.WriteLine($"整形數據:{@int}");
	}
}

輸出結果:

升序排列結果.
整形數據:1
整形數據:2
整形數據:3
整形數據:4
整形數據:5
整形數據:6
整形數據:7
整形數據:8
整形數據:9
降序排列結果.
整形數據:9
整形數據:8
整形數據:7
整形數據:6
整形數據:5
整形數據:4
整形數據:3
整形數據:2
整形數據:1

除了上述的基本排序以外,有的時候我們可能會有幾個條件一起進行排序操作,例如先按照年齡排序,之后再按照成績排序。這個時候就可以使用到 IEnumerable<T>.ThenBy()IEnumerable<T>.ThenByDescending() 來繼續進行排序。

標准用法:

public class Student
{
	public string Name { get; set; }

	public int Age { get; set; }

	public int Score { get; set; }
}

void Main()
{
	// 初始化數據
	var students = new List<Student>
	{
		new Student{Name="張三",Age=14,Score=120},
		new Student{Name="李四",Age=17,Score=80},
		new Student{Name="王二",Age=11,Score=170},
		new Student{Name ="孫五",Age=21,Score=145}
	};

	// 首先按照成績降序排序,然后按照年齡升序排序
	Console.WriteLine("使用 Then XX 方法進行排序。");
	var result = students.OrderByDescending(student=>student.Score).ThenBy(student=>student.Age);
	// 輸出排序結果
	Output(result);
	
	// 如果不使用 ThenXX 進行排序,而直接使用 OrderXX 進行多個排序條件排序結果
	Console.WriteLine("沒有使用 Then XX 方法進行排序。");
	var newResult = students.OrderByDescending(student => student.Score).OrderBy(student => student.Age);
	Output(newResult);
	
	void Output(IEnumerable<Student> outputStudents)
	{
		foreach (var student in outputStudents)
		{
			Console.WriteLine($"學生名稱:{student.Name},學生年齡:{student.Age},學生成績:{student.Score}");
		}
	}
}

輸出結果:

使用 Then XX 方法進行排序。
學生名稱:王二,學生年齡:11,學生成績:170
學生名稱:孫五,學生年齡:21,學生成績:145
學生名稱:張三,學生年齡:14,學生成績:120
學生名稱:李四,學生年齡:17,學生成績:80
沒有使用 Then XX 方法進行排序。
學生名稱:王二,學生年齡:11,學生成績:170
學生名稱:張三,學生年齡:14,學生成績:120
學生名稱:李四,學生年齡:17,學生成績:80
學生名稱:孫五,學生年齡:21,學生成績:145

可以看到如果沒有使用 ThenBy() 方法排序的結果,第一個條件根本沒有起作用。所以在使用 LINQ 對序列進行排序的時候應該注意這一點,否則得到的結果可能與預期的不一樣。

5. IEnuemrable<T>.GroupBy()

IEnumerable<T>.GroupBy() 方法主要用於將序列按照指定的列進行分組,這樣我們就可以很方便對這些結果進行處理。

IEnumerable<T>.GroupBy() 方法一樣是擁有八種不同參數的重載,但按照工作方式來說只有四類,只是每一類都會有一個支持 comparer 比較器的重載。

GroupBy() 方法擁有 延遲執行 特性。

下面的例子列舉了不同重載的使用方法:

方法 1

public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector);

keySelector 指定的是分組所需要的的列/屬性,最后的結果會是一個分組結果的序列。這個序列的值是使用 TKeyTSource 組成的分組結果,TKey 指代的是用作分組的列/屬性,而 TSource 也是一個序列,存儲的是這個分組下的結果。

標准用法:

public class Student
{
    public string Name { get; set; }

    public bool Graduation { get; set; }

    public int Age { get; set; }

    public int Score { get; set; }

    public string City { get; set; }

    public override string ToString()
    {
        return $"姓名:{Name},年齡:{Age},分數:{Score},是否畢業:{Graduation},城市:{City}";
    }
}

class Program
{
    static void Main(string[] args)
    {
        var students = new List<Student>
        {
            new Student{Name = "張三",Age = 15,Score = 94,Graduation = true,City = "北京"},
            new Student{Name = "李四",Age = 17,Score = 47,Graduation = false,City = "北京"},
            new Student{Name = "王二",Age = 19,Score = 77,Graduation = false,City = "廣州"},
            new Student{Name = "孫五",Age = 14,Score = 14,Graduation = false,City = "上海"}
        };

        var groupByResult = students.GroupBy(x => x.City);
        foreach (var item in groupByResult)
        {
            Console.WriteLine($"分組城市:{item.Key}");
            foreach (var student in item)
            {
                Console.WriteLine(student.ToString());
            }
        }
    }
}

輸出結果:

分組城市:北京
姓名:張三,年齡:15,分數:94,是否畢業:True,城市:北京
姓名:李四,年齡:17,分數:47,是否畢業:False,城市:北京
分組城市:廣州
姓名:王二,年齡:19,分數:77,是否畢業:False,城市:廣州
分組城市:上海
姓名:孫五,年齡:14,分數:14,是否畢業:False,城市:上海

方法 2

public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TSource, TElement> elementSelector);

其第一個參數的意思與之前一樣,用於指定分組條件。第二個 elementSelector 參數則是與 Select() 方法類似,可以指定輸出的分組結果類型。

標准用法:

public class Student
{
    public string Name { get; set; }

    public bool Graduation { get; set; }

    public int Age { get; set; }

    public int Score { get; set; }

    public string City { get; set; }

    public override string ToString()
    {
        return $"姓名:{Name},年齡:{Age},分數:{Score},是否畢業:{Graduation},城市:{City}";
    }
}

class Program
{
    static void Main(string[] args)
    {
        var students = new List<Student>
        {
            new Student{Name = "張三",Age = 15,Score = 94,Graduation = true,City = "北京"},
            new Student{Name = "李四",Age = 17,Score = 47,Graduation = false,City = "北京"},
            new Student{Name = "王二",Age = 19,Score = 77,Graduation = false,City = "廣州"},
            new Student{Name = "孫五",Age = 14,Score = 14,Graduation = false,City = "上海"}
        };

        var groupByResult = students.GroupBy(x => x.City,student=>new{student.Name,student.City});
        foreach (var item in groupByResult)
        {
            Console.WriteLine($"分組城市:{item.Key}");
            foreach (var student in item)
            {
                Console.WriteLine($"姓名:{student.Name},城市:{student.City}");
            }
        }
    }
}

輸出結果:

標准用法:

分組城市:北京
姓名:張三,城市:北京
姓名:李四,城市:北京
分組城市:廣州
姓名:王二,城市:廣州
分組城市:上海
姓名:孫五,城市:上海

方法 3

public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TKey, IEnumerable<TSource>, TResult> resultSelector);

本方法重載與之前的方法不一樣,沒有了 elementSelector 參數,變成了 resultSelector 參數,並且其返回值也由 IEnumerable<IGrouping<TKey, TSource>> 變成了 IEnumerable<YResult>

在這個方法當中,我們只需要通過 resultSelector 委托即可指定需要輸出的數據類型,這里該委托的 TKey 參數是分組的列/屬性,而 IEnumerable<TSource> 則是每個分組結果的關聯序列。

標准用法:

public class Student
{
    public string Name { get; set; }

    public bool Graduation { get; set; }

    public int Age { get; set; }

    public int Score { get; set; }

    public string City { get; set; }

    public override string ToString()
    {
        return $"姓名:{Name},年齡:{Age},分數:{Score},是否畢業:{Graduation},城市:{City}";
    }
}

class Program
{
    static void Main(string[] args)
    {
        var students = new List<Student>
        {
            new Student{Name = "張三",Age = 15,Score = 94,Graduation = true,City = "北京"},
            new Student{Name = "李四",Age = 17,Score = 47,Graduation = false,City = "北京"},
            new Student{Name = "王二",Age = 19,Score = 77,Graduation = false,City = "廣州"},
            new Student{Name = "孫五",Age = 14,Score = 14,Graduation = false,City = "上海"}
        };

        var groupByResult = students.GroupBy(x => x.City, (key, enumerable) =>
        {
            return new
            {
                City = key,
                Avg = enumerable.Average(x => x.Score),
                Max = enumerable.Max(x => x.Score)
            };
        });

        foreach (var student in groupByResult)
        {
            Console.WriteLine($"城市:{student.City},平均分:{student.Avg},最高分:{student.Max}");
        }
    }
}

輸出結果:

城市:北京,平均分:70.5,最高分:94
城市:廣州,平均分:77,最高分:77
城市:上海,平均分:14,最高分:14

方法 4

public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    Func<TSource, TElement> elementSelector,
    Func<TKey, IEnumerable<TElement>, TResult> resultSelector);

最后一個方法與之前的方法一樣,不過多了 elementSelector,與第二個方法一樣,用於指定要選擇的屬性字段,排除其他字段。

標准用法:

public class Student
{
	public string Name { get; set; }

	public bool Graduation { get; set; }

	public int Age { get; set; }

	public int Score { get; set; }

	public string City { get; set; }

	public override string ToString()
	{
		return $"姓名:{Name},年齡:{Age},分數:{Score},是否畢業:{Graduation},城市:{City}";
	}
}

class Program
{
	static void Main(string[] args)
	{
		var students = new List<Student>
		{
			new Student{Name = "張三",Age = 15,Score = 94,Graduation = true,City = "北京"},
			new Student{Name = "李四",Age = 17,Score = 47,Graduation = false,City = "北京"},
			new Student{Name = "王二",Age = 19,Score = 77,Graduation = false,City = "廣州"},
			new Student{Name = "孫五",Age = 14,Score = 14,Graduation = false,City = "上海"}
		};

		var groupByResult = students.GroupBy(x => x.City, x=>new{x.Name,x.City,x.Age,x.Score},(key, enumerable) =>
		{
			return new
			{
				City = key,
				Avg = enumerable.Average(x => x.Score),
				Max = enumerable.Max(x => x.Score),
				AvgAge = enumerable.Average(x=>x.Age)
			};
		});

		foreach (var student in groupByResult)
		{
			Console.WriteLine($"城市:{student.City},平均分:{student.Avg},最高分:{student.Max},平均年齡:{student.AvgAge}");
		}
	}
}

輸出結果:

城市:北京,平均分:70.5,最高分:94,平均年齡:16
城市:廣州,平均分:77,最高分:77,平均年齡:19
城市:上海,平均分:14,最高分:14,平均年齡:14

6. IEnuemrable<T>.Join()

IEnumerable<T>.Join() 方法一般用來連接兩個不同的表進行關聯查詢,其方法定義大概如下。

public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, TInner, TResult> resultSelector);

假設我們擁有兩張表,分別是員工表與聯系方式表,都是通過一個 Id 相互關聯。如果我們需要找到某個員工的聯系方式,那么員工是主表則為 inner,而聯系方式是從表,則為 outer 。另外兩個 Selector 分別用於指定各自的關聯屬性。

IEnumerable<T>.Join() 擁有 延遲執行 的特性,並且是內連查詢。

標准用法:

public class Person
{
    public int Id { get; set; }

    public string Name { get; set; }
}

public class ContactInformation
{
    public int PersonId { get; set; }

    public string PhoneNumber { get; set; }

    public string Address { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        // 初始化員工信息
        var persons = new[] {new Person {Id = 1, Name = "張三"}, new Person {Id = 2, Name = "李四"}, new Person {Id = 3, Name = "王二"}};
        
        // 初始化員工的聯系方式
        var contactInfos = new[]
        {
            new ContactInformation {PersonId = 1, Address = "上海", PhoneNumber = "001-00001"},
            new ContactInformation {PersonId = 2, Address = "北京", PhoneNumber = "002-00002"},
            new ContactInformation {PersonId = 3, Address = "廣州", PhoneNumber = "003-00003"},
            new ContactInformation {PersonId = 1, Address = "深圳", PhoneNumber = "004-00004"}
        };

        var joinResult = contactInfos.Join(persons, contactInfo => contactInfo.PersonId, person => person.Id, (information, person) => new
        {
            Name = person.Name,
            PhoneNumber = information.PhoneNumber,
            Address = information.Address
        });

        foreach (var item in joinResult)
        {
            Console.WriteLine($"姓名:{item.Name},電話:{item.PhoneNumber},地址:{item.Address}");
        }
    }
}

輸出結果:

姓名:張三,電話:001-00001,地址:上海
姓名:李四,電話:002-00002,地址:北京
姓名:王二,電話:003-00003,地址:廣州
姓名:張三,電話:004-00004,地址:深圳

7. IEnuemrable<T>.GroupJoin()

以上一個 IEnumerable<T>.Join() 的方法結果為例,可以看到某個員工的聯系方式數據有兩筆甚至以上的時候就會擁有重復數據,這個時候就需要使用到 GroupJoin() 來進行處理。

public static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter, IEnumerable<TInner>, TResult> resultSelector);

GroupJoin() 方法與 Join() 類似,都是根據 outer 的關聯字段與 inner 的關聯字段相等的數據進行查詢,並支持進行匯總操作。

GroupJoin() 方法也具有 延遲執行 的特性,並且通過 SelectMany() 方法與 DefaultIfEmpty() 方法可以實現 Left Join 的查詢效果。

標准用法:

public class Person
{
    public int Id { get; set; }

    public string Name { get; set; }
}

public class ContactInformation
{
    public int PersonId { get; set; }

    public string PhoneNumber { get; set; }

    public string Address { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        // 初始化員工信息
        var persons = new[] {new Person {Id = 1, Name = "張三"}, new Person {Id = 2, Name = "李四"}, new Person {Id = 3, Name = "王二"}};
        
        // 初始化員工的聯系方式
        var contactInfos = new[]
        {
            new ContactInformation {PersonId = 1, Address = "上海", PhoneNumber = "001-00001"},
            new ContactInformation {PersonId = 2, Address = "北京", PhoneNumber = "002-00002"},
            new ContactInformation {PersonId = 3, Address = "廣州", PhoneNumber = "003-00003"},
            new ContactInformation {PersonId = 1, Address = "深圳", PhoneNumber = "004-00004"}
        };

        var groupJoinResult = persons.GroupJoin(contactInfos, person => person.Id, contactInfo => contactInfo.PersonId, (person, infos) => new
        {
            Name = person.Name,
            Phones = string.Join(',',infos.Select(x=>x.PhoneNumber))
        });

        foreach (var item in groupJoinResult)
        {
            Console.WriteLine($"姓名:{item.Name},電話:{item.Phones}");
        }
    }
}

輸出結果:

姓名:張三,電話:001-00001,004-00004
姓名:李四,電話:002-00002
姓名:王二,電話:003-00003

擴展寫法,使用 SelectMany()DefaultIfEmpty() 實現左連接查詢。

public class Person
{
    public int Id { get; set; }

    public string Name { get; set; }
}

public class ContactInformation
{
    public int PersonId { get; set; }

    public string PhoneNumber { get; set; }

    public string Address { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        // 初始化員工信息
        var persons = new[] {new Person {Id = 1, Name = "張三"}, new Person {Id = 2, Name = "李四"}, new Person {Id = 3, Name = "王二"}};
        
        // 初始化員工的聯系方式
        var contactInfos = new[]
        {
            new ContactInformation {PersonId = 1, Address = "上海", PhoneNumber = "001-00001"},
            new ContactInformation {PersonId = 2, Address = "北京", PhoneNumber = "002-00002"},
            // ContactInformation {PersonId = 3, Address = "廣州", PhoneNumber = "003-00003"},
            new ContactInformation {PersonId = 1, Address = "深圳", PhoneNumber = "004-00004"}
        };

        var groupJoinResult = persons.GroupJoin(contactInfos, person => person.Id, contactInfo => contactInfo.PersonId, (person, infos) => new
        {
            Name = person.Name,
            ContactInfos = infos
        }).SelectMany(x=>x.ContactInfos.DefaultIfEmpty(), (_, __) => new
        {
            PersonName = _.Name,
            PhoneNumber = __?.PhoneNumber ?? null
        });

        foreach (var item in groupJoinResult)
        {
            Console.WriteLine($"姓名:{item.PersonName},電話:{item.PhoneNumber}");
        }
    }
}

輸出結果:

姓名:張三,電話:001-00001
姓名:張三,電話:004-00004
姓名:李四,電話:002-00002
姓名:王二,電話:

8. IEnuemrable<T>.Skip()

IEnumerable<T>.Skip(int N) 方法的主要作用就是從序列首部跳過序列中 N 個元素,然后返回其結果,這個方法還有兩個衍生的方法。

第一個是 IEnumerable<T>.SkipLast(int N) 方法,與 Skip(int N) 方法相反,它會從序列的尾部跳過 N 個元素再返回結果。

第二個則是 IEnumerbale<T>.SkipWhile(Func<T,bool> predicate) ,它可以傳入一個過濾條件,當序列當中遇到第一個不滿足條件的元素時,就會返回該元素及其后續的所有元素,而略過之前符合條件的元素。

標准用法:

class Program
{
	static void Main()
	{
		void OutputResult(IEnumerable<int> ienumerable)
		{
			foreach (var item in ienumerable)
			{
				Console.WriteLine($"{item}");
			}
		}

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

		// Skip() 方法演示。
		Console.WriteLine("Skip() 方法演示:");
		var skipResult = integers.Skip(5);
		OutputResult(skipResult);

		// SkipLast() 方法演示。
		Console.WriteLine("SkipLast() 方法演示:");
		var skipLastResult = integers.SkipLast(5);
		OutputResult(skipLastResult);

		// SkipWhile() 方法演示。
		Console.WriteLine("SkipWhile() 方法演示:");
		var skipWhileResult = integers.SkipWhile(@int => @int != 3);
		OutputResult(skipWhileResult);
	}
}

輸出結果:

Skip() 方法演示:
6
7
8
9
10
SkipLast() 方法演示:
1
2
3
4
5
SkipWhile() 方法演示:
3
4
5
6
7
8
9
10

9. IEnuemrable<T>.Take()

IEnumerable<T>.Take(int N) 方法的作用是從序列首部選取 N 個元素返回結果。

它也擁有兩個衍生的方法,第一個是 IEnumerable<T>.TakeLast(int N) 方法,與 Skip(int N) 方法相同。這個方法主要是從序列的尾部選取 N 個元素組成一個新的序列並返回其結果。

第二個方法也是相似,叫做 IEnumerable<T>.TakeWhile(Func<T,bool>) ,與 IEnumerable<T>.Skip(Func<T,bool>) 方法相反,本方法是會從序列首部開始獲取元素,直到條件滿足時就會停止獲取,然后將這些結果返回成為一個新的序列。

標准用法:

class Program
{
	static void Main()
	{
		void OutputResult(IEnumerable<int> ienumerable)
		{
			foreach (var item in ienumerable)
			{
				Console.WriteLine($"{item}");
			}
		}

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

		// Take() 方法演示。
		Console.WriteLine("Take() 方法演示:");
		var skipResult = integers.Take(5);
		OutputResult(skipResult);

		// TakeLast() 方法演示。
		Console.WriteLine("TakeLast() 方法演示:");
		var skipLastResult = integers.Take(3);
		OutputResult(skipLastResult);

		// TakeWhile() 方法演示。
		Console.WriteLine("TakeWhile() 方法演示:");
		var skipWhileResult = integers.TakeWhile(@int => @int != 3);
		OutputResult(skipWhileResult);
	}
}

輸出結果:

Take() 方法演示:
1
2
3
4
5
TakeLast() 方法演示:
1
2
3
TakeWhile() 方法演示:
1
2

10. IEnuemrable<T>.Aggregate()

IEnumerable<T>.Aggregate() 方法的主要作用是幫助開發人員對一個序列的資料進行匯總處理,即會執行 N 次,每次會將之前的匯總結果與當前元素的值傳入到一個 Func<TSource, TSource, TSource> 委托當中,其返回值就會作為新的匯總結果傳遞給下一個元素。

IEnumerable<T>.Aggregate() 共有 3 個重載方法,其運行機制與上述說明一致,只是在於初始化操作和返回值的時候不太一致。

public static TSource Aggregate<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, TSource, TSource> func);

其中 func 參數是每次執行匯總操作時的邏輯方法,第一個參數則是之前的匯總結果,第二個參數即是當前元素的值,最后一個返回值則是匯總完成的結果,將會作為下一次匯總操作時的傳入參數。(即第一個參數的值)

public static TAccumulate Aggregate<TSource, TAccumulate>(
    this IEnumerable<TSource> source,
    TAccumulate seed,
    Func<TAccumulate, TSource, TAccumulate> func);

其中 seed 是匯總結果的初始值,這個值將會在第一次計算匯總結果的時候用到。

public static TResult Aggregate<TSource, TAccumulate, TResult>(
    this IEnumerable<TSource> source,
    TAccumulate seed,
    Func<TAccumulate, TSource, TAccumulate> func,
    Func<TAccumulate, TResult> resultSelector);

本方法與上一個方法類似,不同的是有一個 resultSelector 委托可以方便我們將數據重新投射到不同的類型當中去。

標准用法:

class Program
{
    void OutputResult(IEnumerable<int> ienumerable)
    {
        foreach (var item in ienumerable)
        {
            Console.WriteLine($"{item}");
        }
    }
    
    static void Main(string[] args)
    {
        var integers = new[] {3, 2, 1, 7, 10, 6, 4, 8, 9, 5};

        // IEnumerable<T>.Sum(x=>x) 效果,計算整個序列元素之和。
        var sumResult = integers.Aggregate((totalCount, nextItem) => totalCount + nextItem);
        Console.WriteLine($"序列求和結果:{sumResult}");
        
        // IEnumerable<T>.Min(x=>x) 效果,計算整個序列當中的最小元素。
        var minResult = integers.Aggregate((min, next) => min > next ? next : min);
        Console.WriteLine($"序列當中的最小值:{minResult}");

        // IEnumerable<T>.Max(x=>x) 效果,計算整個序列當中最大的元素。
        var maxResult = integers.Aggregate((max, next) => max < next ? next : max);
        Console.WriteLine($"序列當中的最大值:{maxResult}");
        
        // IEnumerable<T>.Count() 效果,計算整個序列當中的元素數量。
        var countResult = integers.Aggregate(0,(count, next) => ++count);
        Console.WriteLine($"序列當中的元素數量:{countResult}");
        
        // IEnumerable<T>.Average(x=>) 效果,計算整個序列當中的平均數。
        int enumerableCount = 0;
        var averageResult = integers.Aggregate(0, (total, next) =>
        {
            total += next;
            enumerableCount++;
            return total;
        },total=>new
        {
            averageValue = total / enumerableCount
        });
        
        Console.WriteLine($"序列的平均值:{averageResult.averageValue}");
    }
}

輸出結果:

序列求和結果:55
序列當中的最小值:1
序列當中的最大值:10
序列當中的元素數量:10
序列的平均值:5


免責聲明!

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



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