C#基礎(chǔ)–Lambda 和 LINQ
一、Lambda 的前世今生
在.NetFramewok的不同版本都有不同的樣子;在.NetCore下也都是支持的;
1.1 .Netframework1.0/1.1時(shí)代
public delegate void NoReturnWithPara(int x, string y);
NoReturnWithPara method = new NoReturnWithPara(Study);
method.Invoke(123, "Richard");
private void Study(int id, string name)
{
Console.WriteLine($"{id} {name} 學(xué)習(xí).Net高級(jí)班");
}
1.2 .NetFramework2.0
匿名方法 增加了一個(gè)delegate關(guān)鍵字, 可以訪問到除了參數(shù)以外的局部變量
public delegate void NoReturnWithPara(int x, string y);
int i =0;
NoReturnWithPara method = new NoReturnWithPara(delegate (int id, string name)
{
Console.WriteLine($"{id} {name} 學(xué)習(xí).Net高級(jí)班");
Console.WriteLine(i); //可以訪問到除了參數(shù)以外的局部變量 i
});
method.Invoke(123, "Richard");
1.3 .NetFramework3.0 前期
去掉了delegate關(guān)鍵字,添加了一個(gè)=> goes to
public delegate void NoReturnWithPara(int x, string y);
int i =0;
NoReturnWithPara method = new NoReturnWithPara((int id, string name) =>
{
Console.WriteLine($"{id} {name} 學(xué)習(xí).Net高級(jí)班");
Console.WriteLine(i);
});
method.Invoke(123, "Richard");
1.4 .NetFramework3.0 后期
去掉了匿名方法后的參數(shù)類型,編譯器自動(dòng)推斷來的/編譯器提供的便捷功能(語法糖)
public delegate void NoReturnWithPara(int x, string y);
int i =0;
NoReturnWithPara method = new NoReturnWithPara((id, name) => //編譯器自動(dòng)推斷來的/編譯器提供的便捷功能(語法糖)
{
Console.WriteLine($"{id} {name} 學(xué)習(xí).Net高級(jí)班");
Console.WriteLine(i);
});
method.Invoke(123, "Richard");
如果匿名方法體中只有一行代碼,可以省略方法體的大括號(hào):
public delegate void NoReturnWithPara(int x, string y);
NoReturnWithPara method = new NoReturnWithPara((id, name) => Console.WriteLine($"{id} {name} 學(xué)習(xí).Net高級(jí)班"));
method.Invoke(123, "Richard");
public delegate void NoReturnWithPara(int x, string y);
NoReturnWithPara method = (id, name) => Console.WriteLine($"{id} {name} 學(xué)習(xí).Net高級(jí)班");
method.Invoke(123, "Richard");
如果只有一個(gè)參數(shù)的時(shí)候:
Action<string> method = sn => Console.WriteLine($"歡迎{sn} 來到.Net高級(jí)班進(jìn)階學(xué)習(xí)");
method.Invoke("牧羊人");
如果有返回值? 如果lambda表達(dá)式中只有一行代碼,且有返回值,可以省略return;
Func<string> func0 = () => { return "黃大仙" };
//如果有返回值? 如果lambda表達(dá)式中只有一行代碼,且有返回值,可以省略 `大括號(hào)` + `return`;
Func<string> func = () => "黃大仙";
Func<int, string> func1 = i => i.ToString();
大家覺得Lambda表達(dá)式本質(zhì)是什么?
多播委托中可以把Lambda表達(dá)式+=
,但是不能把Lambda表達(dá)式-=
。因?yàn)長ambda表達(dá)式其實(shí)是一個(gè)方法,不同的lambda表達(dá)式就是不同的方法。
Lambda的本質(zhì)是一個(gè)方法。
語法糖:編譯器提供的便捷功能
二、LINQ
初始化數(shù)據(jù)
List<Student> studentList = new List<Student>()
{
new Student() { Id=1, Name="趙亮", ClassId=2, Age=35 },
new Student() { Id=2, Name="再努力一點(diǎn)", ClassId=2, Age=23 },
new Student() { Id=3, Name="王炸", ClassId=2, Age=27 },
new Student() { Id=4, Name="瘋子科學(xué)家", ClassId=2, Age=26 },
new Student() { Id=5, Name="滅", ClassId=2, Age=25 },
new Student() { Id=6, Name="黑騎士", ClassId=2, Age=24 },
new Student() { Id=7, Name="故鄉(xiāng)的風(fēng)", ClassId=2, Age=21 },
new Student() { Id=8, Name="晴天", ClassId=2, Age=22 }
};
2.1 Linq 擴(kuò)展方法&表達(dá)式
var list = studentList.Where<Student>(s => s.Age < 30); //list里面必然是符合要求的數(shù)據(jù);
var list = from s in studentList
where s.Age < 30
select s; //list里面必然是符合要求的數(shù)據(jù);
以上兩種都是LINQ。
2.2 linq to object
Linq – Linq to object:就是針對IEnumerable類型數(shù)據(jù)
? 1. IEnumerable 類型數(shù)據(jù):可以理解為內(nèi)存中的數(shù)據(jù);其實(shí)就是把不變的邏輯封裝起來,把可變的邏輯封裝成委托來傳遞;
? 2. IQueryable 類型數(shù)據(jù):可以理解成內(nèi)存數(shù)據(jù)—來自于數(shù)據(jù)庫的數(shù)據(jù);
延伸:
Linq to Sql:就是把打開數(shù)據(jù)庫連接,查詢數(shù)據(jù)(不變的的邏輯),把sql 的拼裝(可變的邏輯)
Linq to Xml:把數(shù)據(jù)解析這類動(dòng)作封裝起來(不變的邏輯), 把數(shù)據(jù)篩選條件封裝成委托來傳遞(可變的邏輯)
Linq to Redis:把固定的邏輯封裝起來,把不變的邏輯封裝成委托傳遞
Linq to Cache:…
Linq to JSON:…
Linq to Everything:…
IQueryable 和 IEnumerable 存在本質(zhì)的區(qū)別:
- 使用IQueryable 查詢
List<Student> studentList = this.GetStudentList();
var query = studentList.AsQueryable();
query.Where(a => a.Age < 30);
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
- 使用 IEnumerable 查詢
List<Student> studentList = this.GetStudentList();
var query = studentList.AsEnumerable();
query.Where(a => a.Age < 30);
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
IEnumerable 傳遞的是一個(gè)委托,而 IQueryable 傳遞的是一個(gè)表達(dá)式目錄樹(數(shù)據(jù)結(jié)構(gòu))
現(xiàn)在大家覺得Linq 是什么?
Linq 是一種封裝思想,對于用戶來說,就把不需要關(guān)心內(nèi)部怎么實(shí)現(xiàn),只需要Linq一下即可;微軟之前很推崇這種思想,只讓開發(fā)者關(guān)注自己的核心邏輯,不需要關(guān)注內(nèi)部邏輯,徹底的把開發(fā)者變成編程小白;如果大家有經(jīng)驗(yàn)的話,你會(huì)發(fā)現(xiàn)像Asp .Net MVC就屬于傻瓜式開發(fā),成套的;我們不用去關(guān)心原理就可以直接上手。
但是現(xiàn)在又變了,現(xiàn)在有更高的要求,像是Linq to JSON、Linq to Redis等并沒有人來封裝這些。如果大家有興趣,可以去嘗試封裝一下。
2.3 基本查詢
var list = studentList.Where<Student>(s => s.Age < 30)
.Select(s => new
{
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高級(jí)班" : "其他班"
});
var list = from s in studentList
where s.Age < 30
select new
{
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高級(jí)班" : "其他班"
};
Select 方法:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
**備注:**使用 studentList.Where(s => s.Age < 30) 查詢并返回?cái)?shù)據(jù)后,實(shí)際內(nèi)容還是一個(gè) IEnumerable 的數(shù)據(jù)。但是接著再使用了Select 做投影的話,里面創(chuàng)建了一個(gè)匿名對象,返回的也是一個(gè)匿名對象。
2.4 分頁
var list = studentList.Where<Student>(s => s.Age < 30)//條件過濾
.Select(s => new//投影
{
Id = s.Id,
ClassId = s.ClassId,
IdName = s.Id + s.Name,
ClassName = s.ClassId == 2 ? "高級(jí)班" : "其他班"
})
.OrderBy(s => s.Id)//排序
.ThenBy(s=>s.ClassName) //多重排序,可以多個(gè)字段排序都生效
.OrderByDescending(s => s.ClassId)//倒排
.Skip(2)//跳過幾條
.Take(3)//獲取幾條
;
性能怎樣?=> 本質(zhì)其實(shí)都是循環(huán);
2.5 分組
var list = from s in studentList
where s.Age < 30
group s by s.ClassId into sg
select new
{
key = sg.Key,
maxAge = sg.Max(t => t.Age)
};
var list = studentList.Where(a=>a.Age<30)
.GroupBy(c => c.ClassId)
.Select(sg => new
{
key = sg.Key,
maxAge = sg.Max(t => t.Age)
});
2.6 內(nèi)連接 – inner join
var list = from s in studentList
join c in classList on s.ClassId equals c.Id
select new
{
Name = s.Name,
CalssName = c.ClassName
};
var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
{
Name = s.Name,
CalssName = c.ClassName
});
2.7 左連接 – left join
var list = from s in studentList
join c in classList on s.ClassId equals c.Id
into scList
from sc in scList.DefaultIfEmpty()
select new
{
Name = s.Name,
CalssName = sc == null ? "無班級(jí)" : sc.ClassName//c變sc,為空則用
};
var list = studentList.Join(classList, s => s.ClassId, c => c.Id, (s, c) => new
{
Name = s.Name,
CalssName = c.ClassName
}).DefaultIfEmpty();
三、自定義封裝
3.1 針對一個(gè)具體類的封裝
/// <summary>
/// 如果換個(gè)條件怎么辦?
/// 使用委托:委托可以把方法當(dāng)做參數(shù)傳遞;方法其實(shí)是邏輯,委托可以把邏輯當(dāng)做參數(shù)傳遞;
/// 委托:應(yīng)該是返回值為bool的委托,參數(shù)是一個(gè)Student
/// </summary>
/// <param name="resource"></param>
/// <returns></returns>
public static List<Student> RichardWhere(this List<Student> resource, Func<Student, bool> func)
{
List<Student> list = new List<Student>();
foreach (var item in resource)
{
//if (item.Name.Length > 2) //條件的判斷其實(shí)就是邏輯---動(dòng)作 //唯一的區(qū)別就在與中間這里的條件不一樣;
if (func.Invoke(item))
{
list.Add(item);
}
}
return list;
}
3.2 通用–泛型封裝
/// <summary>
/// 這就是Linq中where的本質(zhì);
/// 1. 是把固定不變的邏輯,封裝起來,把可變的邏輯封裝成委托來傳遞
/// 就可以讓開發(fā)者只需要關(guān)注自己的核心業(yè)務(wù),其他別的都以LINQ封裝
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="resource"></param>
/// <param name="func"></param>
/// <returns></returns>
public static List<T> RichardWhere<T>(this List<T> resource, Func<T, bool> func) where T:class
{
List<T> list = new List<T>();
foreach (var item in resource)
{
//if (item.Name.Length > 2) //條件的判斷其實(shí)就是邏輯---動(dòng)作 //唯一的區(qū)別就在與中間這里的條件不一樣;
if (func.Invoke(item))
{
list.Add(item);
}
}
return list;
}
/// <summary>
/// 有什么好處?
/// 通過接口來擴(kuò)展,只要實(shí)現(xiàn)了這個(gè)接口的,都可以使用當(dāng)前這個(gè)擴(kuò)展方法
/// 相比而言:自然擴(kuò)展抽象要好一些,擴(kuò)展性更好
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="resource"></param>
/// <param name="func"></param>
/// <returns></returns>
public static IEnumerable<T> RichardWhereEnumerable<T>(this IEnumerable<T> resource, Func<T, bool> func) where T : class
{
List<T> list = new List<T>();
foreach (var item in resource)
{
//if (item.Name.Length > 2) //條件的判斷其實(shí)就是邏輯---動(dòng)作 //唯一的區(qū)別就在與中間這里的條件不一樣;
if (func.Invoke(item))
{
list.Add(item);
}
}
return list;
}
3.3 實(shí)際運(yùn)用
如果是學(xué)生類的實(shí)現(xiàn)對象的話,3.1 和3.2都是的話下面代碼一樣,無需任何修改
var list1 = MethodExtension.RichardWhere(studentList, item => item.Age < 30);
var list2 = studentList.RichardWhere(item => item.Age < 30);
var list1 = MethodExtension.RichardWhere(studentList, item => item.Name.Length > 2);
var list2 = studentList.RichardWhere(item => item.Name.Length > 2);
var list1 = MethodExtension.RichardWhere(studentList, item => item.Id > 1
&& item.Name != null
&& item.ClassId == 1
&& item.Age > 20);
//循環(huán)完畢以后,list里面必然是符合要求的數(shù)據(jù);
var list2 = studentList.RichardWhere<Student>(item => item.Id > 1
&& item.Name != null
&& item.ClassId == 1
&& item.Age > 20);
四、yield 使用
/// <summary>
/// yield` 必須和 IEnumerable<T> 配合使用,可以做到按需獲取
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="resource"></param>
/// <param name="func"></param>
/// <returns></returns>
public static IEnumerable<T> RichardWhereIterator<T>(this IEnumerable<T> resource, Func<T, bool> func) where T : class
{
foreach (var item in resource)
{
if (func.Invoke(item))
{
yield return item;
}
}
return list;
}
var list3 = studentList.RichardWhereIterator(item => item.Age < 30);
foreach (var item in list3)
{
Console.WriteLine("Name={0} Age={1}", item.Name, item.Age);
}
yield
必須和 IEnumerable<T>
配合使用,可以做到按需獲??;如果返回值換成 List<T>
就會(huì)產(chǎn)生報(bào)錯(cuò)
按需獲?。?/strong> var list3 = studentList.RichardWhereIterator(item => item.Age < 30);
執(zhí)行完之后,并沒有實(shí)質(zhì)性執(zhí)行到 RichardWhereIterator
方法內(nèi)部,直到 foreach (var item in list3)
的時(shí)候,才執(zhí)行到方法體內(nèi),而且是循環(huán)一次就到 RichardWhereIterator
獲取一條數(shù)據(jù)。
文章來源:http://www.zghlxwxcb.cn/news/detail-581834.html
使用yield 的使用,編譯器會(huì)生成一個(gè)泛型的,會(huì)有個(gè)迭代狀態(tài)機(jī) [IteratorStateMachine] (屬于特性)文章來源地址http://www.zghlxwxcb.cn/news/detail-581834.html
到了這里,關(guān)于C#基礎(chǔ)--Lambda和LINQ的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!