c#中LINQ的基本用法(二)
本文主要介紹LINQ查詢操作符
LINQ查詢?yōu)樽畛S玫牟僮鞣x了一個(gè)聲明語(yǔ)法。還有許多查詢操作符可用于Enumerable類。
下面的例子需要用到LINQ基礎(chǔ)(一)(http://www.dbjr.com.cn/article/244208.htm)的一些代碼
1.篩選
LINQ查詢使用where子句添加條件表達(dá)式來(lái)篩選,where子句可以合并多個(gè)表達(dá)式。
var racers = from r in Formula1.GetChampions()
where r.Wins>15 &&
(r.Country == "Brazil" || r.Country =="Austria")
select r;
foreach(var r in racers)
{
Console.WriteLine("{0:A}", r);
}上述LINQ表達(dá)式映射為C# LINQ查詢的擴(kuò)展方法:
var racers = Formula1.GetChampions().Where(r =>r.Wins>15 && (r.Country == "Brazil" || r.Country =="Austria")).Select(r => r);
注意,并不是所以查詢都可以使用LINQ查詢語(yǔ)法,也不是所有的擴(kuò)展方法都映射到LINQ查詢。高級(jí)查詢需要使用擴(kuò)展方法。
2.用索引篩選
不能使用LINQ查詢的一個(gè)例子是Where()方法的重載。在WHere()方法的重載中,可以傳遞第二個(gè)參數(shù)————索引。索引是篩選器返回的每個(gè)結(jié)果的計(jì)數(shù)器。可以在表達(dá)式中使用這個(gè)索引,執(zhí)行基于索引的計(jì)算:
var racers = Formula1.GetChampions().
Where((r, index) => r.LastName.StartsWith("A") && index % 2 != 0);
foreach (var r in racers)
{
Console.WriteLine("{0:A}", r);
}3.類型篩選
為了進(jìn)行基于類型的篩選,可以使用OfType()擴(kuò)展方法。
object[] data = { "one", 2, 3, "four", "five", 6 };
var query = data.OfType<string>();
foreach (var s in query)
{
Console.WriteLine(s);
}輸出:
one four five
從集合僅返回字符串。
4.復(fù)合的from子句
如果需要根據(jù)對(duì)象的成員進(jìn)行篩選,而該成員本身是一個(gè)系列,就可以使用復(fù)合from子句。例如,LINQ基礎(chǔ)(一)(http://www.dbjr.com.cn/article/244208.htm)中的Racer類定義了一個(gè)屬性Cars,Cars是一個(gè)字符串?dāng)?shù)組。
篩選駕駛Ferrari的所以冠軍:
var ferrariDrivers = from r in Formula1.GetChampions()
from c in r.Cars
where c == "Ferrari"
orderby r.LastName
select r.FirstName + " " + r.LastName;
foreach (var racer in ferrariDrivers)
{
Console.WriteLine(racer);
}第一個(gè)from子句訪問(wèn)Formula1.GetChampions()方法返回的Racer對(duì)象,第二個(gè)from子句訪問(wèn)Racer類的Cars屬性,以返回所以sting類型的賽車。
C#編譯器把復(fù)合的from子句和LINQ查詢轉(zhuǎn)換為SelectMany()擴(kuò)展方法。SelectMany()擴(kuò)展方法可以迭代序列中的序列。
SelectMany()的重載版本:
public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector);
第一個(gè)參數(shù)是隱式參數(shù),它從Formula1.GetChampions()方法接受Racer對(duì)象序列。第二個(gè)參數(shù)是collectionSelector委托,其中定義了內(nèi)部序列,是序列的序列,本例子為Cars。第三個(gè)參數(shù)也是一個(gè)委托,為每個(gè)Racer對(duì)象的Cars屬性的每個(gè)元素調(diào)用這個(gè)委托。
這里Cars是一個(gè)字符串?dāng)?shù)組,會(huì)將每個(gè)Racer和每個(gè)字符串作為參數(shù),調(diào)用這個(gè)委托。
var ferrariDrivers = Formula1.GetChampions().SelectMany(
c => c.Cars, (r, s) => new { Racer=r,Car =s}).Where(
s =>s.Car == "Ferrari").OrderBy(
r => r.Racer.LastName).Select(r => r.Racer.FirstName + " " + r.Racer.LastName);
foreach (var racer in ferrariDrivers)
{
Console.WriteLine(racer);
}5.排序
要對(duì)序列排序,可以使用前面使用過(guò)的orderby.也可以使用orderrby descending子句(降序)。
var racers = (from r in Formula1.GetChampions()
orderby r.Country descending
select r);
foreach (var racer in racers)
{
Console.WriteLine("{0}: {1}, {2}", racer.Country, racer.LastName, racer.FirstName);
}orderby子句解析為OrderBy()方法,orderby r.Country descending解析為OrderByDescending()方法:
var racers = Formula1.GetChampions().OrderByDescending(r => r.Country).Select(r=>r);
OrderBy()和OrderByDescending()方法返回IOrderEnumerable<TSource>。這個(gè)接口派生自IEnumerable<TSource>接口,但包含一個(gè)額外的方法CreateOrderEnumerable<TSource>()方法。這個(gè)方法用于進(jìn)一步給序列排序,可以在最后一個(gè)參數(shù)指定升序還是降序:
// 摘要:
// 根據(jù)某個(gè)鍵對(duì) System.Linq.IOrderedEnumerable<TElement> 的元素執(zhí)行后續(xù)排序。
//
// 參數(shù):
// keySelector:
// 用于提取每個(gè)元素的鍵的 System.Func<T,TResult>。
//
// comparer:
// 用于比較鍵在返回序列中的位置的 System.Collections.Generic.IComparer<T>。
//
// descending:
// 如果為 true,則對(duì)元素進(jìn)行降序排序;如果為 false,則對(duì)元素進(jìn)行升序排序。
//
// 類型參數(shù):
// TKey:
// keySelector 生成的鍵的類型。
//
// 返回結(jié)果:
// 一個(gè) System.Linq.IOrderedEnumerable<TElement>,其元素按鍵排序。
IOrderedEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending);例子:
// Create an array of strings to sort.
string[] fruits = { "apricot", "orange", "banana", "mango", "apple", "grape", "strawberry" };
// First sort the strings by their length.
IOrderedEnumerable<string> sortedFruits2 =
fruits.OrderBy(fruit => fruit.Length);
// Secondarily sort the strings alphabetically, using the default comparer.
IOrderedEnumerable<string> sortedFruits3 =
sortedFruits2.CreateOrderedEnumerable<string>(
fruit => fruit,
Comparer<string>.Default, false);使用ThenBy和ThenByDescending()方法進(jìn)行進(jìn)一步排序,可以添加任意多個(gè):
var racers = Formula1.GetChampions().OrderByDescending(r => r.Country).ThenByDescending( r => r.LastName).ThenByDescending(r => r.FirstName).Select(r => r);
6.分組
要根據(jù)一個(gè)關(guān)鍵字值對(duì)查詢結(jié)果分組,可以使用group子句。
// group r by r.Country into g 根據(jù)Country屬性組合所有的賽車手,并定義為一個(gè)新的集合g,用于訪問(wèn)分組的結(jié)果信息。
//select子句創(chuàng)建一個(gè)帶Country和Count屬性的匿名類型。Country = g.Key Key是r.Country
var countries = from r in Formula1.GetChampions()
group r by r.Country into g
orderby g.Count() descending, g.Key
where g.Count() >= 2
select new
{
Country = g.Key,
Count = g.Count()
};
foreach (var item in countries)
{
Console.WriteLine("{0, -10} {1}", item.Country, item.Count);
}輸出:

