一,、 關(guān)于LINQ LINQ提供的便利: 1)使用一種簡化的方式編寫查詢語句; 2)通過消除運行時錯誤和捕捉編譯時錯誤減少開發(fā)時間,; 3)直接在開發(fā)語言中提供對LINQ的IntelliSense和調(diào)試支持,; 4)消除關(guān)系數(shù)據(jù)和面向?qū)ο箝_發(fā)之間的障礙; 5)提供與數(shù)據(jù)源無關(guān)的統(tǒng)一查詢語法,。 LINQ的一大特性是可以對多種數(shù)據(jù)源查詢,,而且查詢的語法相同,這為開發(fā)人員處理不同的數(shù)據(jù)源的數(shù)據(jù)提供了極大的便利,。 LINQ 能查詢的數(shù)據(jù)有但不限于: 1)對象,,LINQ to Objects。比如集合,、數(shù)組,、字符串等等,。 2)關(guān)系數(shù)據(jù),分為 LINQ to DataSet 和 LINQ to SQL,,前者用于 DataSet 查詢,,后者用于 SQL Server 數(shù)據(jù)庫查詢。 3)XML,,LINQ to XML,。
二、 操作符和LINQ string[] fruits = new string[]
{ "Apple", "Apricot", "Arbutus", "Banana", "Bennet", "Betelnut", "Betelnut", "Black brin" }; IEnumerable<string> val = from fruit in fruits where fruit.EndsWith("s") select fruit; foreach (string s in val) { Console.WriteLine(s + "\n"); }
這是一個簡單的LINQ表達(dá)式,其中where和select是LINQ眾多標(biāo)準(zhǔn)查詢操作符中的兩個,,select是投影操作符,,它在某個系列(即實現(xiàn)了Ienumerable<string>接口的對象上)上進(jìn)行投影,where是限制操作符類型,,類似我們sql語句中的where,,用于過濾某個系列。局部變量fs實現(xiàn)了具有遍歷功能的Ienumerable<string>接口,。LINQ中提供了很多和where,、select這樣的標(biāo)準(zhǔn)查詢操作符,這些查詢操作符提供了很多功能,,如過濾,、排序、分組,、聚合,、轉(zhuǎn)換等功能,LINQ包括兩個標(biāo)準(zhǔn)查詢操作符集合,,一個集合用于操作IEnumerable<T>類型的對象,,另外一個集合則是用于操作IQueryable<T>類型的對象。這些操作符由Enumerable和Queryable類的靜態(tài)方法組成,。如Enumerable類提供了Select,、Where等查詢操作符的靜態(tài)方法,更詳細(xì)的了解,,可以查閱MSDN,,網(wǎng)址:http://msdn.microsoft.com/zh-cn/library/bb341635.aspx。
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source, Func<TSource, bool> predicate )
在觀照from fruit in fruits where fruit.EndsWith("t") select fruit這樣的查詢表達(dá)式會有些疑惑,,因為我們通常使用靜態(tài)方法都是這樣用的:方法名.靜態(tài)方法(方法參數(shù)),但是在上面查詢表達(dá)式中,,這些靜態(tài)方法卻被當(dāng)做關(guān)鍵字的方式來使用,。其實LINQ提供了查詢語法和方法語法這兩種語法來編寫查詢功能,,像上面是查詢語法,實現(xiàn)var fs = from fruit in fruits where fruit.EndsWith("t") select fruit這樣的查詢功能,,我們還可以用LINQ提供的方法語法來實現(xiàn):
三,、 LINQ操作符類型類型
為便于下面代碼的演示,,這里先定義一些公共代碼。我們定義一個學(xué)生類Student,,一個學(xué)校類School,,然后定義一個Student的集合類Sudents和一個學(xué)校的集合schools。
/// <summary>
/// 學(xué)生類 /// </summary> public class Student { public int StuId { get; set; } public string Name { get; set; } public int Age { get; set; } public string Class { get; set; } public int SchoolId { get; set; } } /// <summary> /// 學(xué)校類 /// </summary> public class School { public int SchoolId { get; set; } public string SchoolName { get; set; } } static void Main(string[] args) { IList<School> schools = new List<School>(); schools.Add(new School() { SchoolId = 1, SchoolName = "BeiJing Middle School " }); schools.Add(new School() { SchoolId = 2, SchoolName = "ShangHai Middle School " }); schools.Add(new School() { SchoolId = 8, SchoolName = "GuangZhou Middle School " }); IList<Student> students = new List<Student>(); students.Add(new Student() { StuId = 1, Name = "Li Lei", Age = 9, Class = "Grade Three", SchoolId = 1 }); students.Add(new Student() { StuId = 2, Name = "Han Meimei", Age = 9, Class = "Grade Three", SchoolId = 1 }); students.Add(new Student() { StuId = 3, Name = "Li Ming", Age = 6, Class = "Grade One", SchoolId = 2 }); students.Add(new Student() { StuId = 4, Name = "Zou Qi", Age = 7, Class = "Grade One", SchoolId = 2 }); students.Add(new Student() { StuId = 5, Name = "Wang Long", Age = 7, Class = "Grade One", SchoolId = 1 }); }
1,、投影操作符
var studentSelect = from s in students where s.Age == 9 select new { s.Name, s.Class };
var studentS = students.Where(s => s.Age == 9); foreach (var s in studentSelect) { Console.WriteLine(s.Name + "\n"); }
輸出結(jié)果如下: Li Lei Han Meimei
2) SelectMany 將序列的每個元素投影到 IEnumerable<T> 并將結(jié)果序列合并為一個序列,。
var studentSelectMany = students.SelectMany(s => s.Name.Split(' '));
foreach (string s in studentSelectMany) { Console.WriteLine(s); }
輸出結(jié)果如下: Li
2,、限制操作符 下面示例從學(xué)生集合中過濾名叫LiLei的學(xué)生,,代碼如下:
IEnumerable<Student> studentWhere = students.Where(p => p.Name.Equals("Li Lei"));
foreach (Student student in studentWhere) { Console.WriteLine(student.Name); }
Li Lei
上面代碼建立查詢語句,但這個語句并沒用立即執(zhí)行,,而是在后面用foreach遍歷stu對象時,,查詢語句才是執(zhí)行,這就是所謂的延遲執(zhí)行,。LINQ的查詢語句執(zhí)行有兩種方式,,一種是延遲執(zhí)行,一種是立即執(zhí)行,。任何返回單個值的LINQ查詢都會立即執(zhí)行,,如我們查詢學(xué)生集合中年齡7歲的學(xué)生個數(shù):var count = (from student in students where student.Age == 7 select student).Count();
3、排序操作符
IEnumerable<Student> studentOrderBy = from student in students orderby student.Age descending select student;
foreach (Student student in studentOrderBy) { Console.WriteLine(student.Name + " " + student.Age); }
輸出結(jié)果如下: Li Lei 9
2)OrderByDescending 這個操作符和OrderBy相反,,它是對序列元素按選定的鍵進(jìn)行降序排列 如對學(xué)生集合中的學(xué)生按照年齡進(jìn)行降序序排序:
IEnumerable<Student> studentOrderByDescending = from student in students orderby student.Age descending select student;
foreach (Student student in studentOrderByDescending) { Console.WriteLine(student.Name + " " + student.Age); }
輸出結(jié)果如下: Li Lei 9
3)ThenBy 該操作符實現(xiàn)序列元素按照次關(guān)鍵字進(jìn)行升序排序
IEnumerable<Student> studentThenBy = students.OrderBy(p => p.Age).ThenBy(p => p.Name);
foreach (Student student in studentThenBy) { Console.WriteLine(student.Name + "\t" + student.Age); }
輸出結(jié)果如下: Li Ming 6
我們發(fā)現(xiàn)ThenBy操作符并不能在查詢方法中使用只能在方法語法中使用,。熟悉寫SQL語句的讀者可能會問:IEnumerable<Student> sds = students.OrderBy(p => p.Age).ThenBy(p => p.Name);和IEnumerable<Student> sds = students.OrderBy(p => p.Age).OrderBy(p => p.Name);這兩個查詢語句結(jié)果有區(qū)別嗎?區(qū)別是前者是在對主關(guān)鍵字排序結(jié)果上對那些主關(guān)鍵字相同的元素應(yīng)用ThenBy指定的次關(guān)鍵字進(jìn)行排序,,后者是在對第一個OrderBy指定的關(guān)鍵字升序排序的基礎(chǔ)上對所有的元素進(jìn)行第二個OrderBy指定的關(guān)鍵字進(jìn)行升序排序,,毫無疑問,后者第一個OrderBy其實對于結(jié)果是沒有意義的,,因為第二個OrderBy是針對全部元素的,。
4)ThenByDecending 該操作符和ThenBy相反,它按次關(guān)鍵字降序排序
IEnumerable<Student> studentThenByDescending = students.OrderBy(p => p.Age).ThenByDescending(p => p.Name);
foreach (Student student in studentThenByDescending) { Console.WriteLine(student.Name + "\t" + student.Age); }
輸出結(jié)果如下: Li Ming 6
5)Reverse 該操作符對序列中的元素進(jìn)行反轉(zhuǎn)(逆序)排序
IEnumerable<Student> studentThenReverse = students.Reverse();
foreach (Student student in studentThenReverse) { Console.WriteLine(student.Name); }
輸出結(jié)果如下: Wang Long
4,、聯(lián)接操作符 如學(xué)生集合類根據(jù)SchoolId和學(xué)校集合類的SchoolId匹配聯(lián)接,,產(chǎn)生一個新的序列,。
var studentJoin = students.Join(schools, s => s.SchoolId, c => c.SchoolId,
(s, c) => new { StudentName = s.Name, SchoolName = c.SchoolName }); foreach (var s in studentJoin) { Console.WriteLine("StudentName:{0}\t SchoolName:{1}", s.StudentName, s.SchoolName); }
輸出結(jié)果如下: StudentName:Li Lei SchoolName:BeiJing Middle School
2)GroupJoin 該操作符將主數(shù)據(jù)源的每一個元素和次數(shù)據(jù)源的相應(yīng)的值(根據(jù)某個匹配條件)聯(lián)接起來,返回一個具有層級的結(jié)果集 var studentGroupJoin = schools.GroupJoin(students, s => s.SchoolId, c => c.SchoolId,
(ss, stus) => new { SchoolName = ss.SchoolName, Students = stus.Select(p => p.Name) }); foreach (var s in studentGroupJoin) { Console.WriteLine(s.SchoolName + "\n"); foreach (var name in s.Students) { Console.WriteLine(" " + name); } }
輸出結(jié)果如下: BeiJing Middle School Li Lei Li Ming
var studentGroupBy = students.GroupBy(p => p.SchoolId);
foreach (var st in studentGroupBy) { Console.WriteLine(st.Key + "\n"); foreach (var s in st) { Console.WriteLine(" " + s.Name); } }
輸出結(jié)果如下: Li Lei Li Ming
var studentConcat = students.Select(s => s.Name).Concat(schools.Select(c => c.SchoolName));
foreach (var st in studentConcat) { Console.WriteLine(st); }
下面演示使用Aggregate操作符返回學(xué)生集合中所有的學(xué)生名字: var studentAggregate = students.Select(s => s.Name).Aggregate((n, next) => n + " , " + next);
Console.WriteLine(studentAggregate);
輸出結(jié)果如下:
var avgAge = students.Select(s => s.Age).Average();
Console.WriteLine(avgAge);
7.6
var count = students.Count;
Console.WriteLine(count);
5
var longCount = students.LongCount();
Console.WriteLine(longCount);
輸出結(jié)果如下: 5
var max = students.Max(p => p.Age);
Console.WriteLine(max);
9
var min = students.Min(p => p.Age);
Console.WriteLine(min);
輸出結(jié)果如下:6
var sum = students.Sum(p => p.Age);
Console.WriteLine(sum);
輸出結(jié)果如下: 38
var ages = students.Select(p => p.Age).Distinct();
foreach (int age in ages) { Console.WriteLine(age); }
輸出結(jié)果如下:
如查詢students的StuId和schools的SchoolId的并集,代碼如下: var studentUnion = students.Select(s => s.StuId).Union(schools.Select(c => c.SchoolId));
foreach (int id in studentUnion) { Console.WriteLine(id); }
輸出結(jié)果如下:
3)Intersect 該操作符返回同時存在兩個系列中的元素,,也就是返回兩個系列元素的交集∩
var studentIntersect = students.Select(s => s.StuId).Intersect(schools.Select(c => c.SchoolId));
foreach (int id in studentIntersect) { Console.WriteLine(id); }
var studentExcept = students.Select(s => s.StuId).Except(schools.Select(c => c.SchoolId));
foreach (int id in studentExcept) { Console.WriteLine(id); } 輸出結(jié)果如下:
string[] names1 = { "Hartono, Tommy" };
string[] names2 = { "Adams, Terry", "Andersen, Henriette Thaulow", "Hedlund, Magnus", "Ito, Shu" }; string[] names3 = { "Solanki, Ajay", "Hoeing, Helge", "Andersen, Henriette Thaulow", "Potra, Cristina", "Iallo, Lucio" }; List<string[]> namesList = new List<string[]> { names1, names2, names3 }; IEnumerable<string> allNames = namesList.Aggregate(Enumerable.Empty<string>(), (current, next) => next.Length > 4 ? current.Union(next) : current); foreach (string name in allNames) { Console.WriteLine(name); }
輸出結(jié)果如下:
var nums = Enumerable.Range(1, 5).Select(n => n * 5);
foreach (int num in nums) { Console.WriteLine(num); }
輸出結(jié)果如下:
3)Repeat 該操作符創(chuàng)建一個但值序列,,將次值重復(fù)一定的次數(shù),。
var str = Enumerable.Repeat("test", 3);
foreach (string s in str) { Console.WriteLine(s); }
許多類型都實現(xiàn)了IEnumerable。其中一部分還實現(xiàn)了與IEnumerable一模一樣的公共方法,。比如說,,如果你有一個實現(xiàn)了Where方法的類型MyList(跟IEnumerable<T>一樣),那么在MyList上調(diào)用Where將使用MyList的Where實現(xiàn),。如果先調(diào)用AsEnumerable方法然后再調(diào)用Where的話,,你調(diào)用的就是IEnumerable的Where方法而不是MyList的。
class MyList<T> : List<T>
{ public IEnumerable<T> Where(Func<T, bool> predicate) { Console.WriteLine("調(diào)用MyList的Where方法"); return Enumerable.Where(this, predicate); } } pulic void AsE() { MyList<string> names = new MyList<string> { "Li Lei", "Han Meimei", "Li Long", "Wang Ming", "Zou Qi", "Chen Fei", "Wang Tian" }; //這里調(diào)用MyList的where方法 IEnumerable<string> query1 = names.Where(name => name.Contains("Li")); Console.WriteLine(query1.Count()); //這里隱藏了MyList的where方法,,而是調(diào)用了Enmerable的Where方法,。 IEnumerable<string> query2 = names.AsEnumerable().Where(name => name.Contains("Li")); Console.WriteLine(query2.Count()); }
如下面代碼演示通過使用Cast操作符,,可以將標(biāo)準(zhǔn)操作符應(yīng)用于查詢ArrayList類型對象的數(shù)據(jù)源(ArrayList沒有實現(xiàn)IEnumerable<Of T>)
ArrayList nameList = new ArrayList();
nameList.Add("Li Lei"); nameList.Add("Li Ming"); nameList.Add("Han Meimei"); IEnumerable<string> query = nameList.Cast<string>().Where(name => name.Contains("Li")); foreach (string s in query) { Console.WriteLine(s); }
ArrayList nameList2 = new ArrayList();
nameList2.Add(4); nameList2.Add("Li Ming"); nameList2.Add("Han Meimei"); nameList2.Add(1); nameList2.Add(2); IEnumerable<int> query3 = nameList2.OfType<int>(); foreach (int i in query3) { Console.WriteLine(i); }
var studentToArray = students.Select(student => student.Name).ToArray();
Console.WriteLine(studentToArray.Count());
輸出結(jié)果如下:
var studentToDictionary = students.ToDictionary(st => st.StuId);
foreach (KeyValuePair<int, Student> keyValuePair in studentToDictionary) { Console.WriteLine("Key:{0},Name:{1}", keyValuePair.Key, keyValuePair.Value.Name); }
輸出結(jié)果如下:
List<string> studenToList = (from student in students select student.Name).ToList();
foreach (string s in studenToList) { Console.WriteLine(s); }
var lookUp = students.ToLookup(st => st.Class);
foreach (var grouping in lookUp) { Console.WriteLine(grouping.Key + "\n"); foreach (var student in grouping) { Console.WriteLine(" " + student.Name); } } Li Lei Li Ming
var studentsDefaultIfEmpty = students.Where(s => s.Age == 20).DefaultIfEmpty(new Student() { Name = "默認(rèn)值" });
foreach (Student student in studentsDefaultIfEmpty) { Console.WriteLine(student.Name); }
輸出結(jié)果如下:
var studentEle = students.ElementAt(1);
Console.WriteLine(studentEle.Name);
輸出結(jié)果如下:
如果該索引超出范圍,,則返回默認(rèn)值,如果是引用類型返回為Null,,如果是值類型則返回為0,。如下面獲取索引為100的學(xué)生,因為索引范圍沒有那么大,,索引返回為null,。
var studentEleD = students.ElementAtOrDefault(100);
Console.WriteLine(studentEleD == null ? "返回默認(rèn)值NULL" : studentEleD.Name);
var studentFirst = students.First();
Console.WriteLine(studentFirst.Name);
var studentLast = students.Last();
Console.WriteLine(studentLast.Name);
輸出結(jié)果如下:
var studentFirstD = students.Where(s => s.Age == 20).FirstOrDefault();
Console.WriteLine(studentFirstD == null ? "返回默認(rèn)值NULL" : studentFirstD.Name);
var studentLastD = students.Where(s => s.Age == 20).FirstOrDefault();
Console.WriteLine(studentFirstD == null ? "返回默認(rèn)值NULL" : studentLastD.Name);
var studentSingle = students.Single(s => s.Name.Equals("Li Lei"));
Console.WriteLine(studentSingle.Name);
元素為引用類型默認(rèn)值為null,,值類型默認(rèn)值為0.注意如果符合條件的元素有多個,,會拋出異常。
var studentSingleDefault = students.SingleOrDefault(s => s.SchoolId.Equals(4));
Console.WriteLine(studentSingleDefault == null ? "返回默認(rèn)值null" : studentSingleDefault.Name);
var studentS1 = students.Where(s => s.SchoolId == 1);
var studentS2 = students.Where(s => s.SchoolId == 1); Console.WriteLine(studentS1.SequenceEqual(studentS2));
輸出結(jié)果如下:
bool blAll = students.All(s => s.Name.StartsWith("L"));
Console.WriteLine(blAll); 輸出結(jié)果如下:
bool blAny = students.Any(s => s.Name.StartsWith("L"));
Console.WriteLine(blAny);
輸出結(jié)果如下:
string studentName = "Li Lei";
bool blContains = students.Select(s => s.Name).Contains(studentName); Console.WriteLine(blContains); 輸出結(jié)果如下:
var studentSkip = students.Skip(2);
foreach (Student student in studentSkip) { Console.WriteLine(student.Name); }
輸出結(jié)果如下:
這里需要特別注意,,SkipWhile對數(shù)據(jù)源進(jìn)行枚舉,從第一個枚舉得到的元素開始,,如果返回true,,則跳過該元素,繼續(xù)進(jìn)行枚舉操作。但是,如果一旦返回false,,則該元素以后的所有元素,都不會再調(diào)用SkipWhile,。
int[] numSkipWhile = new int[] { 5, 7, 9, 6, 9, 4 };
var numSw = numSkipWhile.SkipWhile(n => n < 9); foreach (int i in numSw) { Console.WriteLine(i); }
輸出結(jié)果為:
int[] numTake = new int[] { 5, 7, 9, 6, 9, 4 };
var numTk = numTake.Take(3); foreach (int i in numTk) { Console.WriteLine(i); } 輸出結(jié)果為 :
int[] numTaleWhile = new int[] { 5, 7, 9, 6, 9, 4 };
var numTw = numTaleWhile.TakeWhile(n => n < 9); foreach (int i in numTw) { Console.WriteLine(i); }
輸出結(jié)果為:
|
|