日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从LINQ开始之LINQ to Objects(下)

發(fā)布時間:2025/7/14 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从LINQ开始之LINQ to Objects(下) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言


上一篇《從LINQ開始之LINQ to Objects(上)》主要介紹了LINQ的體系結(jié)構(gòu)、基本語法以及LINQ to Objects中標準查詢操作符的使用方法。
本篇則主要討論LINQ to Objects中的擴展方法以及延遲加載等方面的內(nèi)容。

擴展方法


擴展方法簡介

  擴展方法能夠向現(xiàn)有類型“添加”方法,而無需創(chuàng)建新的派生類型、重新編譯或其他方式修改原始類型。擴展方法是靜態(tài)方法,它是類的一部分,但實際沒有放在類的源代碼當中。
下面,我們來看一個簡單示例,為上一篇中定義的Employee類添加擴展方法GetSeniority獲取員工在本公司的工齡:

public static class EmployeeExtension {/// <summary>/// 計算員工在本公司的工齡/// </summary>/// <param name="employee"></param>/// <returns></returns>public static long GetSeniority(this Employee employee){TimeSpan ts = DateTime.Now - employee.EntryDate;return (long)ts.TotalDays / 365;} }

接下來,遍歷employees列表,輸出所有員工的姓名及工齡:

//獲取所有員工的姓名及在本公司的工齡foreach (var employee in employees){Console.WriteLine("EmployeeName: " + employee.EmployeeName + " Seniority: " + employee.GetSeniority());}//******************************Output*******************************//EmployeeName: Mike Seniority: 1//EmployeeName: Jack Seniority: 10//EmployeeName: Adolph Seniority: 0//EmployeeName: Antony Seniority: 6//EmployeeName: Asa Seniority: 2//EmployeeName: Bernie Seniority: 9//EmployeeName: Carl Seniority: 2//EmployeeName: Duncan Seniority: 7//EmployeeName: Aimee Seniority: 0//EmployeeName: Cassie Seniority: 3//*******************************************************************

由示例可以看出:
1)擴展方法中,可以訪問被擴展類型的所有公有方法和屬性。
2)第一個參數(shù)是要擴展的類型,以this關(guān)鍵字開頭。
3)即使擴展方法是靜態(tài)的,也要使用標準的實例方法語法進行調(diào)用。
下面的示例演示了如果擴展方法與類中的某個方法具有相同的簽名,則擴展方法不會被調(diào)用。在Employee類中定義方法SayHello

public void SayHello(){Console.WriteLine("Hello , I'm " + EmployeeName);}

在EmployeeExtension類中為Employee類定義擴展方法SayHello

public static void SayHello(this Employee employee){Console.WriteLine("Hello , I'm " + employee.EmployeeName + " ,this is Extension Method");}

此時,新入職了一位同事Dave,調(diào)用SayHello方法向大家問好

Employee dave = new Employee("011", "Dave", 30, new DateTime(2017, 5, 25), Sex.Male, Department.PD, 200000, new string[] { "climbing" });dave.SayHello();//******************************Output*******************************//Hello , I'm Dave//*******************************************************************

注意:此時調(diào)用的是Employee類下面的SayHello方法。

使用擴展方法來擴展接口

  把方法擴展到某個接口中,實現(xiàn)該接口的多個類就可以使用相同的實現(xiàn)代碼。
以下示例介紹了擴展方法擴展接口的使用場景,首先,定義了一個接口IHobby,接口中包含Play方法

public interface IHobby {void Play(); }

分別創(chuàng)建類Reading、Swimming、Shopping實現(xiàn)IHobby接口

public class Reading : IHobby {public void Play(){Console.WriteLine("I'm Reading.");} }public class Swimming : IHobby {public void Play(){Console.WriteLine("I'm Swimming.");} }public class Shopping : IHobby {public void Play(){Console.WriteLine("I'm Shopping.");} }

