C#中的LINQ to Objects详解(2)

 更新时间:2022年6月6日 15:48  点击:301 作者:springsnow

相关文章:

C#中的LINQ to Objects详解(1)

C#中的LINQ to Objects详解(2)

四、Linq和反射

.NET Framework 类库反射 API 可用于检查 .NET 程序集中的元数据,以及创建位于该程序集中的类型、类型成员、参数等等的集合。 因为这些集合支持泛型 IEnumerable 接口,所以可以使用 LINQ 查询它们。

下面的示例演示了如何将 LINQ 与反射配合使用以检索有关与指定搜索条件匹配的方法的特定元数据。 在这种情况下,该查询将在返回数组等可枚举类型的程序集中查找所有方法的名称。

该示例使用 GetTypes 方法返回指定程序集中的类型的数组。 将应用 where 筛选器,以便仅返回公共类型。 对于每个公共类型,子查询使用从 GetMethods 调用返回的 MethodInfo 数组生成。 筛选这些结果,以仅返回其返回类型为数组或实现 IEnumerable 的其他类型的方法。 最后,通过使用类型名称作为键来对这些结果进行分组。

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 和文件目录

许多文件系统操作实质上是查询,因此非常适合使用 LINQ 方法。

本部分中的查询是非破坏性查询。 它们不用于更改原始文件或文件夹的内容。 这遵循了查询不应引起任何副作用这条规则。 通常,修改源数据的任何代码(包括执行创建/更新/删除运算符的查询)应与只查询数据的代码分开。

实例1、如何查询具有指定属性或名称的文件

此示例演示如何查找指定目录树中具有指定文件扩展名(例如“.txt”)的所有文件,还演示如何根据创建时间返回树中最新或最旧的文件。

//该查询将所有生产的完整路径。txt文件指定的文件夹包括子文件夹下。
const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\";
//取文件系统快照
var dir = new DirectoryInfo(path);
//该方法假定应用程序在指定路径下的所有文件夹都具有搜索权限。
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);

//创建查询
var fileQuery = from file in files
                where file.Extension == ".html"
                orderby file.Name
                select file;

//执行查询
foreach (var file in fileQuery)
{
    Console.WriteLine(file.FullName);
}

//创建和执行一个新的查询,通过查询旧文件的创建时间作为一个出发点
//Last:选最后一个,因为是按日期升序,所以最新的是指最后一个
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}");

实例2、如何按照扩展名对文件进行分组

此示例演示如何使用 LINQ 对文件或文件夹列表执行高级分组和排序操作。此外,它还演示如何使用 Skip 和 Take 方法对控制台窗口中的输出进行分页。

下面的查询演示如何按文件扩展名对指定目录树的内容进行分组。

const string path = @"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7";
//“path”的长度,后续用于在输出时去掉“path”这段前缀
var trimLength = path.Length;
//取文件系统快照
var dir = new DirectoryInfo(path);
//该方法假定应用程序在指定路径下的所有文件夹都具有搜索权限。
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);

//创建查询
var query = from file in files
            group file by file.Extension.ToLower() into fileGroup
            orderby fileGroup.Key
            select fileGroup;

//一次显示一组。如果列表实体的行数大于控制台窗口中的行数,则分页输出。 
PageOutput(trimLength, query);

private static void PageOutput(int rootLength, IOrderedEnumerable<string, FileInfo>> query)
{
    //跳出分页循环的标志
    var isAgain = true;
    //控制台输出的高度
    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”条数
            var resultPage = g.Skip(currentLine).Take(numLines);

            //执行查询
            foreach (var info in resultPage)
            {
                Console.WriteLine("\t{0}", info.FullName.Substring(rootLength));
            }

            //记录输出行数
            currentLine += numLines;
            Console.WriteLine("点击“任意键”继续,按“End”键退出");

            //给用户选择是否跳出
            var key = Console.ReadKey().Key;
            if (key != ConsoleKey.End) continue;

            isAgain = false;
            break;
        } while (currentLine < g.Count());

        if (!isAgain)
        {
            break;
        }
    }
}

为了使您可以查看所有结果,此示例还演示如何按页查看结果。这些方法可应用于 Windows 和 Web 应用程序。

请注意,由于代码将对组中的项进行分页,因此需要嵌套的 foreach 循环。此外,还会使用某他某个逻辑来计算列表中的当前位置,以及使用户可以停止分页并退出程序。在这种特定情况下,将针对原始查询的缓存结果运行分页查询。

实例3、如何查询一组文件夹中的总字节数

此示例演示如何检索指定文件夹及其所有子文件夹中的所有文件所使用的总字节数。

Sum 方法添加在 select 子句中选择的所有项的值。您可以轻松修改此查询以检索指定目录树中的最大或最小文件,方法是调用 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;

//缓存结果,以避免多次访问文件系统
var fileLengths = query as long[] ?? query.ToArray();
//返回最大文件的大小 
var largestLength = fileLengths.Max();
//返回指定文件夹下的所有文件中的总字节数
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);

如果您只需要统计特定目录树中的字节数,则可以更高效地实现此目的,而无需创建 LINQ 查询,因为该查询会引发创建列表集合作为数据源的系统开销。随着查询复杂度的增加,或者当您必须对同一数据源运行多个查询时,LINQ 方法的有用性也会随之增加。

实例4、如何比较两个文件夹中的内容

此示例演示比较两个文件列表的三种方法:

  • (1)查询一个指定两个文件列表是否相同的布尔值;
  • (2)查询用于检索同时位于两个文件夹中的文件的交集;
  • (3)查询用于检索位于一个文件夹中但不在另一个文件夹中的文件的差集;

