C#中的LINQ?to?Objects詳解(2)
相關(guān)文章:
四、Linq和反射
.NET Framework 類庫反射 API 可用于檢查 .NET 程序集中的元數(shù)據(jù),以及創(chuàng)建位于該程序集中的類型、類型成員、參數(shù)等等的集合。 因?yàn)檫@些集合支持泛型 IEnumerable 接口,所以可以使用 LINQ 查詢它們。
下面的示例演示了如何將 LINQ 與反射配合使用以檢索有關(guān)與指定搜索條件匹配的方法的特定元數(shù)據(jù)。 在這種情況下,該查詢將在返回?cái)?shù)組等可枚舉類型的程序集中查找所有方法的名稱。
該示例使用 GetTypes 方法返回指定程序集中的類型的數(shù)組。 將應(yīng)用 where 篩選器,以便僅返回公共類型。 對(duì)于每個(gè)公共類型,子查詢使用從 GetMethods 調(diào)用返回的 MethodInfo 數(shù)組生成。 篩選這些結(jié)果,以僅返回其返回類型為數(shù)組或?qū)崿F(xiàn) IEnumerable 的其他類型的方法。 最后,通過使用類型名稱作為鍵來對(duì)這些結(jié)果進(jìn)行分組。
Assembly assembly = Assembly.Load("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken= b77a5c561934e089");
var pubTypesQuery = from type in assembly.GetTypes()
where type.IsPublic
from method in type.GetMethods()
where method.ReturnType.IsArray == true
|| (method.ReturnType.GetInterface(typeof(System.Collections.Generic.IEnumerable<>).FullName) != null && method.ReturnType.FullName != "System.String")
group method.ToString() by type.ToString();
foreach (var groupOfMethods in pubTypesQuery)
{
Console.WriteLine("Type: {0}", groupOfMethods.Key);
foreach (var method in groupOfMethods)
{
Console.WriteLine(" {0}", method);
}
}
Console.WriteLine("Press any key to exit");五、LINQ 和字符串
1、LINQ 和文件目錄
許多文件系統(tǒng)操作實(shí)質(zhì)上是查詢,因此非常適合使用 LINQ 方法。
本部分中的查詢是非破壞性查詢。 它們不用于更改原始文件或文件夾的內(nèi)容。 這遵循了查詢不應(yīng)引起任何副作用這條規(guī)則。 通常,修改源數(shù)據(jù)的任何代碼(包括執(zhí)行創(chuàng)建/更新/刪除運(yùn)算符的查詢)應(yīng)與只查詢數(shù)據(jù)的代碼分開。
實(shí)例1、如何查詢具有指定屬性或名稱的文件
此示例演示如何查找指定目錄樹中具有指定文件擴(kuò)展名(例如“.txt”)的所有文件,還演示如何根據(jù)創(chuàng)建時(shí)間返回樹中最新或最舊的文件。
//該查詢將所有生產(chǎn)的完整路徑。txt文件指定的文件夾包括子文件夾下。
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\";
//取文件系統(tǒng)快照
var dir = new DirectoryInfo(path);
//該方法假定應(yīng)用程序在指定路徑下的所有文件夾都具有搜索權(quán)限。
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
//創(chuàng)建查詢
var fileQuery = from file in files
where file.Extension == ".html"
orderby file.Name
select file;
//執(zhí)行查詢
foreach (var file in fileQuery)
{
Console.WriteLine(file.FullName);
}
//創(chuàng)建和執(zhí)行一個(gè)新的查詢,通過查詢舊文件的創(chuàng)建時(shí)間作為一個(gè)出發(fā)點(diǎn)
//Last:選最后一個(gè),因?yàn)槭前慈掌谏?,所以最新的是指最后一個(gè)
var newestFile = (from file in fileQuery
orderby file.CreationTime
select new { file.FullName, file.CreationTime })
.Last();
Console.WriteLine($"\r\nThe newest .txt file is {newestFile.FullName}. Creation time: {newestFile.CreationTime}");
實(shí)例2、如何按照擴(kuò)展名對(duì)文件進(jìn)行分組
此示例演示如何使用 LINQ 對(duì)文件或文件夾列表執(zhí)行高級(jí)分組和排序操作。此外,它還演示如何使用 Skip 和 Take 方法對(duì)控制臺(tái)窗口中的輸出進(jìn)行分頁。
下面的查詢演示如何按文件擴(kuò)展名對(duì)指定目錄樹的內(nèi)容進(jìn)行分組。
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7";
//“path”的長度,后續(xù)用于在輸出時(shí)去掉“path”這段前綴
var trimLength = path.Length;
//取文件系統(tǒng)快照
var dir = new DirectoryInfo(path);
//該方法假定應(yīng)用程序在指定路徑下的所有文件夾都具有搜索權(quán)限。
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
//創(chuàng)建查詢
var query = from file in files
group file by file.Extension.ToLower() into fileGroup
orderby fileGroup.Key
select fileGroup;
//一次顯示一組。如果列表實(shí)體的行數(shù)大于控制臺(tái)窗口中的行數(shù),則分頁輸出。
PageOutput(trimLength, query);
private static void PageOutput(int rootLength, IOrderedEnumerable<string, FileInfo>> query)
{
//跳出分頁循環(huán)的標(biāo)志
var isAgain = true;
//控制臺(tái)輸出的高度
var numLines = Console.WindowHeight - 3;
//遍歷分組集合
foreach (var g in query)
{
var currentLine = 0;
do
{
Console.Clear();
Console.WriteLine(string.IsNullOrEmpty(g.Key) ? "[None]" : g.Key);
//從“currentLine”開始顯示“numLines”條數(shù)
var resultPage = g.Skip(currentLine).Take(numLines);
//執(zhí)行查詢
foreach (var info in resultPage)
{
Console.WriteLine("\t{0}", info.FullName.Substring(rootLength));
}
//記錄輸出行數(shù)
currentLine += numLines;
Console.WriteLine("點(diǎn)擊“任意鍵”繼續(xù),按“End”鍵退出");
//給用戶選擇是否跳出
var key = Console.ReadKey().Key;
if (key != ConsoleKey.End) continue;
isAgain = false;
break;
} while (currentLine < g.Count());
if (!isAgain)
{
break;
}
}
}
為了使您可以查看所有結(jié)果,此示例還演示如何按頁查看結(jié)果。這些方法可應(yīng)用于 Windows 和 Web 應(yīng)用程序。
請(qǐng)注意,由于代碼將對(duì)組中的項(xiàng)進(jìn)行分頁,因此需要嵌套的 foreach 循環(huán)。此外,還會(huì)使用某他某個(gè)邏輯來計(jì)算列表中的當(dāng)前位置,以及使用戶可以停止分頁并退出程序。在這種特定情況下,將針對(duì)原始查詢的緩存結(jié)果運(yùn)行分頁查詢。
實(shí)例3、如何查詢一組文件夾中的總字節(jié)數(shù)
此示例演示如何檢索指定文件夾及其所有子文件夾中的所有文件所使用的總字節(jié)數(shù)。
Sum 方法添加在 select 子句中選擇的所有項(xiàng)的值。您可以輕松修改此查詢以檢索指定目錄樹中的最大或最小文件,方法是調(diào)用 Min 或 Max 方法,而不是 Sum。
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
var query = from file in files
select file.Length;
//緩存結(jié)果,以避免多次訪問文件系統(tǒng)
var fileLengths = query as long[] ?? query.ToArray();
//返回最大文件的大小
var largestLength = fileLengths.Max();
//返回指定文件夾下的所有文件中的總字節(jié)數(shù)
var totalBytes = fileLengths.Sum();
Console.WriteLine();
Console.WriteLine("There are {0} bytes in {1} files under {2}",totalBytes, files.Count(), path);
Console.WriteLine("The largest files is {0} bytes.", largestLength);
如果您只需要統(tǒng)計(jì)特定目錄樹中的字節(jié)數(shù),則可以更高效地實(shí)現(xiàn)此目的,而無需創(chuàng)建 LINQ 查詢,因?yàn)樵摬樵儠?huì)引發(fā)創(chuàng)建列表集合作為數(shù)據(jù)源的系統(tǒng)開銷。隨著查詢復(fù)雜度的增加,或者當(dāng)您必須對(duì)同一數(shù)據(jù)源運(yùn)行多個(gè)查詢時(shí),LINQ 方法的有用性也會(huì)隨之增加。
實(shí)例4、如何比較兩個(gè)文件夾中的內(nèi)容
此示例演示比較兩個(gè)文件列表的三種方法:
- (1)查詢一個(gè)指定兩個(gè)文件列表是否相同的布爾值;
- (2)查詢用于檢索同時(shí)位于兩個(gè)文件夾中的文件的交集;
- (3)查詢用于檢索位于一個(gè)文件夾中但不在另一個(gè)文件夾中的文件的差集;
//創(chuàng)建兩個(gè)帶比較的文件夾
const string path1 = @"E:\Test1";
const string path2 = @"E:\Test2";
var dir1 = new DirectoryInfo(path1);
var dir2 = new DirectoryInfo(path2);
//取文件快照
var files1 = dir1.GetFiles("*.*", SearchOption.AllDirectories);
var files2 = dir2.GetFiles("*.*", SearchOption.AllDirectories);
//自定義文件比較器
var comparer = new FileComparer();
//該查詢確定兩個(gè)文件夾包含相同的文件列表,基于自定義文件比較器。查詢立即執(zhí)行,因?yàn)樗祷匾粋€(gè)bool。
var areIdentical = files1.SequenceEqual(files2, comparer);
Console.WriteLine(areIdentical == true ? "the two folders are the same" : "The two folders are not the same");
//交集:找相同的文件
var queryCommonFiles = files1.Intersect(files2, comparer);
var commonFiles = queryCommonFiles as FileInfo[] ?? queryCommonFiles.ToArray();
if (commonFiles.Any())
{
Console.WriteLine("The following files are in both folders:");
foreach (var v in commonFiles)
{
Console.WriteLine(v.FullName);
}
}
else
{
Console.WriteLine("There are no common files in the two folders.");
}
//差集:對(duì)比兩個(gè)文件夾的差異
var diffQuery = files1.Except(files2, comparer);
Console.WriteLine("The following files are in list1 but not list2:");
foreach (var v in diffQuery)
{
Console.WriteLine(v.FullName);
}
//該實(shí)現(xiàn)定義了一個(gè)非常簡單的兩個(gè) FileInfo 對(duì)象之間的比較。它只比較文件的名稱和它們字節(jié)數(shù)的長度
public class FileComparer : IEqualityComparer
{
public bool Equals(FileInfo x, FileInfo y)
{
return string.Equals(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase) && x.Length == y.Length;
}
//返回一個(gè)比較標(biāo)準(zhǔn)的哈希值。根據(jù) IEqualityComparer 規(guī)則,如果相等,那么哈希值也必須是相等的。
//因?yàn)檫@里所定義的相等只是一個(gè)簡單的值相等,而不是引用標(biāo)識(shí),所以兩個(gè)或多個(gè)對(duì)象將產(chǎn)生相同的哈希值是可能的。
public int GetHashCode(FileInfo obj)
{
var s = string.Format("{0}{1}", obj.Name, obj.Length);
return s.GetHashCode();
}
}
【注意】 可以修改上述這些方法以便對(duì)任意類型的對(duì)象序列進(jìn)行比較。
此處顯示的 FileComparer 類演示如何將自定義比較器類與標(biāo)準(zhǔn)查詢運(yùn)算符一起使用。該類不是為在實(shí)際方案中使用而設(shè)計(jì)的。它只是使用每個(gè)文件的名稱和長度(以字節(jié)為單位)來確定每個(gè)文件夾的內(nèi)容是否相同。在實(shí)際方案中,應(yīng)對(duì)此比較器進(jìn)行修改以執(zhí)行更嚴(yán)格的相等性檢查。
實(shí)例5、如何在目錄樹中查詢最大的文件
此示例演示與文件大?。ㄒ宰止?jié)為單位)相關(guān)的五種查詢:
- 如何檢索最大文件的大?。ㄒ宰止?jié)為單位);
- 如何檢索最小文件的大小(以字節(jié)為單位);
- 如何從指定的根文件夾下的一個(gè)或多個(gè)文件夾檢索 FileInfo 對(duì)象最大或最小文件;
- 如何檢索一個(gè)序列,如 10 個(gè)最大文件。
下面的示例包含五種不同的查詢,這些查詢演示如何根據(jù)文件大?。ㄒ宰止?jié)為單位)查詢和分組文件??梢暂p松地修改這些示例,以使查詢基于 FileInfo對(duì)象的某個(gè)其他屬性。
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
var query = from file in files
select file.Length;
//返回最大文件的大小
var maxSize = query.Max();
Console.WriteLine("The length of the largest file under {0} is {1}",path, maxSize);
//倒序排列
var query2 = from file in files
let len = file.Length
where len > 0
orderby len descending
select file;
var fileInfos = query2 as FileInfo[] ?? query2.ToArray();
//倒序排列的第一個(gè)就是最大的文件
var longestFile = fileInfos.First();
//倒序排列的第一個(gè)就是最小的文件
var smallestFile = fileInfos.Last();
Console.WriteLine("The largest file under {0} is {1} with a length of {2} bytes", path, longestFile.FullName, longestFile.Length);
Console.WriteLine("The smallest file under {0} is {1} with a length of {2} bytes", path, smallestFile.FullName, smallestFile.Length);
Console.WriteLine("===== The 10 largest files under {0} are: =====", path);
//返回前10個(gè)最大的文件
var queryTenLargest = fileInfos.Take(10);
foreach (var v in queryTenLargest)
{
Console.WriteLine("{0}: {1} bytes", v.FullName, v.Length);
}
若要返回一個(gè)或多個(gè)完整的 FileInfo 對(duì)象,查詢必須首先檢查數(shù)據(jù)源中的每個(gè)對(duì)象,然后按這些對(duì)象的 Length 屬性的值排序它們。然后查詢可以返回具有最大長度的單個(gè)對(duì)象或序列。使用 First 可返回列表中的第一個(gè)元素。使用 Take 可返回前 n 個(gè)元素。指定降序排序順序可將最小的元素放在列表的開頭。
實(shí)例6、如何在目錄樹中查詢重復(fù)的文件
有時(shí),多個(gè)文件夾中可能存在同名的文件。例如,在 Visual Studio 安裝文件夾中,有多個(gè)文件夾包含 readme.htm 文件。
此示例演示如何在指定的根文件夾中查詢這樣的重復(fù)文件名。
第二個(gè)示例演示如何查詢其大小和創(chuàng)建時(shí)間也匹配的文件。
static void Main(string[] args)
{
QueryDuplicates();
//QueryDuplicates2();
Console.ReadKey();
}
static void QueryDuplicates()
{
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
var charsToSkip = path.Length;
var queryDupNames = (from file in files
group file.FullName.Substring(charsToSkip) by file.Name into fileGroup
where fileGroup.Count() > 1
select fileGroup).Distinct();
PageOutput<string, string>(queryDupNames);
}
private static void QueryDuplicates2()
{
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
//路徑的長度
var charsToSkip = path.Length;
//注意一個(gè)復(fù)合鍵的使用。三個(gè)屬性都匹配的文件屬于同一組。
//匿名類型也可以用于復(fù)合鍵,但不能跨越方法邊界。
var queryDupFiles = from file in files
group file.FullName.Substring(charsToSkip) by
new PortableKey() { Name = file.Name, CreationTime = file.CreationTime, Length = file.Length }
into fileGroup
where fileGroup.Count() > 1
select fileGroup;
var queryDupNames = queryDupFiles as IGroupingstring>[] ?? queryDupFiles.ToArray();
var list = queryDupNames.ToList();
var i = queryDupNames.Count();
//分頁輸出
PageOutputstring>(queryDupNames);
}
private static void PageOutput(IEnumerable> queryDupNames)
{
//跳出分頁循環(huán)的標(biāo)志
var isAgain = true;
var numLines = Console.WindowHeight - 3;
var dupNames = queryDupNames as IGrouping[] ?? queryDupNames.ToArray();
foreach (var queryDupName in dupNames)
{
//分頁開始
var currentLine = 0;
do
{
Console.Clear();
Console.WriteLine("Filename = {0}", queryDupName.Key.ToString() == string.Empty ? "[none]" : queryDupName.Key.ToString());
//跳過 currentLine 行,取 numLines 行
var resultPage = queryDupName.Skip(currentLine).Take(numLines);
foreach (var fileName in resultPage)
{
Console.WriteLine("\t{0}", fileName);
}
//增量器記錄已顯示的行數(shù)
currentLine += numLines;
//讓用戶自動(dòng)選擇下一下
//Console.WriteLine("Press any key to continue or the 'End' key to break...");
//var key = Console.ReadKey().Key;
//if (key == ConsoleKey.End)
//{
// isAgain = false;
// break;
//}
//按得有點(diǎn)累,還是讓它自動(dòng)下一頁吧
Thread.Sleep(100);
} while (currentLine < queryDupName.Count());
//if (!isAgain)
// break;
}
}
第一個(gè)查詢使用一個(gè)簡單的鍵確定是否匹配;這會(huì)找到同名但內(nèi)容可能不同的文件。第二個(gè)查詢使用復(fù)合鍵并根據(jù) FileInfo 對(duì)象的三個(gè)屬性來確定是否匹配。此查詢非常類似于查找同名且內(nèi)容類似或相同的文件。
實(shí)例7、如何在文件夾中查詢文件的內(nèi)容
此示例演示如何查詢指定目錄樹中的所有文件、打開每個(gè)文件并檢查其內(nèi)容。 此類技術(shù)可用于對(duì)目錄樹的內(nèi)容創(chuàng)建索引或反向索引。 此示例中執(zhí)行的是簡單的字符串搜索。 但是,可使用正則表達(dá)式執(zhí)行更復(fù)雜類型的模式匹配。
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 12.0";
var dir = new DirectoryInfo(path);
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
//待匹配的字符串
const string searchTerm = @"Visual Studio";
//搜索每個(gè)文件的內(nèi)容。
//您也可以使用正則表達(dá)式替換 Contains 方法
var queryMatchingFiles = from file in files
where file.Extension == ".html"
let content = GetFileConetnt(file.FullName)
where content.Contains(searchTerm)
select file.FullName;
//執(zhí)行查詢
Console.WriteLine("The term \"{0}\" was found in:", searchTerm);
foreach (var filename in queryMatchingFiles)
{
Console.WriteLine(filename);
}
///
/// 讀取文件的所有內(nèi)容
///
///
///
static string GetFileConetnt(string fileName)
{
//如果我們?cè)诳煺蘸笠褎h除該文件,則忽略它,并返回空字符串。
return File.Exists(fileName) ? File.ReadAllText(fileName) : "";
}
到此這篇關(guān)于C#中LINQ to Objects的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#中C/S端實(shí)現(xiàn)WebService服務(wù)
本文主要介紹了C#中C/S端實(shí)現(xiàn)WebService服務(wù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
C# Form自定義光標(biāo)的簡單實(shí)現(xiàn)
這篇文章主要介紹了C# Form自定義光標(biāo)的簡單實(shí)現(xiàn),有需要的朋友可以參考一下2014-01-01
C#中構(gòu)造函數(shù)和析構(gòu)函數(shù)用法實(shí)例詳解
這篇文章主要介紹了C#中構(gòu)造函數(shù)和析構(gòu)函數(shù)用法,結(jié)合實(shí)例形式詳細(xì)分析了C#中構(gòu)造函數(shù)與析構(gòu)函數(shù)的原理、定義、使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-06-06
C#將DataGridView中的數(shù)據(jù)保存到CSV和Excel中
這篇文章介紹了C#將DataGridView中的數(shù)據(jù)保存到CSV和Excel中的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04
C#中TreeView實(shí)現(xiàn)適合兩級(jí)節(jié)點(diǎn)的選中節(jié)點(diǎn)方法
這篇文章主要介紹了C#中TreeView實(shí)現(xiàn)適合兩級(jí)節(jié)點(diǎn)的選中節(jié)點(diǎn)方法,實(shí)例分析了C#中TreeView節(jié)點(diǎn)操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09