此時,我們需要在實現(xiàn)IHobby接口的類增加一個的方法ShareFeelings,輸出I'm happpy.當然,可以在接口上新增一個方法,然后將實現(xiàn)該接口的類逐個添加ShareFeelings方法,假如實現(xiàn)該接口的類很多,使用擴展方法,就可以大大的減少代碼的修改量,測試起來也非常簡單。

public static void ShareFeelings(this IHobby hobby){Console.WriteLine("I'm happy.");}

使用接口變量來調(diào)用擴展方法

IHobby hobby = new Reading();hobby.ShareFeelings();//******************************Output*******************************//I'm happy.//*******************************************************************

LINQ中的擴展方法

  LINQ為IEnumerable<T>接口提供給了各種擴展方法,以便用戶在實現(xiàn)了該接口的任意集合上使用LINQ查詢。本節(jié)主要研究LINQ中Where擴展方法的實現(xiàn),這個擴展方法位于System.Linq命名空間下的Enumerable類中。

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {if (source == null) throw Error.ArgumentNull("source");if (predicate == null) throw Error.ArgumentNull("predicate");if (source is Iterator<TSource>) return ((Iterator<TSource>)source).Where(predicate);if (source is TSource[]) return new WhereArrayIterator<TSource>((TSource[])source, predicate);if (source is List<TSource>) return new WhereListIterator<TSource>((List<TSource>)source, predicate);return new WhereEnumerableIterator<TSource>(source, predicate);}

由上述代碼可以看出,Where方法是對IEnumberable接口的擴展,需要傳入一個委托參數(shù)predicate,該委托要求返回布爾類型。假設(shè)我們對List<T>類型的對象調(diào)用Where方法,則返回一個WhereListIterator<TSource>對象。WhereListIterator<TSource>類派生自Iterator<TSource>類,下面是Iterator<TSource>類的源碼,這里我們只需要注意GetEnumerator方法,該方法對于同一個線程,返回同一個迭代器,不同線程則克隆一個,并將state屬性設(shè)置為1。

abstract class Iterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>{int threadId;internal int state;internal TSource current;public Iterator() {threadId = Thread.CurrentThread.ManagedThreadId;}public TSource Current {get { return current; }}public abstract Iterator<TSource> Clone();public virtual void Dispose() {current = default(TSource);state = -1;}public IEnumerator<TSource> GetEnumerator() {if (threadId == Thread.CurrentThread.ManagedThreadId && state == 0) {state = 1;return this;}Iterator<TSource> duplicate = Clone();duplicate.state = 1;return duplicate;}public abstract bool MoveNext();public abstract IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector);public abstract IEnumerable<TSource> Where(Func<TSource, bool> predicate);object IEnumerator.Current {get { return Current; }}IEnumerator IEnumerable.GetEnumerator() {return GetEnumerator();}void IEnumerator.Reset() {throw new NotImplementedException();}}

此時,再回到WhereListIterator<TSource>類,該類重寫了MoveNext方法。首先,調(diào)用GetEnumerator方法獲得一個枚舉器,在While循環(huán)中,只要MoveNext方法返回true,就用Current屬性獲得集合當前的元素,并使用委托predicate引用的方法處理該元素,返回剩余元素中滿足條件的第一個元素。當遍歷結(jié)束,調(diào)用Dispose方法釋放非托管資源,并將state屬性設(shè)置為-1。

class WhereListIterator<TSource> : Iterator<TSource>{List<TSource> source;Func<TSource, bool> predicate;List<TSource>.Enumerator enumerator;public WhereListIterator(List<TSource> source, Func<TSource, bool> predicate) {this.source = source;this.predicate = predicate;}public override Iterator<TSource> Clone() {return new WhereListIterator<TSource>(source, predicate);}public override bool MoveNext() {switch (state) {case 1:enumerator = source.GetEnumerator();state = 2;goto case 2;case 2:while (enumerator.MoveNext()) {TSource item = enumerator.Current;if (predicate(item)) {current = item;return true;}}Dispose();break;}return false;}public override IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector) {return new WhereSelectListIterator<TSource, TResult>(source, predicate, selector);}public override IEnumerable<TSource> Where(Func<TSource, bool> predicate) {return new WhereListIterator<TSource>(source, CombinePredicates(this.predicate, predicate));}}