使用擴(kuò)展方法執(zhí)行相同的操作,把group r by r.Country 子句解析為GroupBy()方法。在GroupBy()方法的聲明中,它返回實(shí)現(xiàn)了IGrouping<TKey, TSource>接口的枚舉對(duì)象。IGrouping<TKey, TSource>接口定義了Key屬性,所以在調(diào)用了這個(gè)方法后,可以訪問(wèn)分組的關(guān)鍵字:
public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);
使用GroupBy方法:
var countries = Formula1.GetChampions().GroupBy(r => r.Country).OrderByDescending(
g => g.Count()).ThenBy(g => g.Key).Where(g => g.Count() >= 2).Select(
g=>new
{
Country = g.Key,
Count = g.Count()
});7.對(duì)嵌套的對(duì)象分組
如果得到的分組的對(duì)象需要包含嵌套的序列,就可以改變select子句創(chuàng)建匿名類型。
//返回的對(duì)象不僅需要包含國(guó)家名和賽車手這兩個(gè)屬性,還應(yīng)包含賽車手集合。
//使用from r1 in g orderby r1.LastName select r1.FirstName + " " + r1.LastName 內(nèi)部子句
var countries = from r in Formula1.GetChampions()
group r by r.Country into g
orderby g.Count() descending, g.Key
where g.Count() >= 2
select new
{
Country = g.Key,
Count = g.Count(),
Racers = from r1 in g
orderby r1.LastName
select r1.FirstName + " " + r1.LastName
};
foreach (var item in countries)
{
Console.WriteLine("{0, -10} {1}", item.Country, item.Count);
foreach (var name in item.Racers)
{
Console.Write("{0}; ", name);
}
Console.WriteLine();
}8.內(nèi)連接
使用join子句可以根據(jù)特定的條件合并兩個(gè)數(shù)據(jù)源,但之前要獲得兩個(gè)連接的列表。
使用了LINQ基礎(chǔ)(一)(http://www.dbjr.com.cn/article/244208.htm)的代碼
//GetChampions獲得冠軍賽車手
var racers = from r in Formula1.GetChampions()
from y in r.Years
select new
{
Year = y,
Name = r.FirstName + " " + r.LastName
};
//GetContructorChampions獲取冠軍車隊(duì)
var teams = from t in Formula1.GetContructorChampions()
from y in t.Years
select new
{
Year = y,
Name = t.Name
};
//得到每一年獲得冠軍的賽車手和車隊(duì)
//通過(guò)join t in teams on r.Year equals t.Year into rt 子句連接兩個(gè)數(shù)據(jù)源
var racersAndTeams =
(from r in racers
join t in teams on r.Year equals t.Year into rt
from t in rt.DefaultIfEmpty()
orderby r.Year
select new
{
Year = r.Year,
Champion = r.Name,
Constructor = t == null ? "no constructor championship" : t.Name
});
Console.WriteLine("Year Champion\t\t Constructor Title");
foreach (var item in racersAndTeams)
{
Console.WriteLine("{0}: {1,-20} {2}",
item.Year, item.Champion, item.Constructor);
}9.左連接
使用內(nèi)連接返回匹配r.Year equals t.Year的結(jié)果。左連接返回左邊數(shù)據(jù)源的全部元素,即使在右邊的數(shù)據(jù)源中沒(méi)有匹配的元素。
var racers = from r in Formula1.GetChampions()
from y in r.Years
select new
{
Year = y,
Name = r.FirstName + " " + r.LastName
};
var teams = from t in Formula1.GetContructorChampions()
from y in t.Years
select new
{
Year = y,
Name = t.Name
};
//左連接用join和DefaultIfEmpty方法定義。
//如果查詢到左側(cè)數(shù)據(jù)源沒(méi)有和右邊數(shù)據(jù)源Year相同的結(jié)果,使用DefaultIfEmpty方法定義右側(cè)的默認(rèn)值(為空)
var racersAndTeams =
(from r in racers
join t in teams on r.Year equals t.Year into rt
from t in rt.DefaultIfEmpty()
orderby r.Year
select new
{
Year = r.Year,
Champion = r.Name,
Constructor = t == null ? "no constructor championship" : t.Name
});
Console.WriteLine("Year Champion\t\t Constructor Title");
foreach (var item in racersAndTeams)
{
Console.WriteLine("{0}: {1,-20} {2}",
item.Year, item.Champion, item.Constructor);
}10.組連接
組連接類似內(nèi)連接,內(nèi)連接通過(guò)某一項(xiàng)連接兩個(gè)數(shù)據(jù)源(如 r.Year equals t.Year),組連接使用一組項(xiàng)連接,例如下面的例子,
通過(guò)
new
{
FirstName = r.FirstName,
LastName = r.LastName
}
equals
new
{
FirstName = r2.FirstName,
LastName = r2.LastName
}連接兩個(gè)數(shù)據(jù)源
var racers = Formula1.GetChampionships()
.SelectMany(cs => new List<RacerInfo>()
{
new RacerInfo {
Year = cs.Year,
Position = 1,
FirstName = cs.First.FirstName(),
LastName = cs.First.LastName()
},
new RacerInfo {
Year = cs.Year,
Position = 2,
FirstName = cs.Second.FirstName(),
LastName = cs.Second.LastName()
},
new RacerInfo {
Year = cs.Year,
Position = 3,
FirstName = cs.Third.FirstName(),
LastName = cs.Third.LastName()
}
});
var q = (from r in Formula1.GetChampions()
join r2 in racers on
new
{
FirstName = r.FirstName,
LastName = r.LastName
}
equals
new
{
FirstName = r2.FirstName,
LastName = r2.LastName
}
into yearResults
select new
{
FirstName = r.FirstName,
LastName = r.LastName,
Wins = r.Wins,
Starts = r.Starts,
Results = yearResults
});
foreach (var r in q)
{
Console.WriteLine("{0} {1}", r.FirstName, r.LastName);
foreach (var results in r.Results)
{
Console.WriteLine("{0} {1}", results.Year, results.Position);
}
}11.集合操作
擴(kuò)展方法Distinct(),Union(),Intersect()(獲取交集),Except()都是集合操作。
//獲取同時(shí)駕駛Ferrari和駕駛McLaren獲得過(guò)冠軍的賽車手
static void SetOperations()
{
//定義一個(gè)委托,用來(lái)查詢駕駛Ferrari獲得過(guò)冠軍的賽車手和駕駛McLaren獲得過(guò)冠軍的賽車手
Func<string, IEnumerable<Racer>> racersByCar =
car => from r in Formula1.GetChampions()
from c in r.Cars
where c == car
orderby r.LastName
select r;
Console.WriteLine("World champion with Ferrari and McLaren");
//使用Intersect方法獲取兩個(gè)數(shù)據(jù)源的交集
foreach (var racer in racersByCar("Ferrari").Intersect(racersByCar("McLaren")))
{
Console.WriteLine(racer);
}
}12.合并
Zip()方法是.NET 4.0新增的,允許用一個(gè)為此函數(shù)把兩個(gè)相關(guān)的序列合并為一個(gè)。
對(duì)于合并,第一個(gè)集合中的第一項(xiàng)與第二個(gè)集合的第一項(xiàng)合并,第一個(gè)集合中的第二項(xiàng)與第二個(gè)集合的第二項(xiàng)合并,以此類推。如果兩個(gè)序列的項(xiàng)數(shù)不同,Zip()方法就會(huì)在達(dá)到較小集合的末尾時(shí)停止。
第一個(gè)集合中的元素有一個(gè)Name屬性,第二個(gè)集合中的元素有LastName和Starts屬性。
在racerNames集合上使用Zip()方法,需要把第二個(gè)集合racerNamesAndStarts作為第一個(gè)參數(shù)。第二個(gè)參數(shù)是一個(gè)委托,它通過(guò)參數(shù)first接受第一個(gè)集合的元素,通過(guò)參數(shù)second接受第二個(gè)集合的元素。其實(shí)現(xiàn)代碼返回一個(gè)字符串:
var racerNames = from r in Formula1.GetChampions()
where r.Country == "Italy"
orderby r.Wins descending
select new
{
Name = r.FirstName + " " + r.LastName
};
var racerNamesAndStarts = from r in Formula1.GetChampions()
where r.Country == "Italy"
orderby r.Wins descending
select new
{
LastName = r.LastName,
Starts = r.Starts
};
var racers = racerNames.Zip(racerNamesAndStarts, (first, second) => first.Name + ", starts: " + second.Starts);
foreach (var r in racers)
{
Console.WriteLine(r);
}13.分區(qū)
擴(kuò)展方法Take()和Skip()等的分區(qū)操作可用于分頁(yè)。
例如,在第一頁(yè)只顯示5個(gè)賽車手,下一頁(yè)顯示接下來(lái)的5個(gè)賽車手...
Skip(page * pageSize)方法調(diào)到指定索引出,忽略前面的數(shù)據(jù)。Take(pageSize)方法顯示pageSize條數(shù)據(jù)
int pageSize = 5;
int numberPages = (int)Math.Ceiling(Formula1.GetChampions().Count() /
(double)pageSize);
for (int page = 0; page < numberPages; page++)
{
Console.WriteLine("Page {0}", page);
var racers =
(from r in Formula1.GetChampions()
orderby r.LastName, r.FirstName
select r.FirstName + " " + r.LastName).
Skip(page * pageSize).Take(pageSize);
foreach (var name in racers)
{
Console.WriteLine(name);
}
Console.WriteLine();
}TakeWhile()和SkipWhile()方法,傳遞一個(gè)委托,滿足這個(gè)條件的數(shù)據(jù)就提取或跳轉(zhuǎn):
public static IEnumerable<TSource> SkipWhile<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
14.聚合操作符
聚合操作符(如Count(),Sum(),Min(),Max(),Average(),Aggregate())不返回一個(gè)序列,而是返回一個(gè)值。
例如,使用Count方法應(yīng)用于Racer的Years屬性,篩選獲得冠軍次數(shù)超過(guò)3次的賽車手。因?yàn)槎啻问褂胷.Years.Count(),所以使用let子句定義了一個(gè)變量。
var query = from r in Formula1.GetChampions()
let numberYears = r.Years.Count()
where numberYears >= 3
orderby numberYears descending, r.LastName
select new
{
Name = r.FirstName + " " + r.LastName,
TimesChampion = numberYears
};
foreach (var r in query)
{
Console.WriteLine("{0} {1}", r.Name, r.TimesChampion);
}Aggregate()方法傳遞一個(gè)委托,將數(shù)據(jù)源中的每個(gè)元素作為委托的參數(shù),并使用指定的函數(shù)累加。
15.轉(zhuǎn)換操作符
LINQ基礎(chǔ)(一)(http://www.dbjr.com.cn/article/244208.htm)提到,查詢會(huì)推遲到迭代數(shù)據(jù)項(xiàng)時(shí)才執(zhí)行,使用轉(zhuǎn)換操作符會(huì)立即執(zhí)行查詢,把查詢結(jié)果放在數(shù)組,列表和字典中。
//轉(zhuǎn)換為數(shù)組
var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" };
var namesWithJ = (from n in names
where n.StartsWith("J")
orderby n
select n).ToList();轉(zhuǎn)換為L(zhǎng)ookup<TKey,TElement>
//把Car賽車屬性作為鍵,每個(gè)鍵關(guān)聯(lián)多個(gè)車手Racer
var racers = (from r in Formula1.GetChampions()
from c in r.Cars
select new
{
Car = c,
Racer = r
}).ToLookup(cr => cr.Car, cr => cr.Racer);
foreach (var v in racers)
{
Console.Write(v.Key+"........");
foreach (var k in racers[v.Key])
{
Console.WriteLine(k);
}
}ToLookup(cr => cr.Car, cr => cr.Racer)方法的一個(gè)重載版本傳遞一個(gè)鍵和一個(gè)元素選擇器
如果需要在非類型化的集合上使用LINQ查詢,可以使用Cast()方法,定義強(qiáng)類型化的查詢:
var list = new System.Collections.ArrayList(Formula1.GetChampions() as System.Collections.ICollection);
var query = from r in list.Cast<Racer>()
where r.Country == "USA"
orderby r.Wins descending
select r;
foreach (var racer in query)
{
Console.WriteLine("{0:A}", racer);
}Cast<Racer>()將 System.Collections.IEnumerable 的元素強(qiáng)制轉(zhuǎn)換為指定的類型。
16.生成操作符
生成操作符Range(),Empty(),Repeat()方法不是擴(kuò)展方法,而是返回序列的正常靜態(tài)方法。在LING to Object中,這些方法可用于Enumerable類。
Range()方法用來(lái)填充一個(gè)范圍的數(shù)字。第一個(gè)參數(shù)作為起始值,第二個(gè)參數(shù)作為要填充的項(xiàng)數(shù):
var values = Enumerable.Range(1,20);
結(jié)果為1至20的集合。
可以把該結(jié)果與其它擴(kuò)展方法合并:
var values = Enumerable.Range(1,20).Select(n=> n*3);
Empty()方法返回一個(gè)不返回值的迭代器,它用于需要一個(gè)集合的參數(shù),其中可以給參數(shù)傳遞空集合。
Repeat()方法返回指定個(gè)數(shù)的重復(fù)值的集合迭代器。
到此這篇關(guān)于c#中LINQ的基本用法的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
淺談c#.net中巧用ToString()將日期轉(zhuǎn)成想要的格式
有時(shí)候我們要對(duì)時(shí)間進(jìn)行轉(zhuǎn)換,達(dá)到不同的顯示效果,更多的該怎么辦呢?2013-03-03
C#使用GZipStream解壓縮數(shù)據(jù)文件的方法
這篇文章主要介紹了C#使用GZipStream解壓縮數(shù)據(jù)文件的方法,實(shí)例分析了C#中GZipStream方法的原理與使用技巧,需要的朋友可以參考下2015-04-04
Unity3D使用陀螺儀控制節(jié)點(diǎn)旋轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了Unity3D使用陀螺儀控制節(jié)點(diǎn)旋轉(zhuǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
C#實(shí)現(xiàn)通過(guò)winmm.dll控制聲音播放的方法
這篇文章主要介紹了C#實(shí)現(xiàn)通過(guò)winmm.dll控制聲音播放的方法,很實(shí)用的功能,需要的朋友可以參考下2014-08-08