//创建两个带比较的文件夹
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();

//该查询确定两个文件夹包含相同的文件列表,基于自定义文件比较器。查询立即执行,因为它返回一个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.");
}

//差集:对比两个文件夹的差异
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);
}

//该实现定义了一个非常简单的两个 FileInfo 对象之间的比较。它只比较文件的名称和它们字节数的长度
public class FileComparer : IEqualityComparer
{
    public bool Equals(FileInfo x, FileInfo y)
    {
        return string.Equals(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase) && x.Length == y.Length;
    }

    //返回一个比较标准的哈希值。根据 IEqualityComparer 规则,如果相等,那么哈希值也必须是相等的。
    //因为这里所定义的相等只是一个简单的值相等,而不是引用标识,所以两个或多个对象将产生相同的哈希值是可能的。 
    public int GetHashCode(FileInfo obj)
    {
        var s = string.Format("{0}{1}", obj.Name, obj.Length);

        return s.GetHashCode();
    }
}

【注意】 可以修改上述这些方法以便对任意类型的对象序列进行比较。

此处显示的 FileComparer 类演示如何将自定义比较器类与标准查询运算符一起使用。该类不是为在实际方案中使用而设计的。它只是使用每个文件的名称和长度(以字节为单位)来确定每个文件夹的内容是否相同。在实际方案中,应对此比较器进行修改以执行更严格的相等性检查。

实例5、如何在目录树中查询最大的文件

此示例演示与文件大小(以字节为单位)相关的五种查询:

  • 如何检索最大文件的大小(以字节为单位);
  • 如何检索最小文件的大小(以字节为单位);
  • 如何从指定的根文件夹下的一个或多个文件夹检索 FileInfo 对象最大或最小文件;
  • 如何检索一个序列,如 10 个最大文件。

下面的示例包含五种不同的查询,这些查询演示如何根据文件大小(以字节为单位)查询和分组文件。可以轻松地修改这些示例,以使查询基于 FileInfo对象的某个其他属性。

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();
//倒序排列的第一个就是最大的文件
var longestFile = fileInfos.First();
//倒序排列的第一个就是最小的文件
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个最大的文件
var queryTenLargest = fileInfos.Take(10);
foreach (var v in queryTenLargest)
{
    Console.WriteLine("{0}: {1} bytes", v.FullName, v.Length);
}

若要返回一个或多个完整的 FileInfo 对象,查询必须首先检查数据源中的每个对象,然后按这些对象的 Length 属性的值排序它们。然后查询可以返回具有最大长度的单个对象或序列。使用 First 可返回列表中的第一个元素。使用 Take 可返回前 n 个元素。指定降序排序顺序可将最小的元素放在列表的开头。

实例6、如何在目录树中查询重复的文件

有时,多个文件夹中可能存在同名的文件。例如,在 Visual Studio 安装文件夹中,有多个文件夹包含 readme.htm 文件。

此示例演示如何在指定的根文件夹中查询这样的重复文件名。

第二个示例演示如何查询其大小和创建时间也匹配的文件。

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;

    //注意一个复合键的使用。三个属性都匹配的文件属于同一组。
    //匿名类型也可以用于复合键,但不能跨越方法边界。 
    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)
{
    //跳出分页循环的标志 
    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);
            }

            //增量器记录已显示的行数
            currentLine += numLines;

            //让用户自动选择下一下
            //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;
            //}

            //按得有点累,还是让它自动下一页吧
            Thread.Sleep(100);

        } while (currentLine < queryDupName.Count());

        //if (!isAgain)
        //    break;
    }
}

第一个查询使用一个简单的键确定是否匹配;这会找到同名但内容可能不同的文件。第二个查询使用复合键并根据 FileInfo 对象的三个属性来确定是否匹配。此查询非常类似于查找同名且内容类似或相同的文件。

实例7、如何在文件夹中查询文件的内容

此示例演示如何查询指定目录树中的所有文件、打开每个文件并检查其内容。 此类技术可用于对目录树的内容创建索引或反向索引。 此示例中执行的是简单的字符串搜索。 但是,可使用正则表达式执行更复杂类型的模式匹配。

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";
//搜索每个文件的内容。
//您也可以使用正则表达式替换 Contains 方法
var queryMatchingFiles = from file in files

                         where file.Extension == ".html"
                         let content = GetFileConetnt(file.FullName)
                         where content.Contains(searchTerm)
                         select file.FullName;

//执行查询
Console.WriteLine("The term \"{0}\" was found in:", searchTerm);
foreach (var filename in queryMatchingFiles)
{
    Console.WriteLine(filename);
}
/// 

/// 读取文件的所有内容
/// 
/// 
/// 
static string GetFileConetnt(string fileName)
{
    //如果我们在快照后已删除该文件,则忽略它,并返回空字符串。 
    return File.Exists(fileName) ? File.ReadAllText(fileName) : "";
}

到此这篇关于C#中LINQ to Objects的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持猪先飞。

原文出处:https://www.cnblogs.com/springsnow/p/12894028.html

[!--infotagslink--]

相关文章

  • Illustrator鼠绘堆雪人的孩童矢量插画教程

    今天小编在这里就来给各位Illustrator的这一款软件的使用者们来说说鼠绘堆雪人的孩童矢量插画的教程,各位想知道具体绘制方法的使用者们,那么各位就快来跟着小编来看看...2016-09-14
  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • photoshop想象力无边界超现实合成图鉴赏

    今天小编在这里就为各位photoshop的这一款软件的使用者们带来了想象力无边界超现实合成图,各位想知道这些超现实的合成图都是些什么呢?那么下面各位就来跟着小编一起看...2016-09-14
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25