源碼傳送門:http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,dc4c4c53ff606bc0

延遲加載


延遲執(zhí)行

  在運行期間定義查詢表達式時,查詢不會運行,只有在迭代時才進行計算。
下面的示例定義了一個LINQ查詢,從集合中找出姓名以A開頭的所有員工,因為迭代在查詢定義時不會進行,而是在執(zhí)行每個foreach語句時進行。

var nameStartWithA = from e in employeeswhere e.EmployeeName.StartsWith("A")select e;Console.WriteLine("First iteration : ");foreach (var item in nameStartWithA){Console.WriteLine(item.EmployeeName);}Console.WriteLine();employees.Add(new Employee("011", "Lily", 25, new DateTime(2017, 5, 29), Sex.Female, Department.HR, 100000, new string[] { "shopping" }));employees.Add(new Employee("012", "Leo", 28, new DateTime(2017, 5, 29), Sex.Male, Department.IT, 200000, new string[] { "reading" }));employees.Add(new Employee("013", "Amelia", 29, new DateTime(2017, 5, 29), Sex.Female, Department.PD, 200000, new string[] { "reading", "run" }));employees.Add(new Employee("014", "Ava", 32, new DateTime(2017, 5, 29), Sex.Female, Department.PD, 400000, new string[] { "swimming" }));Console.WriteLine("Second iteration : ");foreach (var item in nameStartWithA){Console.WriteLine(item.EmployeeName);}//******************************Output*******************************//First iteration ://Adolph//Antony//Asa//Aimee//Second iteration ://Adolph//Antony//Asa//Aimee//Amelia//Ava//*******************************************************************

補充:延遲加載的工作原理可從上一章節(jié)中對源碼的分析得出。

立即執(zhí)行

  查詢在定義表達式時立即執(zhí)行,而不是在迭代中進行。通過調(diào)用ToArray()、ToList()等擴展方法可以實現(xiàn)此項操作。
下面,我們修改上一節(jié)中的示例來說明:

var nameStartWithA = (from e in employeeswhere e.EmployeeName.StartsWith("A")select e).ToList();Console.WriteLine("First iteration : ");foreach (var item in nameStartWithA){Console.WriteLine(item.EmployeeName);}Console.WriteLine();employees.Add(new Employee("011", "Lily", 25, new DateTime(2017, 5, 29), Sex.Female, Department.HR, 100000, new string[] { "shopping" }));employees.Add(new Employee("012", "Leo", 28, new DateTime(2017, 5, 29), Sex.Male, Department.IT, 200000, new string[] { "reading" }));employees.Add(new Employee("013", "Amelia", 29, new DateTime(2017, 5, 29), Sex.Female, Department.PD, 200000, new string[] { "reading", "run" }));employees.Add(new Employee("014", "Ava", 32, new DateTime(2017, 5, 29), Sex.Female, Department.PD, 400000, new string[] { "swimming" }));Console.WriteLine("Second iteration : ");foreach (var item in nameStartWithA){Console.WriteLine(item.EmployeeName);}//******************************Output*******************************//First iteration ://Adolph//Antony//Asa//Aimee//Second iteration ://Adolph//Antony//Asa//Aimee//*******************************************************************

從輸出結(jié)果中可以看出,兩次迭代輸出的結(jié)果相同,但是集合中值改變了。
示例代碼下載:https://github.com/Answer-Geng/LINQ

轉(zhuǎn)載于:https://www.cnblogs.com/zhangyingai/p/7074508.html

總結(jié)

以上是生活随笔為你收集整理的从LINQ开始之LINQ to Objects(下)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。